diff --git a/config/alfresco/public-rest-context.xml b/config/alfresco/public-rest-context.xml index 930153a320..1fc9454e3a 100644 --- a/config/alfresco/public-rest-context.xml +++ b/config/alfresco/public-rest-context.xml @@ -915,6 +915,22 @@ + + + + + + org.alfresco.rest.workflow.api.Activities + + + + + + + + + + @@ -925,18 +941,24 @@ - + + + + + + + - + - + @@ -956,6 +978,6 @@ - + diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/audit/control_nb_NO.properties b/config/alfresco/templates/webscripts/org/alfresco/repository/audit/control_nb_NO.properties index d3d8761efc..d20f87a372 100755 --- a/config/alfresco/templates/webscripts/org/alfresco/repository/audit/control_nb_NO.properties +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/audit/control_nb_NO.properties @@ -2,6 +2,6 @@ audit.err.app.notProvided=Programnavn ikke gitt. audit.err.app.notFound=Program ikke funnet: {0} audit.err.path.notProvided=Ingen sti har blitt gitt etter programnavnet. -audit.err.action.invalid=Parameteren 'handling' m\u00e5 enten v\u00e6re 'aktiver' eller 'deaktiver' -audit.err.value.classNotFound='valueType' ikke gjenkjent: {0} +audit.err.action.invalid=Parameteren ''handling'' m\u00e5 enten v\u00e6re ''aktiver'' eller ''deaktiver'' +audit.err.value.classNotFound=''valueType'' ikke gjenkjent: {0} audit.err.value.convertFailed=Kan ikke konvertere ''{0}'' til type ''{1}'' diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/links/links-delete.post_nb_NO.properties b/config/alfresco/templates/webscripts/org/alfresco/repository/links/links-delete.post_nb_NO.properties index c7049dc2d2..8b15ed9623 100755 --- a/config/alfresco/templates/webscripts/org/alfresco/repository/links/links-delete.post_nb_NO.properties +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/links/links-delete.post_nb_NO.properties @@ -1,3 +1,3 @@ links-delete.err.not.found=Ingen gyldige koblingsnavn ble angitt -links-delete.access.denied=Du har ikke tillatelse til \u00e5 slette koblingen med navnet '{0}' +links-delete.access.denied=Du har ikke tillatelse til \u00e5 slette koblingen med navnet ''{0}'' links-delete.msg.deleted=Slettet node {0} diff --git a/config/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.post_it.properties b/config/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.post_it.properties new file mode 100644 index 0000000000..6a6deed140 --- /dev/null +++ b/config/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.post_it.properties @@ -0,0 +1,7 @@ +page.create.error.nameAlreadyUsed=Il nome ''{0}'' \u00e8 stato gi\u00e0 utilizzato +page.create.error.noNameProvided=Non \u00e8 stato fornito alcun nome per la pagina +page.create.error.noDefProvided=Non \u00e8 stata fornita alcuna definizione di pagina per la pagina +page.create.error.invalidJson=La definizione di pagina non era valida JSON: {0} +page.create.error.noTargetLocation=La posizione di destinazione per le pagine "Condividi risorse/pagine" non \u00e8 stata creata nel dizionario dei dati +page.create.error.couldNotCreate=Non \u00e8 stato possibile creare la pagina. +page.create.error.unexpected=Si \u00e8 verificato un errore imprevisto. diff --git a/config/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.post_ja.properties b/config/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.post_ja.properties new file mode 100644 index 0000000000..6204ba537a --- /dev/null +++ b/config/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.post_ja.properties @@ -0,0 +1,7 @@ +page.create.error.nameAlreadyUsed=\u540d\u524d''{0}''\u306f\u65e2\u306b\u4f7f\u308f\u308c\u3066\u3044\u307e\u3059\u3002 +page.create.error.noNameProvided=\u30da\u30fc\u30b8\u306e\u540d\u524d\u304c\u6307\u5b9a\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002 +page.create.error.noDefProvided=\u30da\u30fc\u30b8\u306e\u30da\u30fc\u30b8\u5b9a\u7fa9\u304c\u6307\u5b9a\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002 +page.create.error.invalidJson=\u30da\u30fc\u30b8\u5b9a\u7fa9\u306eJSON\u304c\u7121\u52b9\u3067\u3059: {0} +page.create.error.noTargetLocation=\u30da\u30fc\u30b8"ShareResources/pages"\u306e\u30bf\u30fc\u30b2\u30c3\u30c8\u306e\u5834\u6240\u304c\u30c7\u30fc\u30bf\u30c7\u30a3\u30af\u30b7\u30e7\u30ca\u30ea\u5185\u306b\u4f5c\u6210\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002 +page.create.error.couldNotCreate=\u30da\u30fc\u30b8\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +page.create.error.unexpected=\u4e0d\u660e\u306a\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 diff --git a/config/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.post_nb_NO.properties b/config/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.post_nb_NO.properties new file mode 100644 index 0000000000..aa047e2f4d --- /dev/null +++ b/config/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.post_nb_NO.properties @@ -0,0 +1,7 @@ +page.create.error.nameAlreadyUsed=Navnet ''{0}'' er allerede brukt +page.create.error.noNameProvided=Intet navn oppgitt for siden +page.create.error.noDefProvided=Ingen sidedefinisjon oppgitt for siden +page.create.error.invalidJson=Sidedefinisjonen ikke gyldig JSON: {0} +page.create.error.noTargetLocation=M\u00e5lstedet for sidene "ShareResources/sider" ikke opprettet i datamappen +page.create.error.couldNotCreate=Det var ikke mulig \u00e5 opprette siden. +page.create.error.unexpected=Det oppstod en uventet feil. diff --git a/config/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.post_nl.properties b/config/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.post_nl.properties new file mode 100644 index 0000000000..74a8b02383 --- /dev/null +++ b/config/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.post_nl.properties @@ -0,0 +1,7 @@ +page.create.error.nameAlreadyUsed=De naam ''{0}'' is al gebruikt +page.create.error.noNameProvided=Er is geen naam opgegeven voor de pagina +page.create.error.noDefProvided=Er is geen paginadefinitie opgegeven voor de pagina +page.create.error.invalidJson=De paginadefinitie is niet geldig (JSON): {0} +page.create.error.noTargetLocation=De doellocatie voor de pagina's "Resources/pagina's delen" is niet gemaakt in de data dictionary +page.create.error.couldNotCreate=De pagina kon niet worden gemaakt. +page.create.error.unexpected=Er is een onverwachte fout opgetreden. diff --git a/config/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.post_ru.properties b/config/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.post_ru.properties new file mode 100644 index 0000000000..f38aea007c --- /dev/null +++ b/config/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.post_ru.properties @@ -0,0 +1,7 @@ +page.create.error.nameAlreadyUsed=\u0418\u043c\u044f ''{0}'' \u0443\u0436\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u043e +page.create.error.noNameProvided=\u0414\u043b\u044f \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b \u043d\u0435 \u0443\u043a\u0430\u0437\u0430\u043d\u043e \u0438\u043c\u044f +page.create.error.noDefProvided=\u0414\u043b\u044f \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b \u043d\u0435 \u0443\u043a\u0430\u0437\u0430\u043d\u043e \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0435 +page.create.error.invalidJson=\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b \u043d\u0435 \u044f\u0432\u043b\u044f\u043b\u043e\u0441\u044c \u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u043c JSON: {0} +page.create.error.noTargetLocation=\u0412 \u0441\u043b\u043e\u0432\u0430\u0440\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u043d\u0435 \u0441\u043e\u0437\u0434\u0430\u043d\u043e \u0446\u0435\u043b\u0435\u0432\u043e\u0435 \u043c\u0435\u0441\u0442\u043e \u0434\u043b\u044f \u0441\u0442\u0440\u0430\u043d\u0438\u0446 "ShareResources/pages" +page.create.error.couldNotCreate=\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443. +page.create.error.unexpected=\u041f\u0440\u043e\u0438\u0437\u043e\u0448\u043b\u0430 \u043d\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430. diff --git a/config/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.post_zh_CN.properties b/config/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.post_zh_CN.properties new file mode 100644 index 0000000000..3b20136a9c --- /dev/null +++ b/config/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.post_zh_CN.properties @@ -0,0 +1,7 @@ +page.create.error.nameAlreadyUsed=\u540d\u79f0''{0}''\u5df2\u4f7f\u7528 +page.create.error.noNameProvided=\u6ca1\u6709\u4e3a\u9875\u9762\u6307\u5b9a\u540d\u79f0 +page.create.error.noDefProvided=\u6ca1\u6709\u63d0\u4f9b\u9875\u9762\u5b9a\u4e49 +page.create.error.invalidJson=\u9875\u9762\u5b9a\u4e49\u65e0\u6548 JSON\uff1a{0} +page.create.error.noTargetLocation=\u672a\u5728\u6570\u636e\u5b57\u5178\u4e2d\u521b\u5efa\u9875\u9762"ShareResources/pages"\u7684\u76ee\u6807\u4f4d\u7f6e +page.create.error.couldNotCreate=\u65e0\u6cd5\u521b\u5efa\u9875\u9762\u3002 +page.create.error.unexpected=\u51fa\u73b0\u610f\u5916\u9519\u8bef\u3002 diff --git a/config/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.put_it.properties b/config/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.put_it.properties new file mode 100644 index 0000000000..4b12ddb749 --- /dev/null +++ b/config/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.put_it.properties @@ -0,0 +1,8 @@ +page.update.error.nameAlreadyUsed=Il nome ''{0}'' \u00e8 stato gi\u00e0 utilizzato +page.update.error.noNameProvided=Non \u00e8 stato fornito alcun nome per la pagina +page.update.error.noDefProvided=Non \u00e8 stata fornita alcuna definizione di pagina per la pagina +page.update.error.invalidJson=La definizione di pagina non era valida JSON: {0} +page.update.error.noTargetLocation=La posizione di destinazione per le pagine "Condividi risorse/pagine" non \u00e8 stata creata nel dizionario dei dati +page.update.error.couldNotCreate=Non \u00e8 stato possibile creare la pagina. +page.update.error.doesNotExist=La pagina richiesta da aggiornare non esiste +page.update.error.unexpected=Si \u00e8 verificato un errore imprevisto. diff --git a/config/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.put_ja.properties b/config/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.put_ja.properties new file mode 100644 index 0000000000..80e23ced96 --- /dev/null +++ b/config/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.put_ja.properties @@ -0,0 +1,8 @@ +page.update.error.nameAlreadyUsed=\u540d\u524d''{0}''\u306f\u65e2\u306b\u4f7f\u308f\u308c\u3066\u3044\u307e\u3059\u3002 +page.update.error.noNameProvided=\u30da\u30fc\u30b8\u306e\u540d\u524d\u304c\u6307\u5b9a\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002 +page.update.error.noDefProvided=\u30da\u30fc\u30b8\u306e\u30da\u30fc\u30b8\u5b9a\u7fa9\u304c\u6307\u5b9a\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002 +page.update.error.invalidJson=\u30da\u30fc\u30b8\u5b9a\u7fa9\u306eJSON\u304c\u7121\u52b9\u3067\u3059: {0} +page.update.error.noTargetLocation=\u30da\u30fc\u30b8"ShareResources/pages"\u306e\u30bf\u30fc\u30b2\u30c3\u30c8\u306e\u5834\u6240\u304c\u30c7\u30fc\u30bf\u30c7\u30a3\u30af\u30b7\u30e7\u30ca\u30ea\u5185\u306b\u4f5c\u6210\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002 +page.update.error.couldNotCreate=\u30da\u30fc\u30b8\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +page.update.error.doesNotExist=\u66f4\u65b0\u3059\u308b\u3053\u3068\u3092\u8981\u6c42\u3055\u308c\u305f\u30da\u30fc\u30b8\u306f\u5b58\u5728\u3057\u307e\u305b\u3093\u3002 +page.update.error.unexpected=\u4e0d\u660e\u306a\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002 diff --git a/config/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.put_nb_NO.properties b/config/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.put_nb_NO.properties new file mode 100644 index 0000000000..adb2dd1277 --- /dev/null +++ b/config/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.put_nb_NO.properties @@ -0,0 +1,8 @@ +page.update.error.nameAlreadyUsed=Navnet ''{0}'' er allerede brukt +page.update.error.noNameProvided=Intet navn oppgitt for siden +page.update.error.noDefProvided=Ingen sidedefinisjon oppgitt for siden +page.update.error.invalidJson=Sidedefinisjonen ikke gyldig JSON: {0} +page.update.error.noTargetLocation=M\u00e5lstedet for sidene "ShareResources/sider" ikke opprettet i datamappen +page.update.error.couldNotCreate=Det var ikke mulig \u00e5 opprette siden. +page.update.error.doesNotExist=Forespurt side som skal oppdateres eksisterer ikke +page.update.error.unexpected=Det oppstod en uventet feil. diff --git a/config/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.put_nl.properties b/config/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.put_nl.properties new file mode 100644 index 0000000000..8aafc38bd4 --- /dev/null +++ b/config/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.put_nl.properties @@ -0,0 +1,8 @@ +page.update.error.nameAlreadyUsed=De naam ''{0}'' is al gebruikt +page.update.error.noNameProvided=Er is geen naam opgegeven voor de pagina +page.update.error.noDefProvided=Er is geen paginadefinitie opgegeven voor de pagina +page.update.error.invalidJson=De paginadefinitie is niet geldig (JSON): {0} +page.update.error.noTargetLocation=De doellocatie voor de pagina's "Resources/pagina's delen" is niet gemaakt in de data dictionary +page.update.error.couldNotCreate=De pagina kon niet worden gemaakt. +page.update.error.doesNotExist=De opgevraagde pagina die moet worden bijgewerkt bestaat niet +page.update.error.unexpected=Er is een onverwachte fout opgetreden. diff --git a/config/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.put_ru.properties b/config/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.put_ru.properties new file mode 100644 index 0000000000..37c7f97cb3 --- /dev/null +++ b/config/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.put_ru.properties @@ -0,0 +1,8 @@ +page.update.error.nameAlreadyUsed=\u0418\u043c\u044f ''{0}'' \u0443\u0436\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u043e +page.update.error.noNameProvided=\u0414\u043b\u044f \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b \u043d\u0435 \u0443\u043a\u0430\u0437\u0430\u043d\u043e \u0438\u043c\u044f +page.update.error.noDefProvided=\u0414\u043b\u044f \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b \u043d\u0435 \u0443\u043a\u0430\u0437\u0430\u043d\u043e \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0435 +page.update.error.invalidJson=\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b \u043d\u0435 \u044f\u0432\u043b\u044f\u043b\u043e\u0441\u044c \u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b\u043c JSON: {0} +page.update.error.noTargetLocation=\u0412 \u0441\u043b\u043e\u0432\u0430\u0440\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u043d\u0435 \u0441\u043e\u0437\u0434\u0430\u043d\u043e \u0446\u0435\u043b\u0435\u0432\u043e\u0435 \u043c\u0435\u0441\u0442\u043e \u0434\u043b\u044f \u0441\u0442\u0440\u0430\u043d\u0438\u0446 "ShareResources/pages" +page.update.error.couldNotCreate=\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443. +page.update.error.doesNotExist=\u0421\u0442\u0440\u0430\u043d\u0438\u0446\u0430, \u0434\u043b\u044f \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0437\u0430\u043f\u0440\u043e\u0448\u0435\u043d\u043e \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435, \u043d\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442 +page.update.error.unexpected=\u041f\u0440\u043e\u0438\u0437\u043e\u0448\u043b\u0430 \u043d\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430. diff --git a/config/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.put_zh_CN.properties b/config/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.put_zh_CN.properties new file mode 100644 index 0000000000..617d728e18 --- /dev/null +++ b/config/alfresco/templates/webscripts/org/alfresco/slingshot/remote-content/page-definition.put_zh_CN.properties @@ -0,0 +1,8 @@ +page.update.error.nameAlreadyUsed=\u540d\u79f0''{0}''\u5df2\u4f7f\u7528 +page.update.error.noNameProvided=\u6ca1\u6709\u4e3a\u9875\u9762\u6307\u5b9a\u540d\u79f0 +page.update.error.noDefProvided=\u6ca1\u6709\u63d0\u4f9b\u9875\u9762\u5b9a\u4e49 +page.update.error.invalidJson=\u9875\u9762\u5b9a\u4e49\u65e0\u6548 JSON\uff1a{0} +page.update.error.noTargetLocation=\u672a\u5728\u6570\u636e\u5b57\u5178\u4e2d\u521b\u5efa\u9875\u9762"ShareResources/pages"\u7684\u76ee\u6807\u4f4d\u7f6e +page.update.error.couldNotCreate=\u65e0\u6cd5\u521b\u5efa\u9875\u9762\u3002 +page.update.error.doesNotExist=\u8bf7\u6c42\u66f4\u65b0\u7684\u9875\u9762\u4e0d\u5b58\u5728 +page.update.error.unexpected=\u51fa\u73b0\u610f\u5916\u9519\u8bef\u3002 diff --git a/source/java/org/alfresco/rest/workflow/api/Activities.java b/source/java/org/alfresco/rest/workflow/api/Activities.java new file mode 100644 index 0000000000..403381190a --- /dev/null +++ b/source/java/org/alfresco/rest/workflow/api/Activities.java @@ -0,0 +1,10 @@ +package org.alfresco.rest.workflow.api; + +import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo; +import org.alfresco.rest.framework.resource.parameters.Parameters; +import org.alfresco.rest.workflow.api.model.Activity; + +public interface Activities +{ + CollectionWithPagingInfo getActivities(String processId, Parameters parameters); +} diff --git a/source/java/org/alfresco/rest/workflow/api/ProcessDefinitions.java b/source/java/org/alfresco/rest/workflow/api/ProcessDefinitions.java index dfc93a7563..c8e2b38eae 100644 --- a/source/java/org/alfresco/rest/workflow/api/ProcessDefinitions.java +++ b/source/java/org/alfresco/rest/workflow/api/ProcessDefinitions.java @@ -18,11 +18,12 @@ */ package org.alfresco.rest.workflow.api; +import org.alfresco.rest.framework.resource.content.BinaryResource; 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.workflow.api.model.ProcessDefinition; import org.alfresco.rest.workflow.api.model.FormModelElement; +import org.alfresco.rest.workflow.api.model.ProcessDefinition; public interface ProcessDefinitions { @@ -30,5 +31,7 @@ public interface ProcessDefinitions public ProcessDefinition getProcessDefinition(String definitionId); + public BinaryResource getProcessDefinitionImage(String definitionId); + public CollectionWithPagingInfo getStartFormModel(String definitionId, Paging paging); } diff --git a/source/java/org/alfresco/rest/workflow/api/Processes.java b/source/java/org/alfresco/rest/workflow/api/Processes.java index 3afaf03ceb..d28f93623c 100644 --- a/source/java/org/alfresco/rest/workflow/api/Processes.java +++ b/source/java/org/alfresco/rest/workflow/api/Processes.java @@ -18,6 +18,8 @@ */ package org.alfresco.rest.workflow.api; +import java.util.List; + import org.alfresco.rest.framework.resource.content.BinaryResource; import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo; import org.alfresco.rest.framework.resource.parameters.Paging; @@ -47,6 +49,8 @@ public interface Processes CollectionWithPagingInfo getVariables(String processId, Paging paging); Variable updateVariable(String processId, Variable entity); + + List updateVariables(String processId, List variables); void deleteVariable(String processId, String id); diff --git a/source/java/org/alfresco/rest/workflow/api/Tasks.java b/source/java/org/alfresco/rest/workflow/api/Tasks.java index 631302b77b..d3d5db51ca 100644 --- a/source/java/org/alfresco/rest/workflow/api/Tasks.java +++ b/source/java/org/alfresco/rest/workflow/api/Tasks.java @@ -18,6 +18,8 @@ */ package org.alfresco.rest.workflow.api; +import java.util.List; + import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo; import org.alfresco.rest.framework.resource.parameters.Paging; import org.alfresco.rest.framework.resource.parameters.Parameters; @@ -26,11 +28,14 @@ import org.alfresco.rest.workflow.api.model.Item; import org.alfresco.rest.workflow.api.model.Task; import org.alfresco.rest.workflow.api.model.TaskCandidate; import org.alfresco.rest.workflow.api.model.TaskVariable; +import org.alfresco.rest.workflow.api.model.Variable; import org.alfresco.rest.workflow.api.model.VariableScope; public interface Tasks { CollectionWithPagingInfo getTasks(Parameters parameters); + + CollectionWithPagingInfo getTasks(String processId, Parameters parameters); Task getTask(String taskId); @@ -44,6 +49,8 @@ public interface Tasks CollectionWithPagingInfo getTaskVariables(String taskId, Paging paging, VariableScope scope); TaskVariable updateTaskVariable(String taskId, TaskVariable taskVariable); + + List updateTaskVariables(String taskId, List variables); void deleteTaskVariable(String taskId, String variableName); diff --git a/source/java/org/alfresco/rest/workflow/api/deployments/package-info.java b/source/java/org/alfresco/rest/workflow/api/deployments/package-info.java index fde009568e..1fc1f53e71 100644 --- a/source/java/org/alfresco/rest/workflow/api/deployments/package-info.java +++ b/source/java/org/alfresco/rest/workflow/api/deployments/package-info.java @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Alfresco. If not, see . */ -@WebApi(name="alfresco", scope=Api.SCOPE.PUBLIC, version=1) +@WebApi(name="workflow", scope=Api.SCOPE.PUBLIC, version=1) package org.alfresco.rest.workflow.api.deployments; import org.alfresco.rest.framework.Api; import org.alfresco.rest.framework.WebApi; \ No newline at end of file diff --git a/source/java/org/alfresco/rest/workflow/api/impl/ActivitiesImpl.java b/source/java/org/alfresco/rest/workflow/api/impl/ActivitiesImpl.java new file mode 100644 index 0000000000..a1f52ededc --- /dev/null +++ b/source/java/org/alfresco/rest/workflow/api/impl/ActivitiesImpl.java @@ -0,0 +1,50 @@ +package org.alfresco.rest.workflow.api.impl; + +import java.util.ArrayList; +import java.util.List; + +import org.activiti.engine.history.HistoricActivityInstance; +import org.activiti.engine.history.HistoricActivityInstanceQuery; +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.workflow.api.Activities; +import org.alfresco.rest.workflow.api.model.Activity; + +public class ActivitiesImpl extends WorkflowRestImpl implements Activities +{ + private static final String STATUS_ACTIVE = "active"; + private static final String STATUS_COMPLETED = "completed"; + + @Override + public CollectionWithPagingInfo getActivities(String processId, Parameters parameters) + { + Paging paging = parameters.getPaging(); + String status = parameters.getParameter("status"); + + validateIfUserAllowedToWorkWithProcess(processId); + + HistoricActivityInstanceQuery query = activitiProcessEngine + .getHistoryService() + .createHistoricActivityInstanceQuery(); + + if (STATUS_ACTIVE.equals(status)) query.unfinished(); + else if (STATUS_COMPLETED.equals(status)) query.finished(); + + query.processInstanceId(processId); + + query.orderByExecutionId().asc(); + + List activities = query.listPage(paging.getSkipCount(), paging.getMaxItems()); + + List page = new ArrayList(activities.size()); + for (HistoricActivityInstance activityInstance: activities) + { + Activity activity = new Activity(activityInstance); + page.add(activity); + } + + return CollectionWithPagingInfo.asPaged(paging, page, false, page.size()); + } + +} diff --git a/source/java/org/alfresco/rest/workflow/api/impl/MapBasedQueryWalker.java b/source/java/org/alfresco/rest/workflow/api/impl/MapBasedQueryWalker.java index 75c27fbcc3..8112409a1c 100644 --- a/source/java/org/alfresco/rest/workflow/api/impl/MapBasedQueryWalker.java +++ b/source/java/org/alfresco/rest/workflow/api/impl/MapBasedQueryWalker.java @@ -170,7 +170,7 @@ public class MapBasedQueryWalker extends WalkerCallbackAdapter { String localPropertyName = propertyName.replaceFirst("variables/", ""); Object actualValue = null; - if (propertyValue.contains("_") && propertyValue.contains(" ")) + if ((propertyValue.contains("_") || propertyValue.contains(":")) && propertyValue.contains(" ")) { String typeDef = propertyValue.substring(0, propertyValue.indexOf(' ')); try diff --git a/source/java/org/alfresco/rest/workflow/api/impl/ProcessDefinitionsImpl.java b/source/java/org/alfresco/rest/workflow/api/impl/ProcessDefinitionsImpl.java index c3ba7a5242..35a05f3ea3 100644 --- a/source/java/org/alfresco/rest/workflow/api/impl/ProcessDefinitionsImpl.java +++ b/source/java/org/alfresco/rest/workflow/api/impl/ProcessDefinitionsImpl.java @@ -18,13 +18,19 @@ */ package org.alfresco.rest.workflow.api.impl; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.UUID; import org.activiti.engine.form.StartFormData; +import org.activiti.engine.impl.form.StartFormHandler; import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity; import org.activiti.engine.repository.ProcessDefinitionQuery; import org.alfresco.repo.i18n.MessageService; @@ -34,7 +40,11 @@ import org.alfresco.repo.workflow.WorkflowModel; import org.alfresco.repo.workflow.WorkflowObjectFactory; import org.alfresco.repo.workflow.WorkflowQNameConverter; import org.alfresco.rest.antlr.WhereClauseParser; +import org.alfresco.rest.framework.core.exceptions.ApiException; import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException; +import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException; +import org.alfresco.rest.framework.resource.content.BinaryResource; +import org.alfresco.rest.framework.resource.content.FileBinaryResource; import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo; import org.alfresco.rest.framework.resource.parameters.Paging; import org.alfresco.rest.framework.resource.parameters.Parameters; @@ -44,6 +54,8 @@ import org.alfresco.rest.workflow.api.model.FormModelElement; import org.alfresco.rest.workflow.api.model.ProcessDefinition; import org.alfresco.service.cmr.dictionary.TypeDefinition; import org.alfresco.service.namespace.QName; +import org.alfresco.util.TempFileProvider; +import org.apache.commons.io.IOUtils; public class ProcessDefinitionsImpl extends WorkflowRestImpl implements ProcessDefinitions { @@ -55,6 +67,9 @@ public class ProcessDefinitionsImpl extends WorkflowRestImpl implements ProcessD "category", "key", "name" })); + private static final Set PROCESS_DEFINITION_COLLECTION_SORT_PROPERTIES = new HashSet(Arrays.asList( + "deploymentId", "key", "category", "id", "version", "name" + )); MessageService messageService; String engineId; @@ -79,52 +94,135 @@ public class ProcessDefinitionsImpl extends WorkflowRestImpl implements ProcessD ProcessDefinitionQuery query = activitiProcessEngine .getRepositoryService() .createProcessDefinitionQuery() - .processDefinitionCategoryNotEquals(WorkflowDeployer.CATEGORY_ALFRESCO_INTERNAL) - .orderByProcessDefinitionName().asc() - .orderByProcessDefinitionVersion().asc(); - - // Filter based on tenant, if required - if (tenantService.isEnabled() && deployWorkflowsInTenant) - { - query.processDefinitionKeyLike("@" + TenantUtil.getCurrentDomain() + "@%"); - } + .processDefinitionCategoryNotEquals(WorkflowDeployer.CATEGORY_ALFRESCO_INTERNAL); MapBasedQueryWalker propertyWalker = new MapBasedQueryWalker(PROCESS_DEFINITION_COLLECTION_EQUALS_QUERY_PROPERTIES, PROCESS_DEFINITION_COLLECTION_MATCHES_QUERY_PROPERTIES); + boolean keyQueryIncluded = false; + if(parameters.getQuery() != null) { QueryHelper.walk(parameters.getQuery(), propertyWalker); // Property equals - if(propertyWalker.getProperty("category", WhereClauseParser.EQUALS) != null) { - query.processDefinitionCategory(propertyWalker.getProperty("category", WhereClauseParser.EQUALS)); + String categoryProperty = propertyWalker.getProperty("category", WhereClauseParser.EQUALS); + if (categoryProperty != null) + { + query.processDefinitionCategory(categoryProperty); } - if(propertyWalker.getProperty("key", WhereClauseParser.EQUALS) != null) { - query.processDefinitionKey(propertyWalker.getProperty("key", WhereClauseParser.EQUALS)); + + String keyProperty = propertyWalker.getProperty("key", WhereClauseParser.EQUALS); + if (keyProperty != null) + { + query.processDefinitionKey(getProcessDefinitionKey(keyProperty)); + keyQueryIncluded = true; } - if(propertyWalker.getProperty("name", WhereClauseParser.EQUALS) != null) { - query.processDefinitionName(propertyWalker.getProperty("name", WhereClauseParser.EQUALS)); + + String nameProperty = propertyWalker.getProperty("name", WhereClauseParser.EQUALS); + if (nameProperty != null) + { + query.processDefinitionName(nameProperty); } - if(propertyWalker.getProperty("version", WhereClauseParser.EQUALS) != null) { - query.processDefinitionVersion(propertyWalker.getProperty("version", WhereClauseParser.EQUALS, Integer.class)); + + Integer versionProperty = propertyWalker.getProperty("version", WhereClauseParser.EQUALS, Integer.class); + if (versionProperty != null) + { + query.processDefinitionVersion(versionProperty); } - if(propertyWalker.getProperty("deploymentId", WhereClauseParser.EQUALS) != null) { - query.deploymentId(propertyWalker.getProperty("deploymentId", WhereClauseParser.EQUALS)); + + String deploymentProperty = propertyWalker.getProperty("deploymentId", WhereClauseParser.EQUALS); + if (deploymentProperty != null) + { + query.deploymentId(deploymentProperty); } // Property matches - if(propertyWalker.getProperty("category", WhereClauseParser.MATCHES) != null) { - query.processDefinitionCategoryLike(propertyWalker.getProperty("category", WhereClauseParser.MATCHES)); + String categoryMatchesProperty = propertyWalker.getProperty("category", WhereClauseParser.MATCHES); + if (categoryMatchesProperty != null) + { + query.processDefinitionCategoryLike(categoryMatchesProperty); } - if(propertyWalker.getProperty("key", WhereClauseParser.MATCHES) != null) { - query.processDefinitionKeyLike(propertyWalker.getProperty("key", WhereClauseParser.MATCHES)); + + String keyMatchesProperty = propertyWalker.getProperty("key", WhereClauseParser.MATCHES); + if (keyMatchesProperty != null) + { + query.processDefinitionKeyLike(getProcessDefinitionKey(keyMatchesProperty)); + keyQueryIncluded = true; } - if(propertyWalker.getProperty("name", WhereClauseParser.MATCHES) != null) { - query.processDefinitionNameLike(propertyWalker.getProperty("name", WhereClauseParser.MATCHES)); + + String nameLikeProperty = propertyWalker.getProperty("name", WhereClauseParser.MATCHES); + if (nameLikeProperty != null) + { + query.processDefinitionNameLike(nameLikeProperty); } } + // Filter based on tenant, if required + if (keyQueryIncluded == false && tenantService.isEnabled() && deployWorkflowsInTenant) + { + query.processDefinitionKeyLike("@" + TenantUtil.getCurrentDomain() + "@%"); + } + + String sortParam = parameters.getParameter("sort"); + if (sortParam != null) + { + if (PROCESS_DEFINITION_COLLECTION_SORT_PROPERTIES.contains(sortParam)) + { + if ("id".equalsIgnoreCase(sortParam)) + { + query.orderByProcessDefinitionId(); + } + else if ("deploymentId".equalsIgnoreCase(sortParam)) + { + query.orderByDeploymentId(); + } + else if ("key".equalsIgnoreCase(sortParam)) + { + query.orderByProcessDefinitionKey(); + } + else if ("category".equalsIgnoreCase(sortParam)) + { + query.orderByProcessDefinitionCategory(); + } + else if ("version".equalsIgnoreCase(sortParam)) + { + query.orderByProcessDefinitionVersion(); + } + else if ("name".equalsIgnoreCase(sortParam)) + { + query.orderByProcessDefinitionName(); + } + } + else + { + throw new InvalidArgumentException("sort " + sortParam + + " is not supported, supported items are " + PROCESS_DEFINITION_COLLECTION_SORT_PROPERTIES.toArray()); + } + + String sortOrderParam = parameters.getParameter("sortOrder"); + if (sortOrderParam != null) + { + if ("asc".equalsIgnoreCase(sortOrderParam)) + { + query.asc(); + } + else if ("desc".equalsIgnoreCase(sortOrderParam)) + { + query.desc(); + } + else + { + throw new InvalidArgumentException("sort order " + sortOrderParam + + " is not supported, supported items are asc and desc"); + } + } + } + else + { + query.orderByProcessDefinitionId().asc(); + } + List processDefinitions = query.listPage(parameters.getPaging().getSkipCount(), parameters.getPaging().getMaxItems()); @@ -160,10 +258,70 @@ public class ProcessDefinitionsImpl extends WorkflowRestImpl implements ProcessD ProcessDefinition deploymentRest = createProcessDefinitionRest((ProcessDefinitionEntity) processDefinition); return deploymentRest; } + + @Override + public BinaryResource getProcessDefinitionImage(String definitionId) + { + ProcessDefinitionQuery query = activitiProcessEngine + .getRepositoryService() + .createProcessDefinitionQuery() + .processDefinitionId(definitionId); + + if (tenantService.isEnabled() && deployWorkflowsInTenant) + { + query.processDefinitionKeyLike("@" + TenantUtil.getCurrentDomain() + "@%"); + } + + org.activiti.engine.repository.ProcessDefinition processDefinition = query.singleResult(); + + if (processDefinition == null) + { + throw new EntityNotFoundException(definitionId); + } + + try + { + InputStream processDiagram = activitiProcessEngine.getRepositoryService().getProcessDiagram(definitionId); + if (processDiagram != null) + { + File file = TempFileProvider.createTempFile(definitionId + UUID.randomUUID(), ".png"); + FileOutputStream fos = new FileOutputStream(file); + IOUtils.copy(processDiagram, fos); + fos.close(); + + return new FileBinaryResource(file); + } + else + { + throw new ApiException("No image available for definitionId " + definitionId); + } + } + catch (IOException error) + { + throw new ApiException("Error while getting process definition image."); + } + } @Override public CollectionWithPagingInfo getStartFormModel(String definitionId, Paging paging) { + // first validate if user is allowed to access the process definition if workflows are deployed per tenant + if (tenantService.isEnabled() && deployWorkflowsInTenant) + { + ProcessDefinitionQuery query = activitiProcessEngine + .getRepositoryService() + .createProcessDefinitionQuery() + .processDefinitionId(definitionId); + + query.processDefinitionKeyLike("@" + TenantUtil.getCurrentDomain() + "@%"); + org.activiti.engine.repository.ProcessDefinition processDefinition = query.singleResult(); + + if (processDefinition == null) + { + throw new EntityNotFoundException(definitionId); + } + } + StartFormData startFormData = activitiProcessEngine.getFormService().getStartFormData(definitionId); if (startFormData == null) { @@ -183,23 +341,88 @@ public class ProcessDefinitionsImpl extends WorkflowRestImpl implements ProcessD TypeDefinition startTaskType = workflowFactory.getTaskFullTypeDefinition(startFormData.getFormKey(), true); return getFormModelElements(startTaskType, paging); } + + protected String getProcessDefinitionKey(String key) + { + String processDefKey = null; + if (tenantService.isEnabled() && deployWorkflowsInTenant) + { + processDefKey = "@" + TenantUtil.getCurrentDomain() + "@" + key; + } + else + { + processDefKey = key; + } + return processDefKey; + } + + protected String getLocalProcessDefinitionKey(String key) + { + String processDefKey = null; + if (tenantService.isEnabled() && deployWorkflowsInTenant) + { + processDefKey = key.substring(key.lastIndexOf("@") + 1); + } + else + { + processDefKey = key; + } + return processDefKey; + } protected ProcessDefinition createProcessDefinitionRest(ProcessDefinitionEntity processDefinition) { ProcessDefinition processDefinitionRest = new ProcessDefinition(processDefinition); + String localKey = getLocalProcessDefinitionKey(processDefinition.getKey()); + processDefinitionRest.setKey(localKey); + + String displayId = localKey + ".workflow"; + processDefinitionRest.setTitle(getLabel(displayId, "title")); + processDefinitionRest.setDescription(getLabel(displayId, "description")); + processDefinitionRest.setGraphicNotationDefined(processDefinition.isGraphicalNotationDefined()); if (processDefinition.hasStartFormKey()) { - try { - StartFormData startFormData = activitiProcessEngine.getFormService().getStartFormData(processDefinition.getId()); + try + { + StartFormData startFormData = null; + ProcessDefinitionEntity definitionEntity = getCachedProcessDefinition(processDefinition.getId()); + if (definitionEntity != null) + { + StartFormHandler startFormHandler = definitionEntity.getStartFormHandler(); + if (startFormHandler == null) { + throw new ApiException("No start form defined for " + processDefinition.getId()); + } + + startFormData = startFormHandler.createStartFormData(definitionEntity); + } + else + { + startFormData = activitiProcessEngine.getFormService().getStartFormData(processDefinition.getId()); + } + if (startFormData != null) { processDefinitionRest.setStartFormResourceKey(startFormData.getFormKey()); } - } catch(Exception e) { - e.printStackTrace(); + } + catch (ApiException e) + { + throw e; + } + catch (Exception e) + { + throw new ApiException("Error while retrieving start form key"); } } return processDefinitionRest; } + + protected String getLabel(String displayId, String labelKey) + { + String keyBase = displayId.replace(":", "_"); + String key = keyBase+ "." + labelKey; + String label = messageService.getMessage(key); + return label; + } } diff --git a/source/java/org/alfresco/rest/workflow/api/impl/ProcessesImpl.java b/source/java/org/alfresco/rest/workflow/api/impl/ProcessesImpl.java index ee567020ac..6871d31661 100644 --- a/source/java/org/alfresco/rest/workflow/api/impl/ProcessesImpl.java +++ b/source/java/org/alfresco/rest/workflow/api/impl/ProcessesImpl.java @@ -39,13 +39,11 @@ import org.activiti.engine.RuntimeService; import org.activiti.engine.form.StartFormData; import org.activiti.engine.history.HistoricProcessInstance; import org.activiti.engine.history.HistoricProcessInstanceQuery; -import org.activiti.engine.history.HistoricTaskInstance; -import org.activiti.engine.history.HistoricTaskInstanceQuery; import org.activiti.engine.history.HistoricVariableInstance; import org.activiti.engine.impl.bpmn.diagram.ProcessDiagramGenerator; import org.activiti.engine.impl.identity.Authentication; -import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity; import org.activiti.engine.repository.ProcessDefinition; +import org.activiti.engine.repository.ProcessDefinitionQuery; import org.activiti.engine.runtime.ProcessInstance; import org.alfresco.model.ContentModel; import org.alfresco.repo.i18n.MessageService; @@ -71,7 +69,6 @@ import org.alfresco.rest.antlr.WhereClauseParser; import org.alfresco.rest.framework.core.exceptions.ApiException; import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException; import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException; -import org.alfresco.rest.framework.core.exceptions.PermissionDeniedException; import org.alfresco.rest.framework.resource.content.BinaryResource; import org.alfresco.rest.framework.resource.content.FileBinaryResource; import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo; @@ -90,12 +87,15 @@ import org.alfresco.service.cmr.dictionary.PropertyDefinition; import org.alfresco.service.cmr.dictionary.TypeDefinition; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.ContentData; +import org.alfresco.service.cmr.repository.InvalidNodeRefException; 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.repository.datatype.DefaultTypeConverter; import org.alfresco.service.cmr.security.PersonService; import org.alfresco.service.namespace.InvalidQNameException; import org.alfresco.service.namespace.QName; +import org.alfresco.util.ISO8601DateFormat; import org.alfresco.util.TempFileProvider; import org.alfresco.util.collections.CollectionUtils; import org.apache.commons.io.IOUtils; @@ -142,6 +142,10 @@ public class ProcessesImpl extends WorkflowRestImpl implements Processes "startedAt", "endedAt" )); + private static final Set PROCESS_COLLECTION_SORT_PROPERTIES = new HashSet(Arrays.asList( + "processDefinitionId", "businessKey", "id", "startedAt", "endedAt", "durationInMillis" + )); + public void setAuthorityDAO(AuthorityDAO authorityDAO) { this.authorityDAO = authorityDAO; @@ -303,8 +307,7 @@ public class ProcessesImpl extends WorkflowRestImpl implements Processes final HistoricProcessInstanceQuery query = activitiProcessEngine .getHistoryService() - .createHistoricProcessInstanceQuery() - .orderByProcessInstanceStartTime().desc(); + .createHistoricProcessInstanceQuery(); if (processDefinitionId != null) query.processDefinitionId(processDefinitionId); if (businessKey != null) query.processInstanceBusinessKey(businessKey); @@ -400,12 +403,71 @@ public class ProcessesImpl extends WorkflowRestImpl implements Processes query.involvedUser(AuthenticationUtil.getRunAsUser()); } + String sortParam = parameters.getParameter("sort"); + if (sortParam != null) + { + if (PROCESS_COLLECTION_SORT_PROPERTIES.contains(sortParam)) + { + if ("processDefinitionId".equalsIgnoreCase(sortParam)) + { + query.orderByProcessDefinitionId(); + } + else if ("id".equalsIgnoreCase(sortParam)) + { + query.orderByProcessInstanceId(); + } + else if ("businessKey".equalsIgnoreCase(sortParam)) + { + query.orderByProcessInstanceBusinessKey(); + } + else if ("startedAt".equalsIgnoreCase(sortParam)) + { + query.orderByProcessInstanceStartTime(); + } + else if ("endedAt".equalsIgnoreCase(sortParam)) + { + query.orderByProcessInstanceEndTime(); + } + else if ("durationInMillis".equalsIgnoreCase(sortParam)) + { + query.orderByProcessInstanceDuration(); + } + } + else + { + throw new InvalidArgumentException("sort " + sortParam + + " is not supported, supported items are " + PROCESS_COLLECTION_SORT_PROPERTIES.toArray()); + } + + String sortOrderParam = parameters.getParameter("sortOrder"); + if (sortOrderParam != null) + { + if ("asc".equalsIgnoreCase(sortOrderParam)) + { + query.asc(); + } + else if ("desc".equalsIgnoreCase(sortOrderParam)) + { + query.desc(); + } + else + { + throw new InvalidArgumentException("sort order " + sortOrderParam + + " is not supported, supported items are asc and desc"); + } + } + } + else + { + query.orderByProcessInstanceStartTime().desc(); + } + List processInstances = query.listPage(paging.getSkipCount(), paging.getMaxItems()); List page = new ArrayList(processInstances.size()); for (HistoricProcessInstance processInstance: processInstances) { - page.add(new ProcessInfo(processInstance)); + page.add(createProcessInfo(processInstance)); } return CollectionWithPagingInfo.asPaged(paging, page, false, page.size()); @@ -419,8 +481,6 @@ public class ProcessesImpl extends WorkflowRestImpl implements Processes throw new InvalidArgumentException("processId is required to get the process info"); } - validateIfUserAllowedToWorkWithProcess(processId); - HistoricProcessInstance processInstance = activitiProcessEngine .getHistoryService() .createHistoricProcessInstanceQuery() @@ -429,10 +489,10 @@ public class ProcessesImpl extends WorkflowRestImpl implements Processes if (processInstance == null) { - throw new EntityNotFoundException("could not find process for id " + processId); + throw new EntityNotFoundException(processId); } - return new ProcessInfo(processInstance); + return createProcessInfo(processInstance); } @Override @@ -455,7 +515,7 @@ public class ProcessesImpl extends WorkflowRestImpl implements Processes ProcessDefinition definition = activitiProcessEngine .getRepositoryService() .createProcessDefinitionQuery() - .processDefinitionKey(createProcessDefinitionKey(process.getProcessDefinitionKey())) + .processDefinitionKey(getProcessDefinitionKey(process.getProcessDefinitionKey())) .latestVersion() .singleResult(); @@ -475,7 +535,17 @@ public class ProcessesImpl extends WorkflowRestImpl implements Processes if(!definitionExistingChecked) { // Check if the required definition actually exists - if(activitiProcessEngine.getRepositoryService().createProcessDefinitionQuery().processDefinitionId(processDefinitionId).count() == 0) + ProcessDefinitionQuery query = activitiProcessEngine + .getRepositoryService() + .createProcessDefinitionQuery() + .processDefinitionId(processDefinitionId); + + if (tenantService.isEnabled() && deployWorkflowsInTenant) + { + query.processDefinitionKeyLike("@" + TenantUtil.getCurrentDomain() + "@%"); + } + + if(query.count() == 0) { throw new InvalidArgumentException("No workflow definition could be found with id '" + processDefinitionId +"'."); } @@ -512,10 +582,14 @@ public class ProcessesImpl extends WorkflowRestImpl implements Processes if (taskAssociations.containsKey(propNameMap.get(variableName))) { AssociationDefinition associationDef = taskAssociations.get(propNameMap.get(variableName)); - if (variableValue != null && "person".equalsIgnoreCase(associationDef.getTargetClass().getTitle(dictionaryService))) + if (variableValue != null && ContentModel.TYPE_PERSON.equals(associationDef.getTargetClass().getName())) { variableValue = getPersonNodeRef(variableValue.toString()); } + else if (variableValue != null && ContentModel.TYPE_AUTHORITY_CONTAINER.equals(associationDef.getTargetClass().getName())) + { + variableValue = authorityService.getAuthorityNodeRef(variableValue.toString()); + } } if (variableValue instanceof Serializable) @@ -533,28 +607,29 @@ public class ProcessesImpl extends WorkflowRestImpl implements Processes NodeRef workflowPackageNodeRef = null; try { - workflowPackageNodeRef = workflowPackageComponent.createPackage(null); - startParams.put(WorkflowModel.ASSOC_PACKAGE, workflowPackageNodeRef); + workflowPackageNodeRef = workflowPackageComponent.createPackage(null); + startParams.put(WorkflowModel.ASSOC_PACKAGE, workflowPackageNodeRef); } catch (Exception e) { - throw new ApiException("couldn't create workflow package: " + e.getMessage(), e); + throw new ApiException("couldn't create workflow package: " + e.getMessage(), e); } if (org.apache.commons.collections.CollectionUtils.isNotEmpty(process.getItems())) { - try - { - for (String item: process.getItems()) + try { - QName workflowPackageItemId = QName.createQName("wpi", item); - nodeService.addChild(workflowPackageNodeRef, new NodeRef(item), WorkflowModel.ASSOC_PACKAGE_CONTAINS, workflowPackageItemId); + for (String item: process.getItems()) + { + NodeRef itemNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, item); + QName workflowPackageItemId = QName.createQName("wpi", itemNodeRef.toString()); + nodeService.addChild(workflowPackageNodeRef, itemNodeRef, WorkflowModel.ASSOC_PACKAGE_CONTAINS, workflowPackageItemId); + } + } + catch (Exception e) + { + throw new ApiException("Error while adding items to package: " + e.getMessage(), e); } - } - catch (Exception e) - { - throw new ApiException("Error while adding items to package: " + e.getMessage(), e); - } } // Set start task properties. This should be done before instance is started, since it's id will be used @@ -578,7 +653,7 @@ public class ProcessesImpl extends WorkflowRestImpl implements Processes } } - if(tenantService.isEnabled()) + if (tenantService.isEnabled()) { // Specify which tenant domain the workflow was started in. variables.put(ActivitiConstants.VAR_TENANT_DOMAIN, TenantUtil.getCurrentDomain()); @@ -598,7 +673,7 @@ public class ProcessesImpl extends WorkflowRestImpl implements Processes .processInstanceId(processInstance.getId()) .singleResult(); - return new ProcessInfo(historicProcessInstance); + return createProcessInfo(historicProcessInstance); } @Override @@ -673,7 +748,7 @@ public class ProcessesImpl extends WorkflowRestImpl implements Processes List documentList = nodeService.getChildAssocs(packageScriptNode.getNodeRef()); for (ChildAssociationRef childAssociationRef : documentList) { - if (childAssociationRef.getChildRef().toString().equals(itemId)) + if (childAssociationRef.getChildRef().getId().equals(itemId)) { item = createItemForNodeRef(childAssociationRef.getChildRef()); break; @@ -718,10 +793,22 @@ public class ProcessesImpl extends WorkflowRestImpl implements Processes throw new InvalidArgumentException("process doesn't contain a workflow package variable"); } + // check if noderef exists try { - QName workflowPackageItemId = QName.createQName("wpi", item.getId()); - nodeService.addChild(packageScriptNode.getNodeRef(), new NodeRef(item.getId()), WorkflowModel.ASSOC_PACKAGE_CONTAINS, workflowPackageItemId); + nodeService.getProperties(new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, item.getId())); + } + catch (Exception e) + { + throw new EntityNotFoundException("item with id " + item.getId() + " not found"); + } + + try + { + NodeRef itemNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, item.getId()); + QName workflowPackageItemId = QName.createQName("wpi", itemNodeRef.toString()); + nodeService.addChild(packageScriptNode.getNodeRef(), itemNodeRef, + WorkflowModel.ASSOC_PACKAGE_CONTAINS, workflowPackageItemId); } catch (Exception e) { @@ -761,13 +848,29 @@ public class ProcessesImpl extends WorkflowRestImpl implements Processes throw new InvalidArgumentException("process doesn't contain a workflow package variable"); } + boolean itemIdFoundInPackage = false; + List documentList = nodeService.getChildAssocs(packageScriptNode.getNodeRef()); + for (ChildAssociationRef childAssociationRef : documentList) + { + if (childAssociationRef.getChildRef().getId().equals(itemId)) + { + itemIdFoundInPackage = true; + break; + } + } + + if (itemIdFoundInPackage == false) + { + throw new EntityNotFoundException("Item " + itemId + " not found in the process package variable"); + } + try { - nodeService.removeChild(packageScriptNode.getNodeRef(), new NodeRef(itemId)); + nodeService.removeChild(packageScriptNode.getNodeRef(), new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, itemId)); } - catch (Exception e) + catch (InvalidNodeRefException e) { - throw new ApiException("could not delete item from process " + e.getMessage(), e); + throw new EntityNotFoundException("Item " + itemId + " not found"); } } @@ -822,6 +925,7 @@ public class ProcessesImpl extends WorkflowRestImpl implements Processes { formKey = startFormData.getFormKey(); } + TypeDefinition startTaskTypeDefinition = workflowFactory.getTaskFullTypeDefinition(formKey, true); // Convert raw variables to Variable objects @@ -838,12 +942,41 @@ public class ProcessesImpl extends WorkflowRestImpl implements Processes ProcessInstance processInstance = activitiProcessEngine.getRuntimeService().createProcessInstanceQuery() .processInstanceId(processId).singleResult(); - if(processInstance == null) + if (processInstance == null) { throw new EntityNotFoundException(processId); } - if(variable.getName() == null) + return updateVariableInProcess(processId, variable); + } + + @Override + public List updateVariables(String processId, List variables) + { + validateIfUserAllowedToWorkWithProcess(processId); + + ProcessInstance processInstance = activitiProcessEngine.getRuntimeService().createProcessInstanceQuery() + .processInstanceId(processId).singleResult(); + + if (processInstance == null) + { + throw new EntityNotFoundException(processId); + } + + List updatedVariables = new ArrayList(); + if (variables != null) + { + for (Variable variable : variables) + { + updatedVariables.add(updateVariableInProcess(processId, variable)); + } + } + return updatedVariables; + } + + protected Variable updateVariableInProcess(String processId,Variable variable) + { + if (variable.getName() == null) { throw new InvalidArgumentException("Variable name is required."); } @@ -872,9 +1005,18 @@ public class ProcessesImpl extends WorkflowRestImpl implements Processes throw new InvalidArgumentException("Unsupported type of variable: '" + variable.getType() +"'."); } + Object actualValue = null; + if ("java.util.Date".equalsIgnoreCase(dataTypeDefinition.getJavaClassName())) + { + // fix for different ISO 8601 Date format classes in Alfresco (org.alfresco.util and Spring Surf) + actualValue = ISO8601DateFormat.parse((String) variable.getValue()); + } + else + { + actualValue = DefaultTypeConverter.INSTANCE.convert(dataTypeDefinition, variable.getValue()); + } + variable.setValue(actualValue); - // Get raw variable value and set value - Object actualValue = DefaultTypeConverter.INSTANCE.convert(dataTypeDefinition, variable.getValue()); activitiProcessEngine.getRuntimeService().setVariable(processId, variable.getName(), actualValue); // Set actual used type before returning @@ -921,13 +1063,10 @@ public class ProcessesImpl extends WorkflowRestImpl implements Processes try { - ProcessDefinition procDef = activitiProcessEngine.getRepositoryService().createProcessDefinitionQuery() - .processDefinitionId(processInstance.getProcessDefinitionId()) - .singleResult(); + BpmnModel model = activitiProcessEngine.getRepositoryService().getBpmnModel(processInstance.getProcessDefinitionId()); - if(((ProcessDefinitionEntity) procDef).isGraphicalNotationDefined()) + if(model != null && model.getLocationMap().size() > 0) { - BpmnModel model = activitiProcessEngine.getRepositoryService().getBpmnModel(processInstance.getProcessDefinitionId()); List activeActivities = activitiProcessEngine.getRuntimeService().getActiveActivityIds(processId); InputStream generateDiagram = ProcessDiagramGenerator.generateDiagram(model, "png", activeActivities); @@ -949,65 +1088,7 @@ public class ProcessesImpl extends WorkflowRestImpl implements Processes } } - protected void validateIfUserAllowedToWorkWithProcess(String processId) - { - if (tenantService.isEnabled()) - { - try - { - String tenantDomain = (String) activitiProcessEngine.getRuntimeService().getVariable(processId, ActivitiConstants.VAR_TENANT_DOMAIN); - if (TenantUtil.getCurrentDomain().equals(tenantDomain) == false) - { - throw new PermissionDeniedException("Process is running in another tenant"); - } - } - catch (ActivitiObjectNotFoundException e) - { - throw new EntityNotFoundException(processId); - } - } - - try - { - ActivitiScriptNode initiator = (ActivitiScriptNode) activitiProcessEngine.getRuntimeService().getVariable(processId, WorkflowConstants.PROP_INITIATOR); - if (AuthenticationUtil.getRunAsUser().equals(initiator.getNodeRef().getId())) - { - // user is allowed - return; - } - } - catch (ActivitiObjectNotFoundException e) - { - throw new EntityNotFoundException(processId); - } - - HistoricTaskInstanceQuery query = activitiProcessEngine.getHistoryService() - .createHistoricTaskInstanceQuery() - .processInstanceId(processId); - - if (authorityService.isAdminAuthority(AuthenticationUtil.getRunAsUser())) - { - // Admin is allowed to read all processes in the current tenant - if (tenantService.isEnabled()) - { - query.processVariableValueEquals(ActivitiConstants.VAR_TENANT_DOMAIN, TenantUtil.getCurrentDomain()); - } - } - else - { - // If non-admin user, involvement in the task is required (either owner, assignee or externally involved). - query.taskInvolvedUser(AuthenticationUtil.getRunAsUser()); - } - - List taskList = query.list(); - - if(org.apache.commons.collections.CollectionUtils.isEmpty(taskList)) - { - throw new PermissionDeniedException("user is not allowed to access information about process " + processId); - } - } - - protected String createProcessDefinitionKey(String paramProcessDefinitionKey) + protected String getProcessDefinitionKey(String paramProcessDefinitionKey) { String processDefinitionKey = null; if (tenantService.isEnabled() && deployWorkflowsInTenant) @@ -1020,6 +1101,20 @@ public class ProcessesImpl extends WorkflowRestImpl implements Processes } return processDefinitionKey; } + + protected String getLocalProcessDefinitionKey(String key) + { + String processDefKey = null; + if (tenantService.isEnabled() && deployWorkflowsInTenant) + { + processDefKey = key.substring(key.lastIndexOf("@") + 1); + } + else + { + processDefKey = key; + } + return processDefKey; + } protected NodeRef getPersonNodeRef(String name) { @@ -1034,6 +1129,18 @@ public class ProcessesImpl extends WorkflowRestImpl implements Processes return authority; } + protected ProcessInfo createProcessInfo(HistoricProcessInstance processInstance) + { + ProcessInfo processInfo = new ProcessInfo(processInstance); + ProcessDefinition definitionEntity = getCachedProcessDefinition(processInstance.getProcessDefinitionId()); + if (definitionEntity == null) + { + definitionEntity = activitiProcessEngine.getRepositoryService().getProcessDefinition(processInstance.getProcessDefinitionId()); + } + processInfo.setProcessDefinitionKey(getLocalProcessDefinitionKey(definitionEntity.getKey())); + return processInfo; + } + protected Item createItemForNodeRef(NodeRef nodeRef) { Map properties = nodeService.getProperties(nodeRef); Item item = new Item(); @@ -1047,7 +1154,7 @@ public class ProcessesImpl extends WorkflowRestImpl implements Processes ContentData contentData = (ContentData) nodeService.getProperty(nodeRef, ContentModel.PROP_CONTENT); - item.setId(nodeRef.toString()); + item.setId(nodeRef.getId()); item.setName(name); item.setTitle(title); item.setDescription(description); diff --git a/source/java/org/alfresco/rest/workflow/api/impl/TasksImpl.java b/source/java/org/alfresco/rest/workflow/api/impl/TasksImpl.java index ddb6c650f5..7d281a9d35 100644 --- a/source/java/org/alfresco/rest/workflow/api/impl/TasksImpl.java +++ b/source/java/org/alfresco/rest/workflow/api/impl/TasksImpl.java @@ -30,6 +30,8 @@ import org.activiti.engine.ActivitiTaskAlreadyClaimedException; import org.activiti.engine.form.FormData; import org.activiti.engine.history.HistoricTaskInstance; import org.activiti.engine.history.HistoricTaskInstanceQuery; +import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity; +import org.activiti.engine.impl.task.TaskDefinition; import org.activiti.engine.task.IdentityLink; import org.activiti.engine.task.IdentityLinkType; import org.activiti.engine.task.TaskQuery; @@ -57,6 +59,7 @@ import org.alfresco.rest.workflow.api.model.FormModelElement; import org.alfresco.rest.workflow.api.model.Item; import org.alfresco.rest.workflow.api.model.Task; import org.alfresco.rest.workflow.api.model.TaskCandidate; +import org.alfresco.rest.workflow.api.model.TaskStateTransition; import org.alfresco.rest.workflow.api.model.TaskVariable; import org.alfresco.rest.workflow.api.model.VariableScope; import org.alfresco.service.cmr.dictionary.AssociationDefinition; @@ -64,8 +67,10 @@ import org.alfresco.service.cmr.dictionary.DataTypeDefinition; import org.alfresco.service.cmr.dictionary.PropertyDefinition; import org.alfresco.service.cmr.dictionary.TypeDefinition; import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; +import org.alfresco.service.cmr.security.AuthorityType; import org.alfresco.service.namespace.InvalidQNameException; import org.alfresco.service.namespace.QName; +import org.alfresco.util.ISO8601DateFormat; public class TasksImpl extends WorkflowRestImpl implements Tasks { @@ -76,12 +81,12 @@ public class TasksImpl extends WorkflowRestImpl implements Tasks * All properties that are read-only and cannot be updated on a single task-resource. */ private static final List TASK_READ_ONLY_PROPERTIES = Arrays.asList( - "id", "processId", "processDefinitionId", "actibityDefinitionId", "startedAt", "endedAt", "durationInMs", "formResourceKey" + "id", "processId", "processDefinitionId", "activityDefinitionId", "startedAt", "endedAt", "durationInMs", "formResourceKey" ); private static final Set TASK_COLLECTION_EQUALS_QUERY_PROPERTIES = new HashSet(Arrays.asList( - "status", "assignee", "owner", "candidateUser", "candidateGroup", "name", "description", "priority", "processInstanceId", - "processInstanceBusinessKey", "activityDefinitionId", "processDefinitionId", "processDefinitionName", "startedAt", "dueAt" + "status", "assignee", "owner", "candidateUser", "candidateGroup", "name", "description", "priority", "processId", + "processBusinessKey", "activityDefinitionId", "processDefinitionId", "processDefinitionName", "startedAt", "dueAt" )); private static final Set TASK_COLLECTION_MATCHES_QUERY_PROPERTIES = new HashSet(Arrays.asList( @@ -104,6 +109,14 @@ public class TasksImpl extends WorkflowRestImpl implements Tasks "priority" )); + private static final Set TASK_COLLECTION_RUNNING_SORT_PROPERTIES = new HashSet(Arrays.asList( + "id", "name", "description", "priority", "processId", "assignee", "startedAt", "dueAt" + )); + + private static final Set TASK_COLLECTION_HISTORY_SORT_PROPERTIES = new HashSet(Arrays.asList( + "id", "name", "description", "priority", "processId", "processDefinitionId", "assignee", "owner", "startedAt", "endedAt", "durationInMs", "dueAt" + )); + private RestVariableHelper restVariableHelper; private WorkflowObjectFactory workflowFactory; private WorkflowQNameConverter qNameConverter; @@ -155,8 +168,8 @@ public class TasksImpl extends WorkflowRestImpl implements Tasks Integer priority = propertyWalker.getProperty("priority", WhereClauseParser.EQUALS, Integer.class); Integer priorityGreaterThanOrEquals = propertyWalker.getProperty("priority", WhereClauseParser.GREATERTHANOREQUALS, Integer.class); Integer priorityLessThanOrEquals = propertyWalker.getProperty("priority", WhereClauseParser.LESSTHANOREQUALS, Integer.class); - String processInstanceId = propertyWalker.getProperty("processInstanceId", WhereClauseParser.EQUALS); - String processInstanceBusinessKey = propertyWalker.getProperty("processInstanceBusinessKey", WhereClauseParser.EQUALS); + String processInstanceId = propertyWalker.getProperty("processId", WhereClauseParser.EQUALS); + String processInstanceBusinessKey = propertyWalker.getProperty("processBusinessKey", WhereClauseParser.EQUALS); String activityDefinitionId = propertyWalker.getProperty("activityDefinitionId", WhereClauseParser.EQUALS); String activityDefinitionIdLike = propertyWalker.getProperty("activityDefinitionId", WhereClauseParser.MATCHES); String processDefinitionId = propertyWalker.getProperty("processDefinitionId", WhereClauseParser.EQUALS); @@ -177,7 +190,30 @@ public class TasksImpl extends WorkflowRestImpl implements Tasks if (assignee != null) query.taskAssignee(assignee); if (owner != null) query.taskOwner(owner); - if (candidateUser != null) query.taskCandidateUser(candidateUser); + if (candidateUser != null) + { + Set parents = authorityService.getContainingAuthorities(AuthorityType.GROUP, candidateUser, false); + if (parents != null) + { + List authorities = new ArrayList(); + authorities.addAll(parents); + + // there's a limitation in at least Oracle for using an IN statement with more than 1000 items + if (parents.size() > 1000) + { + authorities = authorities.subList(0, 1000); + } + + if (authorities.size() > 0) + { + query.taskCandidateGroupIn(authorities); + } + else + { + query.taskCandidateUser(candidateUser); + } + } + } if (candidateGroup != null) query.taskCandidateGroup(candidateGroup); if (name != null) query.taskName(name); if (nameLike != null) query.taskNameLike(nameLike); @@ -208,6 +244,30 @@ public class TasksImpl extends WorkflowRestImpl implements Tasks { query.taskVariableValueEquals(queryVariableHolder.getPropertyName(), queryVariableHolder.getPropertyValue()); } + else if (queryVariableHolder.getOperator() == WhereClauseParser.GREATERTHAN) + { + query.taskVariableValueGreaterThan(queryVariableHolder.getPropertyName(), queryVariableHolder.getPropertyValue()); + } + else if (queryVariableHolder.getOperator() == WhereClauseParser.GREATERTHANOREQUALS) + { + query.taskVariableValueGreaterThanOrEqual(queryVariableHolder.getPropertyName(), queryVariableHolder.getPropertyValue()); + } + else if (queryVariableHolder.getOperator() == WhereClauseParser.LESSTHAN) + { + query.taskVariableValueLessThan(queryVariableHolder.getPropertyName(), queryVariableHolder.getPropertyValue()); + } + else if (queryVariableHolder.getOperator() == WhereClauseParser.LESSTHANOREQUALS) + { + query.taskVariableValueLessThanOrEqual(queryVariableHolder.getPropertyName(), queryVariableHolder.getPropertyValue()); + } + else if (queryVariableHolder.getOperator() == WhereClauseParser.MATCHES) + { + if (queryVariableHolder.getPropertyValue() instanceof String == false) + { + throw new InvalidArgumentException("the matches operator can only be used with a String value for property " + queryVariableHolder.getPropertyName()); + } + query.taskVariableValueLike(queryVariableHolder.getPropertyName(), (String) queryVariableHolder.getPropertyValue()); + } else if (queryVariableHolder.getOperator() == WhereClauseParser.NEGATION) { query.taskVariableValueNotEquals(queryVariableHolder.getPropertyName(), queryVariableHolder.getPropertyValue()); @@ -285,6 +345,30 @@ public class TasksImpl extends WorkflowRestImpl implements Tasks { query.taskVariableValueEquals(queryVariableHolder.getPropertyName(), queryVariableHolder.getPropertyValue()); } + else if (queryVariableHolder.getOperator() == WhereClauseParser.GREATERTHAN) + { + query.taskVariableValueGreaterThan(queryVariableHolder.getPropertyName(), queryVariableHolder.getPropertyValue()); + } + else if (queryVariableHolder.getOperator() == WhereClauseParser.GREATERTHANOREQUALS) + { + query.taskVariableValueGreaterThanOrEqual(queryVariableHolder.getPropertyName(), queryVariableHolder.getPropertyValue()); + } + else if (queryVariableHolder.getOperator() == WhereClauseParser.LESSTHAN) + { + query.taskVariableValueLessThan(queryVariableHolder.getPropertyName(), queryVariableHolder.getPropertyValue()); + } + else if (queryVariableHolder.getOperator() == WhereClauseParser.LESSTHANOREQUALS) + { + query.taskVariableValueLessThanOrEqual(queryVariableHolder.getPropertyName(), queryVariableHolder.getPropertyValue()); + } + else if (queryVariableHolder.getOperator() == WhereClauseParser.MATCHES) + { + if (queryVariableHolder.getPropertyValue() instanceof String == false) + { + throw new InvalidArgumentException("the matches operator can only be used with a String value for property " + queryVariableHolder.getPropertyName()); + } + query.taskVariableValueLike(queryVariableHolder.getPropertyName(), (String) queryVariableHolder.getPropertyValue()); + } else if (queryVariableHolder.getOperator() == WhereClauseParser.NEGATION) { query.taskVariableValueNotEquals(queryVariableHolder.getPropertyName(), queryVariableHolder.getPropertyValue()); @@ -325,6 +409,220 @@ public class TasksImpl extends WorkflowRestImpl implements Tasks return CollectionWithPagingInfo.asPaged(paging, page, false, page.size()); } + + @Override + public CollectionWithPagingInfo getTasks(String processId, Parameters parameters) + { + Paging paging = parameters.getPaging(); + String status = parameters.getParameter("status"); + + validateIfUserAllowedToWorkWithProcess(processId); + + List page = null; + if (status == null || STATUS_ACTIVE.equals(status)) + { + TaskQuery query = activitiProcessEngine + .getTaskService() + .createTaskQuery(); + + query.processInstanceId(processId); + + String sortParam = parameters.getParameter("sort"); + if (sortParam != null) + { + if (TASK_COLLECTION_RUNNING_SORT_PROPERTIES.contains(sortParam)) + { + if ("id".equalsIgnoreCase(sortParam)) + { + query.orderByTaskId(); + } + else if ("name".equalsIgnoreCase(sortParam)) + { + query.orderByTaskName(); + } + else if ("description".equalsIgnoreCase(sortParam)) + { + query.orderByTaskDescription(); + } + else if ("priority".equalsIgnoreCase(sortParam)) + { + query.orderByTaskPriority(); + } + else if ("processId".equalsIgnoreCase(sortParam)) + { + query.orderByProcessInstanceId(); + } + else if ("assignee".equalsIgnoreCase(sortParam)) + { + query.orderByTaskAssignee(); + } + else if ("startedAt".equalsIgnoreCase(sortParam)) + { + query.orderByTaskCreateTime(); + } + else if ("dueAt".equalsIgnoreCase(sortParam)) + { + query.orderByDueDate(); + } + } + else + { + throw new InvalidArgumentException("sort " + sortParam + + " is not supported, supported items are " + TASK_COLLECTION_RUNNING_SORT_PROPERTIES.toArray()); + } + + String sortOrderParam = parameters.getParameter("sortOrder"); + if (sortOrderParam != null) + { + if ("asc".equalsIgnoreCase(sortOrderParam)) + { + query.asc(); + } + else if ("desc".equalsIgnoreCase(sortOrderParam)) + { + query.desc(); + } + else + { + throw new InvalidArgumentException("sort order " + sortOrderParam + + " is not supported, supported items are asc and desc"); + } + } + } + else + { + query.orderByDueDate().asc(); + } + + List tasks = query.listPage(paging.getSkipCount(), paging.getMaxItems()); + + page = new ArrayList(tasks.size()); + for (org.activiti.engine.task.Task taskInstance: tasks) + { + Task task = new Task(taskInstance); + task.setFormResourceKey(getFormResourceKey(taskInstance)); + page.add(task); + } + } + else if (STATUS_COMPLETED.equals(status) || STATUS_ANY.equals(status)) + { + HistoricTaskInstanceQuery query = activitiProcessEngine + .getHistoryService() + .createHistoricTaskInstanceQuery(); + + if (STATUS_COMPLETED.equals(status)) query.finished(); + + query.processInstanceId(processId); + + // Add tenant filtering + if(tenantService.isEnabled()) { + query.processVariableValueEquals(ActivitiConstants.VAR_TENANT_DOMAIN, TenantUtil.getCurrentDomain()); + } + + // Add involvment filtering if user is not admin + if(!authorityService.isAdminAuthority(AuthenticationUtil.getRunAsUser())) { + query.taskInvolvedUser(AuthenticationUtil.getRunAsUser()); + } + + String sortParam = parameters.getParameter("sort"); + if (sortParam != null) + { + if (TASK_COLLECTION_HISTORY_SORT_PROPERTIES.contains(sortParam)) + { + if ("id".equalsIgnoreCase(sortParam)) + { + query.orderByTaskId(); + } + else if ("name".equalsIgnoreCase(sortParam)) + { + query.orderByTaskName(); + } + else if ("description".equalsIgnoreCase(sortParam)) + { + query.orderByTaskDescription(); + } + else if ("priority".equalsIgnoreCase(sortParam)) + { + query.orderByTaskPriority(); + } + else if ("processId".equalsIgnoreCase(sortParam)) + { + query.orderByProcessInstanceId(); + } + else if ("processDefinitionId".equalsIgnoreCase(sortParam)) + { + query.orderByProcessDefinitionId(); + } + else if ("assignee".equalsIgnoreCase(sortParam)) + { + query.orderByTaskAssignee(); + } + else if ("owner".equalsIgnoreCase(sortParam)) + { + query.orderByTaskOwner(); + } + else if ("startedAt".equalsIgnoreCase(sortParam)) + { + query.orderByHistoricTaskInstanceStartTime(); + } + else if ("endedAt".equalsIgnoreCase(sortParam)) + { + query.orderByHistoricTaskInstanceEndTime(); + } + else if ("durationInMs".equalsIgnoreCase(sortParam)) + { + query.orderByHistoricTaskInstanceDuration(); + } + else if ("dueAt".equalsIgnoreCase(sortParam)) + { + query.orderByTaskDueDate(); + } + } + else + { + throw new InvalidArgumentException("sort " + sortParam + + " is not supported, supported items are " + TASK_COLLECTION_HISTORY_SORT_PROPERTIES.toArray()); + } + + String sortOrderParam = parameters.getParameter("sortOrder"); + if (sortOrderParam != null) + { + if ("asc".equalsIgnoreCase(sortOrderParam)) + { + query.asc(); + } + else if ("desc".equalsIgnoreCase(sortOrderParam)) + { + query.desc(); + } + else + { + throw new InvalidArgumentException("sort order " + sortOrderParam + + " is not supported, supported items are asc and desc"); + } + } + } + else + { + query.orderByTaskDueDate().asc(); + } + + List tasks = query.listPage(paging.getSkipCount(), paging.getMaxItems()); + + page = new ArrayList(tasks.size()); + for (HistoricTaskInstance taskInstance: tasks) + { + Task task = new Task(taskInstance); + page.add(task); + } + } + else + { + throw new InvalidArgumentException("Invalid status parameter: " + status); + } + + return CollectionWithPagingInfo.asPaged(paging, page, false, page.size()); + } @Override public Task getTask(String taskId) @@ -509,7 +807,7 @@ public class TasksImpl extends WorkflowRestImpl implements Tasks } // Lookup type definition for the task - TypeDefinition taskType = workflowFactory.getTaskFullTypeDefinition(formData.getFormKey(), true); + TypeDefinition taskType = getWorkflowFactory().getTaskFullTypeDefinition(formData.getFormKey(), true); return getFormModelElements(taskType, paging); } @@ -543,7 +841,25 @@ public class TasksImpl extends WorkflowRestImpl implements Tasks public TaskVariable updateTaskVariable(String taskId, TaskVariable taskVariable) { org.activiti.engine.task.Task taskInstance = getValidTask(taskId, true); - + return updateVariableInTask(taskId, taskInstance, taskVariable); + } + + public List updateTaskVariables(String taskId, List variables) + { + org.activiti.engine.task.Task taskInstance = getValidTask(taskId, true); + List updatedVariables = new ArrayList(); + if (variables != null) + { + for (TaskVariable variable : variables) + { + updatedVariables.add(updateVariableInTask(taskId, taskInstance, variable)); + } + } + return updatedVariables; + } + + protected TaskVariable updateVariableInTask(String taskId, org.activiti.engine.task.Task taskInstance, TaskVariable taskVariable) + { if(taskVariable.getName() == null) { throw new InvalidArgumentException("Variable name is required."); @@ -603,12 +919,22 @@ public class TasksImpl extends WorkflowRestImpl implements Tasks } } - if(dataTypeDefinition == null) + if (dataTypeDefinition == null) { throw new InvalidArgumentException("Unsupported type of variable: '" + taskVariable.getType() +"'."); } - Object actualValue = DefaultTypeConverter.INSTANCE.convert(dataTypeDefinition, taskVariable.getValue()); + Object actualValue = null; + if ("java.util.Date".equalsIgnoreCase(dataTypeDefinition.getJavaClassName())) + { + // fix for different ISO 8601 Date format classes in Alfresco (org.alfresco.util and Spring Surf) + actualValue = ISO8601DateFormat.parse((String) taskVariable.getValue()); + } + else + { + actualValue = DefaultTypeConverter.INSTANCE.convert(dataTypeDefinition, taskVariable.getValue()); + } + taskVariable.setValue(actualValue); if (VariableScope.LOCAL.equals(taskVariable.getVariableScope())) { @@ -716,10 +1042,27 @@ public class TasksImpl extends WorkflowRestImpl implements Tasks return processes.getItems(task.getProcessInstanceId(), paging); } - protected String getFormResourceKey(org.activiti.engine.task.Task task) { + protected String getFormResourceKey(final org.activiti.engine.task.Task task) + { if (task.getProcessDefinitionId() != null) { - return activitiProcessEngine.getFormService().getTaskFormKey(task.getProcessDefinitionId(), task.getTaskDefinitionKey()); + ProcessDefinitionEntity definitionEntity = getCachedProcessDefinition(task.getProcessDefinitionId()); + + String formKey = null; + if (definitionEntity != null) + { + TaskDefinition taskDefinition = definitionEntity.getTaskDefinitions().get(task.getTaskDefinitionKey()); + if (taskDefinition != null) + { + formKey = taskDefinition.getTaskFormHandler().getFormKey().getExpressionText(); + } + } + else + { + formKey = activitiProcessEngine.getFormService().getTaskFormKey(task.getProcessDefinitionId(), task.getTaskDefinitionKey()); + } + + return formKey; } else { @@ -910,10 +1253,10 @@ public class TasksImpl extends WorkflowRestImpl implements Tasks // Task is not yet finished, so potentially claimable. If user is part of a "candidateGroup", the task is accessible to the // user regardless of not being involved/owner/assignee isTaskClaimable = activitiProcessEngine.getTaskService() - .createTaskQuery() - .taskCandidateGroupIn(new ArrayList(authorityService.getAuthoritiesForUser(AuthenticationUtil.getRunAsUser()))) - .taskId(taskId) - .count() == 1; + .createTaskQuery() + .taskCandidateGroupIn(new ArrayList(authorityService.getAuthoritiesForUser(AuthenticationUtil.getRunAsUser()))) + .taskId(taskId) + .count() == 1; } if(!isTaskClaimable) @@ -943,26 +1286,4 @@ public class TasksImpl extends WorkflowRestImpl implements Tasks } return workflowFactory; } - - - private enum TaskStateTransition { - - COMPLETED, CLAIMED, UNCLAIMED, DELEGATED, RESOLVED; - - /** - * @return the {@link TaskStateTransition} for the given string - * @throws InvalidArgumentException when no action exists for the given string - */ - public static TaskStateTransition getTaskActionFromString(String action) - { - for(TaskStateTransition taskAction : values()) - { - if(taskAction.name().toLowerCase().equals(action)) - { - return taskAction; - } - } - throw new InvalidArgumentException("The task state property has an invalid value: " + action); - } - } } diff --git a/source/java/org/alfresco/rest/workflow/api/impl/WorkflowRestImpl.java b/source/java/org/alfresco/rest/workflow/api/impl/WorkflowRestImpl.java index 7308db82c0..aa64e460d1 100644 --- a/source/java/org/alfresco/rest/workflow/api/impl/WorkflowRestImpl.java +++ b/source/java/org/alfresco/rest/workflow/api/impl/WorkflowRestImpl.java @@ -1,6 +1,7 @@ package org.alfresco.rest.workflow.api.impl; import java.util.ArrayList; +import java.util.Arrays; import java.util.Calendar; import java.util.Date; import java.util.HashSet; @@ -9,16 +10,36 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import org.activiti.engine.ActivitiObjectNotFoundException; import org.activiti.engine.ProcessEngine; +import org.activiti.engine.history.HistoricTaskInstance; +import org.activiti.engine.history.HistoricTaskInstanceQuery; +import org.activiti.engine.impl.ProcessEngineImpl; +import org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl; +import org.activiti.engine.impl.context.Context; +import org.activiti.engine.impl.interceptor.Command; +import org.activiti.engine.impl.interceptor.CommandContext; +import org.activiti.engine.impl.persistence.deploy.DeploymentCache; +import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity; import org.alfresco.model.ContentModel; +import org.alfresco.repo.dictionary.constraint.ListOfValuesConstraint; +import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.tenant.TenantService; +import org.alfresco.repo.tenant.TenantUtil; +import org.alfresco.repo.workflow.WorkflowConstants; +import org.alfresco.repo.workflow.activiti.ActivitiConstants; +import org.alfresco.repo.workflow.activiti.ActivitiScriptNode; +import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException; import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException; +import org.alfresco.rest.framework.core.exceptions.PermissionDeniedException; import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo; import org.alfresco.rest.framework.resource.parameters.Paging; import org.alfresco.rest.framework.resource.parameters.Parameters; import org.alfresco.rest.workflow.api.model.FormModelElement; import org.alfresco.service.cmr.dictionary.AssociationDefinition; import org.alfresco.service.cmr.dictionary.ClassDefinition; +import org.alfresco.service.cmr.dictionary.Constraint; +import org.alfresco.service.cmr.dictionary.ConstraintDefinition; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.dictionary.PropertyDefinition; import org.alfresco.service.cmr.dictionary.TypeDefinition; @@ -42,6 +63,7 @@ public class WorkflowRestImpl protected DictionaryService dictionaryService; protected ProcessEngine activitiProcessEngine; protected boolean deployWorkflowsInTenant; + protected List excludeModelTypes = new ArrayList(Arrays.asList("bpm_priority", "bpm_description", "bpm_dueDate")); static { @@ -81,6 +103,32 @@ public class WorkflowRestImpl this.deployWorkflowsInTenant = deployWorkflowsInTenant; } + /** + * Get the process definition from the cache if available + * + * @param processDefinitionId the unique id identifier of the process definition + */ + public ProcessDefinitionEntity getCachedProcessDefinition(final String processDefinitionId) + { + ProcessEngineConfigurationImpl processConfig = (ProcessEngineConfigurationImpl) ((ProcessEngineImpl) activitiProcessEngine).getProcessEngineConfiguration(); + ProcessDefinitionEntity definitionEntity = processConfig.getCommandExecutorTxRequired().execute(new Command() + { + + @Override + public ProcessDefinitionEntity execute(CommandContext commandContext) + { + DeploymentCache cache = Context + .getProcessEngineConfiguration() + .getDeploymentManager() + .getProcessDefinitionCache(); + + return cache.get(processDefinitionId); + } + + }); + return definitionEntity; + } + /** * Get the first parameter value, converted to the requested type. * @param parameters used to extract parameter value from @@ -131,16 +179,33 @@ public class WorkflowRestImpl List page = new ArrayList(); for (Entry entry : taskProperties.entrySet()) { + String name = entry.getKey().toPrefixString(namespaceService).replace(':', '_'); + // Only add properties which are not part of an excluded type - if(!typesToExclude.contains(entry.getValue().getContainerClass().getName())) + if(!typesToExclude.contains(entry.getValue().getContainerClass().getName()) && excludeModelTypes.contains(name) == false) { FormModelElement element = new FormModelElement(); - element.setName(entry.getKey().toPrefixString(namespaceService).replace(':', '_')); + element.setName(name); element.setQualifiedName(entry.getKey().toString()); element.setTitle(entry.getValue().getTitle(dictionaryService)); element.setRequired(entry.getValue().isMandatory()); element.setDataType(entry.getValue().getDataType().getName().toPrefixString(namespaceService)); element.setDefaultValue(entry.getValue().getDefaultValue()); + if (entry.getValue().getConstraints() != null) + { + for (ConstraintDefinition constraintDef : entry.getValue().getConstraints()) + { + Constraint constraint = constraintDef.getConstraint(); + if (constraint != null && constraint instanceof ListOfValuesConstraint) + { + ListOfValuesConstraint valuesConstraint = (ListOfValuesConstraint) constraint; + if (valuesConstraint.getAllowedValues() != null && valuesConstraint.getAllowedValues().size() > 0) + { + element.setAllowedValues(valuesConstraint.getAllowedValues()); + } + } + } + } page.add(element); } } @@ -174,7 +239,8 @@ public class WorkflowRestImpl ClassDefinition parentClassDefinition = taskType.getParentClassDefinition(); boolean contentClassFound = false; - while(parentClassDefinition != null) { + while(parentClassDefinition != null) + { if(contentClassFound) { typesToExclude.add(parentClassDefinition.getName()); @@ -190,4 +256,72 @@ public class WorkflowRestImpl } return typesToExclude; } + + /** + * Validates if the logged in user is allowed to get information about a specific process instance. + * If the user is not allowed an exception is thrown. + * + * @param processId identifier of the process instance + */ + protected void validateIfUserAllowedToWorkWithProcess(String processId) + { + if (tenantService.isEnabled()) + { + try + { + String tenantDomain = (String) activitiProcessEngine.getRuntimeService().getVariable(processId, ActivitiConstants.VAR_TENANT_DOMAIN); + if (TenantUtil.getCurrentDomain().equals(tenantDomain) == false) + { + throw new PermissionDeniedException("Process is running in another tenant"); + } + } + catch (ActivitiObjectNotFoundException e) + { + throw new EntityNotFoundException(processId); + } + } + + try + { + ActivitiScriptNode initiator = (ActivitiScriptNode) activitiProcessEngine.getRuntimeService().getVariable(processId, WorkflowConstants.PROP_INITIATOR); + if (AuthenticationUtil.getRunAsUser().equals(initiator.getNodeRef().getId())) + { + // user is allowed + return; + } + } + catch (ActivitiObjectNotFoundException e) + { + throw new EntityNotFoundException(processId); + } + + HistoricTaskInstanceQuery query = activitiProcessEngine.getHistoryService() + .createHistoricTaskInstanceQuery() + .processInstanceId(processId); + + if (authorityService.isAdminAuthority(AuthenticationUtil.getRunAsUser())) + { + // Admin is allowed to read all processes in the current tenant + if (tenantService.isEnabled()) + { + query.processVariableValueEquals(ActivitiConstants.VAR_TENANT_DOMAIN, TenantUtil.getCurrentDomain()); + } + else + { + return; + } + } + else + { + // If non-admin user, involvement in the task is required (either owner, assignee or externally involved). + query.taskInvolvedUser(AuthenticationUtil.getRunAsUser()); + } + + List taskList = query.list(); + + if(org.apache.commons.collections.CollectionUtils.isEmpty(taskList)) + { + throw new PermissionDeniedException("user is not allowed to access information about process " + processId); + } + } } diff --git a/source/java/org/alfresco/rest/workflow/api/model/Activity.java b/source/java/org/alfresco/rest/workflow/api/model/Activity.java new file mode 100644 index 0000000000..e967c565b6 --- /dev/null +++ b/source/java/org/alfresco/rest/workflow/api/model/Activity.java @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2005-2012 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 . + */ +package org.alfresco.rest.workflow.api.model; + +import java.util.Date; + +import org.activiti.engine.history.HistoricActivityInstance; + +/** + * Representation of an activity in the Activiti engine. + * + * @author Tijs Rademakers + */ +public class Activity +{ + String id; + String activityDefinitionId; + String activityDefinitionName; + String activityDefinitionType; + Date startedAt; + Date endedAt; + Long durationInMs; + + public Activity(HistoricActivityInstance activity) { + this.id = activity.getId(); + this.activityDefinitionId = activity.getActivityId(); + this.activityDefinitionName = activity.getActivityName(); + this.activityDefinitionType = activity.getActivityType(); + this.startedAt = activity.getStartTime(); + this.endedAt = activity.getEndTime(); + this.durationInMs = activity.getDurationInMillis(); + } + + public String getId() + { + return id; + } + + public void setId(String id) + { + this.id = id; + } + + public String getActivityDefinitionId() + { + return activityDefinitionId; + } + + public void setActivityDefinitionId(String activityDefinitionId) + { + this.activityDefinitionId = activityDefinitionId; + } + + public String getActivityDefinitionName() + { + return activityDefinitionName; + } + + public void setActivityDefinitionName(String activityDefinitionName) + { + this.activityDefinitionName = activityDefinitionName; + } + + public String getActivityDefinitionType() + { + return activityDefinitionType; + } + + public void setActivityDefinitionType(String activityDefinitionType) + { + this.activityDefinitionType = activityDefinitionType; + } + + public Date getStartedAt() + { + return startedAt; + } + + public void setStartedAt(Date startedAt) + { + this.startedAt = startedAt; + } + + public Date getEndedAt() + { + return endedAt; + } + + public void setEndedAt(Date endedAt) + { + this.endedAt = endedAt; + } + + public Long getDurationInMs() + { + return durationInMs; + } + + public void setDurationInMs(Long durationInMs) + { + this.durationInMs = durationInMs; + } +} diff --git a/source/java/org/alfresco/rest/workflow/api/model/FormModelElement.java b/source/java/org/alfresco/rest/workflow/api/model/FormModelElement.java index 4d6a2c8386..f285ed1b83 100644 --- a/source/java/org/alfresco/rest/workflow/api/model/FormModelElement.java +++ b/source/java/org/alfresco/rest/workflow/api/model/FormModelElement.java @@ -18,6 +18,8 @@ */ package org.alfresco.rest.workflow.api.model; +import java.util.List; + public class FormModelElement { String name; @@ -26,6 +28,7 @@ public class FormModelElement String dataType; boolean required; String defaultValue; + List allowedValues; public String getName() { @@ -75,4 +78,12 @@ public class FormModelElement { this.defaultValue = defaultValue; } + public List getAllowedValues() + { + return allowedValues; + } + public void setAllowedValues(List allowedValues) + { + this.allowedValues = allowedValues; + } } diff --git a/source/java/org/alfresco/rest/workflow/api/model/ProcessDefinition.java b/source/java/org/alfresco/rest/workflow/api/model/ProcessDefinition.java index 9db50ef2d3..0a46846017 100644 --- a/source/java/org/alfresco/rest/workflow/api/model/ProcessDefinition.java +++ b/source/java/org/alfresco/rest/workflow/api/model/ProcessDefinition.java @@ -27,7 +27,8 @@ public class ProcessDefinition String category; int version; String deploymentId; - + String title; + String description; String startFormResourceKey; Boolean isGraphicNotationDefined; @@ -38,7 +39,6 @@ public class ProcessDefinition public ProcessDefinition(org.activiti.engine.repository.ProcessDefinition processDefinition) { this.id = processDefinition.getId(); - this.key = processDefinition.getKey(); this.name = processDefinition.getName(); this.category = processDefinition.getCategory(); this.version = processDefinition.getVersion(); @@ -104,6 +104,26 @@ public class ProcessDefinition { this.deploymentId = deploymentId; } + + public String getTitle() + { + return title; + } + + public void setTitle(String title) + { + this.title = title; + } + + public String getDescription() + { + return description; + } + + public void setDescription(String description) + { + this.description = description; + } public String getStartFormResourceKey() { diff --git a/source/java/org/alfresco/rest/workflow/api/model/ProcessInfo.java b/source/java/org/alfresco/rest/workflow/api/model/ProcessInfo.java index a5502fb730..6efcb2ca89 100644 --- a/source/java/org/alfresco/rest/workflow/api/model/ProcessInfo.java +++ b/source/java/org/alfresco/rest/workflow/api/model/ProcessInfo.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -32,10 +32,14 @@ public class ProcessInfo String processDefinitionKey; Date startedAt; Date endedAt; - Long durationInMillis; + Long durationInMs; String deleteReason; + String startUserId; + String startActivityId; + String endActivityId; String businessKey; String superProcessInstanceId; + boolean completed; Map variables; Set items; @@ -50,9 +54,14 @@ public class ProcessInfo this.processDefinitionId = processInstance.getProcessDefinitionId(); this.startedAt = processInstance.getStartTime(); this.endedAt = processInstance.getEndTime(); - this.durationInMillis = processInstance.getDurationInMillis(); + this.durationInMs = processInstance.getDurationInMillis(); this.deleteReason = processInstance.getDeleteReason(); + this.startUserId = processInstance.getStartUserId(); + this.startActivityId = processInstance.getStartActivityId(); + this.endActivityId = processInstance.getEndActivityId(); this.businessKey = processInstance.getBusinessKey(); + this.superProcessInstanceId = processInstance.getSuperProcessInstanceId(); + this.completed = (processInstance.getEndTime() != null); } public String getId() @@ -75,14 +84,14 @@ public class ProcessInfo this.processDefinitionId = processDefinitionId; } - public Long getDurationInMillis() + public Long getDurationInMs() { - return durationInMillis; + return durationInMs; } - public void setDurationInMillis(Long durationInMillis) + public void setDurationInMs(Long durationInMs) { - this.durationInMillis = durationInMillis; + this.durationInMs = durationInMs; } public String getDeleteReason() @@ -145,6 +154,46 @@ public class ProcessInfo this.endedAt = endedAt; } + public String getStartUserId() + { + return startUserId; + } + + public void setStartUserId(String startUserId) + { + this.startUserId = startUserId; + } + + public String getStartActivityId() + { + return startActivityId; + } + + public void setStartActivityId(String startActivityId) + { + this.startActivityId = startActivityId; + } + + public String getEndActivityId() + { + return endActivityId; + } + + public void setEndActivityId(String endActivityId) + { + this.endActivityId = endActivityId; + } + + public boolean isCompleted() + { + return completed; + } + + public void setCompleted(boolean completed) + { + this.completed = completed; + } + public Map getVariables() { return variables; diff --git a/source/java/org/alfresco/rest/workflow/api/model/Task.java b/source/java/org/alfresco/rest/workflow/api/model/Task.java index 1e39f28b93..571e7b72b0 100644 --- a/source/java/org/alfresco/rest/workflow/api/model/Task.java +++ b/source/java/org/alfresco/rest/workflow/api/model/Task.java @@ -21,6 +21,7 @@ package org.alfresco.rest.workflow.api.model; import java.util.Date; import org.activiti.engine.history.HistoricTaskInstance; +import org.activiti.engine.task.DelegationState; public class Task { @@ -60,6 +61,18 @@ public class Task this.owner = taskInstance.getOwner(); this.assignee = taskInstance.getAssignee(); this.formResourceKey = taskInstance.getFormKey(); + if (taskInstance.getEndTime() != null) + { + this.state = TaskStateTransition.COMPLETED.name().toLowerCase(); + } + else if (taskInstance.getAssignee() != null) + { + this.state = TaskStateTransition.CLAIMED.name().toLowerCase(); + } + else + { + this.state = TaskStateTransition.UNCLAIMED.name().toLowerCase(); + } } public Task(org.activiti.engine.task.Task taskInstance) @@ -75,6 +88,18 @@ public class Task this.priority = taskInstance.getPriority(); this.owner = taskInstance.getOwner(); this.assignee = taskInstance.getAssignee(); + if (taskInstance.getDelegationState() == DelegationState.PENDING) + { + + } + if (taskInstance.getAssignee() != null) + { + this.state = TaskStateTransition.CLAIMED.name().toLowerCase(); + } + else + { + this.state = TaskStateTransition.UNCLAIMED.name().toLowerCase(); + } } public String getId() diff --git a/source/java/org/alfresco/rest/workflow/api/model/TaskStateTransition.java b/source/java/org/alfresco/rest/workflow/api/model/TaskStateTransition.java new file mode 100644 index 0000000000..cb7b57a3a0 --- /dev/null +++ b/source/java/org/alfresco/rest/workflow/api/model/TaskStateTransition.java @@ -0,0 +1,24 @@ +package org.alfresco.rest.workflow.api.model; + +import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException; + +public enum TaskStateTransition { + + COMPLETED, CLAIMED, UNCLAIMED, DELEGATED, RESOLVED; + + /** + * @return the {@link TaskStateTransition} for the given string + * @throws InvalidArgumentException when no action exists for the given string + */ + public static TaskStateTransition getTaskActionFromString(String action) + { + for(TaskStateTransition taskAction : values()) + { + if(taskAction.name().toLowerCase().equals(action)) + { + return taskAction; + } + } + throw new InvalidArgumentException("The task state property has an invalid value: " + action); + } +} diff --git a/source/java/org/alfresco/rest/workflow/api/package-info.java b/source/java/org/alfresco/rest/workflow/api/package-info.java index 89066ee3e4..0127e16e21 100644 --- a/source/java/org/alfresco/rest/workflow/api/package-info.java +++ b/source/java/org/alfresco/rest/workflow/api/package-info.java @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Alfresco. If not, see . */ -@WebApi(name="alfresco", scope=Api.SCOPE.PUBLIC, version=1) +@WebApi(name="workflow", scope=Api.SCOPE.PUBLIC, version=1) package org.alfresco.rest.workflow.api; import org.alfresco.rest.framework.Api; import org.alfresco.rest.framework.WebApi; \ No newline at end of file diff --git a/source/java/org/alfresco/rest/workflow/api/processdefinitions/ProcessDefinitionsRestEntityResource.java b/source/java/org/alfresco/rest/workflow/api/processdefinitions/ProcessDefinitionsRestEntityResource.java index 9d694a1444..84ae7ace56 100644 --- a/source/java/org/alfresco/rest/workflow/api/processdefinitions/ProcessDefinitionsRestEntityResource.java +++ b/source/java/org/alfresco/rest/workflow/api/processdefinitions/ProcessDefinitionsRestEntityResource.java @@ -18,13 +18,16 @@ */ package org.alfresco.rest.workflow.api.processdefinitions; +import org.alfresco.rest.framework.BinaryProperties; import org.alfresco.rest.framework.WebApiDescription; import org.alfresco.rest.framework.WebApiParam; import org.alfresco.rest.framework.WebApiParameters; import org.alfresco.rest.framework.core.ResourceParameter; import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException; import org.alfresco.rest.framework.resource.EntityResource; +import org.alfresco.rest.framework.resource.actions.interfaces.BinaryResourceAction; import org.alfresco.rest.framework.resource.actions.interfaces.EntityResourceAction; +import org.alfresco.rest.framework.resource.content.BinaryResource; import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo; import org.alfresco.rest.framework.resource.parameters.Parameters; import org.alfresco.rest.workflow.api.ProcessDefinitions; @@ -32,7 +35,8 @@ import org.alfresco.rest.workflow.api.model.ProcessDefinition; @EntityResource(name="process-definitions", title = "Process definitions") public class ProcessDefinitionsRestEntityResource implements EntityResourceAction.Read, - EntityResourceAction.ReadById{ + EntityResourceAction.ReadById, + BinaryResourceAction.Read { ProcessDefinitions processDefinitions; @@ -59,4 +63,12 @@ public class ProcessDefinitionsRestEntityResource implements EntityResourceActio { return processDefinitions.getProcessDefinition(id); } + + @Override + @WebApiDescription(title = "Get a process definition image", description = "Get a process definition image") + @BinaryProperties({"image"}) + public BinaryResource readProperty(String id, Parameters parameters) throws EntityNotFoundException + { + return processDefinitions.getProcessDefinitionImage(id); + } } diff --git a/source/java/org/alfresco/rest/workflow/api/processdefinitions/package-info.java b/source/java/org/alfresco/rest/workflow/api/processdefinitions/package-info.java index e7e3c9bc5a..4e6b2054ae 100644 --- a/source/java/org/alfresco/rest/workflow/api/processdefinitions/package-info.java +++ b/source/java/org/alfresco/rest/workflow/api/processdefinitions/package-info.java @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Alfresco. If not, see . */ -@WebApi(name="alfresco", scope=Api.SCOPE.PUBLIC, version=1) +@WebApi(name="workflow", scope=Api.SCOPE.PUBLIC, version=1) package org.alfresco.rest.workflow.api.processdefinitions; import org.alfresco.rest.framework.Api; import org.alfresco.rest.framework.WebApi; \ No newline at end of file diff --git a/source/java/org/alfresco/rest/workflow/api/processes/ProcessActivitiesRelation.java b/source/java/org/alfresco/rest/workflow/api/processes/ProcessActivitiesRelation.java new file mode 100644 index 0000000000..dd2a847ea6 --- /dev/null +++ b/source/java/org/alfresco/rest/workflow/api/processes/ProcessActivitiesRelation.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2005-2012 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 . + */ +package org.alfresco.rest.workflow.api.processes; + +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.Parameters; +import org.alfresco.rest.workflow.api.Activities; +import org.alfresco.rest.workflow.api.model.Activity; + +/** + * + * @author Tijs Rademakers + * + */ +@RelationshipResource(name = "activities", entityResource = ProcessesRestEntityResource.class, title = "Activities for the current process") +public class ProcessActivitiesRelation implements RelationshipResourceAction.Read +{ + protected Activities activities; + + public void setActivities(Activities activities) + { + this.activities = activities; + } + + /** + * List the activities. + */ + @Override + @WebApiDescription(title = "Get Activities", description = "Get a paged list of the activities") + public CollectionWithPagingInfo readAll(String processId, Parameters parameters) + { + return activities.getActivities(processId, parameters); + } +} diff --git a/source/java/org/alfresco/rest/workflow/api/processes/ProcessTasksRelation.java b/source/java/org/alfresco/rest/workflow/api/processes/ProcessTasksRelation.java new file mode 100644 index 0000000000..b093758bcf --- /dev/null +++ b/source/java/org/alfresco/rest/workflow/api/processes/ProcessTasksRelation.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2005-2012 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 . + */ +package org.alfresco.rest.workflow.api.processes; + +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.Parameters; +import org.alfresco.rest.workflow.api.Tasks; +import org.alfresco.rest.workflow.api.model.Task; + +/** + * + * @author Tijs Rademakers + * + */ +@RelationshipResource(name = "tasks", entityResource = ProcessesRestEntityResource.class, title = "Tasks for the current process") +public class ProcessTasksRelation implements RelationshipResourceAction.Read +{ + protected Tasks tasks; + + public void setTasks(Tasks tasks) + { + this.tasks = tasks; + } + + /** + * List the tasks. + */ + @Override + @WebApiDescription(title = "Get Tasks", description = "Get a paged list of the tasks") + public CollectionWithPagingInfo readAll(String processId, Parameters parameters) + { + return tasks.getTasks(processId, parameters); + } +} diff --git a/source/java/org/alfresco/rest/workflow/api/processes/ProcessVariablesRelation.java b/source/java/org/alfresco/rest/workflow/api/processes/ProcessVariablesRelation.java index 38ca6cff7b..a752c127af 100644 --- a/source/java/org/alfresco/rest/workflow/api/processes/ProcessVariablesRelation.java +++ b/source/java/org/alfresco/rest/workflow/api/processes/ProcessVariablesRelation.java @@ -18,6 +18,8 @@ */ package org.alfresco.rest.workflow.api.processes; +import java.util.List; + import org.alfresco.rest.framework.WebApiDescription; import org.alfresco.rest.framework.resource.RelationshipResource; import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceAction; @@ -32,7 +34,7 @@ import org.alfresco.rest.workflow.api.model.Variable; * */ @RelationshipResource(name = "variables", entityResource = ProcessesRestEntityResource.class, title = "Variables for the current process") -public class ProcessVariablesRelation implements RelationshipResourceAction.Read, +public class ProcessVariablesRelation implements RelationshipResourceAction.Read, RelationshipResourceAction.Create, RelationshipResourceAction.Update, RelationshipResourceAction.Delete { protected Processes processes; @@ -51,6 +53,16 @@ public class ProcessVariablesRelation implements RelationshipResourceAction.Read { return processes.getVariables(processId, parameters.getPaging()); } + + /** + * Creates or updates multiple variables. If the variable name doesn't exist yet it will be created + */ + @Override + @WebApiDescription(title = "Create or Update Variables", description = "Create or update multiple variable") + public List create(String processId, List variables, Parameters parameters) + { + return processes.updateVariables(processId, variables); + } /** * Update a variable. If the variable name doesn't exist yet it will be created diff --git a/source/java/org/alfresco/rest/workflow/api/processes/ProcessesRestEntityResource.java b/source/java/org/alfresco/rest/workflow/api/processes/ProcessesRestEntityResource.java index c2d7020433..3babbc1552 100644 --- a/source/java/org/alfresco/rest/workflow/api/processes/ProcessesRestEntityResource.java +++ b/source/java/org/alfresco/rest/workflow/api/processes/ProcessesRestEntityResource.java @@ -27,7 +27,6 @@ import org.alfresco.rest.framework.WebApiParam; import org.alfresco.rest.framework.WebApiParameters; import org.alfresco.rest.framework.core.ResourceParameter; import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException; -import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException; import org.alfresco.rest.framework.resource.EntityResource; import org.alfresco.rest.framework.resource.actions.interfaces.BinaryResourceAction; import org.alfresco.rest.framework.resource.actions.interfaces.EntityResourceAction; @@ -79,16 +78,11 @@ public class ProcessesRestEntityResource implements EntityResourceAction.Read. */ -@WebApi(name="alfresco", scope=Api.SCOPE.PUBLIC, version=1) +@WebApi(name="workflow", scope=Api.SCOPE.PUBLIC, version=1) package org.alfresco.rest.workflow.api.processes; import org.alfresco.rest.framework.Api; import org.alfresco.rest.framework.WebApi; \ No newline at end of file diff --git a/source/java/org/alfresco/rest/workflow/api/tasks/TaskVariablesRelation.java b/source/java/org/alfresco/rest/workflow/api/tasks/TaskVariablesRelation.java index 00d7ca93e2..9a95783711 100644 --- a/source/java/org/alfresco/rest/workflow/api/tasks/TaskVariablesRelation.java +++ b/source/java/org/alfresco/rest/workflow/api/tasks/TaskVariablesRelation.java @@ -18,6 +18,8 @@ */ package org.alfresco.rest.workflow.api.tasks; +import java.util.List; + import org.alfresco.rest.framework.WebApiDescription; import org.alfresco.rest.framework.resource.RelationshipResource; import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceAction; @@ -35,7 +37,7 @@ import org.alfresco.rest.workflow.api.model.VariableScope; * */ @RelationshipResource(name = "variables", entityResource = TasksRestEntityResource.class, title = "Variables for the current task") -public class TaskVariablesRelation implements RelationshipResourceAction.Read, +public class TaskVariablesRelation implements RelationshipResourceAction.Read, RelationshipResourceAction.Create, RelationshipResourceAction.Update, RelationshipResourceAction.Delete { private Tasks tasks; @@ -64,6 +66,16 @@ public class TaskVariablesRelation implements RelationshipResourceAction.Read create(String taskId, List variables, Parameters parameters) + { + return tasks.updateTaskVariables(taskId, variables); + } /** * Update a task variable. If the variable name doesn't exist yet it will be created diff --git a/source/java/org/alfresco/rest/workflow/api/tasks/package-info.java b/source/java/org/alfresco/rest/workflow/api/tasks/package-info.java index 21e76c7e87..867383a3be 100644 --- a/source/java/org/alfresco/rest/workflow/api/tasks/package-info.java +++ b/source/java/org/alfresco/rest/workflow/api/tasks/package-info.java @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Alfresco. If not, see . */ -@WebApi(name="alfresco", scope=Api.SCOPE.PUBLIC, version=1) +@WebApi(name="workflow", scope=Api.SCOPE.PUBLIC, version=1) package org.alfresco.rest.workflow.api.tasks; import org.alfresco.rest.framework.Api; import org.alfresco.rest.framework.WebApi; \ No newline at end of file diff --git a/source/test-java/org/alfresco/rest/api/tests/client/PublicApiHttpClient.java b/source/test-java/org/alfresco/rest/api/tests/client/PublicApiHttpClient.java index 545c87bf56..a98a1d9673 100644 --- a/source/test-java/org/alfresco/rest/api/tests/client/PublicApiHttpClient.java +++ b/source/test-java/org/alfresco/rest/api/tests/client/PublicApiHttpClient.java @@ -71,7 +71,7 @@ public class PublicApiHttpClient private static final String OLD_BASE_URL = "http://{0}:{1}{2}/{3}/{4}/api/"; private static final String INDEX_URL = "http://{0}:{1}{2}/{3}"; - private static final String BASE_URL = "http://{0}:{1}{2}/{3}/{4}/{5}/alfresco/versions/1"; + private static final String BASE_URL = "http://{0}:{1}{2}/{3}/{4}/{5}/{6}/versions/1"; private static final String PUBLICAPI_CMIS_SERVICE_URL = "http://{0}:{1}{2}/{3}/cmis/versions/{4}/{5}"; private static final String PUBLICAPI_CMIS_URL = "http://{0}:{1}{2}/{3}/{4}/{5}/cmis/versions/{6}/{7}"; private static final String ATOM_PUB_URL = "http://{0}:{1}{2}/cmisatom"; @@ -86,6 +86,9 @@ public class PublicApiHttpClient private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver(); private MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(this.resourcePatternResolver); + // can be overriden by other clients like the workflow client + protected String apiName = "alfresco"; + public PublicApiHttpClient(String host, int port, String contextPath, String servletName, AuthenticatedHttp authenticatedHttp) { super(); @@ -500,7 +503,7 @@ public class PublicApiHttpClient Pair relationshipCollectionInfo = getRelationCollectionInfo(resourceClass); sb.append(MessageFormat.format(BASE_URL, new Object[] {host, String.valueOf(port), contextPath, servletName, - tenantDomain == null ? TenantUtil.DEFAULT_TENANT : tenantDomain, scope.toString()})); + tenantDomain == null ? TenantUtil.DEFAULT_TENANT : tenantDomain, scope.toString(), apiName})); if(relationshipCollectionInfo != null) { @@ -578,7 +581,7 @@ public class PublicApiHttpClient tenantDomain = TenantUtil.DEFAULT_TENANT; } sb.append(MessageFormat.format(BASE_URL, new Object[] {host, String.valueOf(port), contextPath, servletName, - tenantDomain, scope})); + tenantDomain, scope, apiName})); if(collectionName != null) { diff --git a/source/test-java/org/alfresco/rest/workflow/api/tests/EnterpriseWorkflowTestApi.java b/source/test-java/org/alfresco/rest/workflow/api/tests/EnterpriseWorkflowTestApi.java index 2c291d226a..d2686c033d 100644 --- a/source/test-java/org/alfresco/rest/workflow/api/tests/EnterpriseWorkflowTestApi.java +++ b/source/test-java/org/alfresco/rest/workflow/api/tests/EnterpriseWorkflowTestApi.java @@ -2,9 +2,12 @@ package org.alfresco.rest.workflow.api.tests; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import java.io.InputStream; import java.util.Date; +import java.util.Iterator; +import java.util.List; import org.activiti.engine.ProcessEngine; import org.activiti.engine.repository.DeploymentBuilder; @@ -23,11 +26,11 @@ import org.alfresco.rest.api.tests.client.AuthenticationDetailsProvider; import org.alfresco.rest.api.tests.client.HttpClientProvider; import org.alfresco.rest.api.tests.client.HttpResponse; import org.alfresco.rest.api.tests.client.PublicApiException; -import org.alfresco.rest.api.tests.client.PublicApiHttpClient; import org.alfresco.rest.api.tests.client.RequestContext; import org.alfresco.rest.api.tests.client.UserAuthenticationDetailsProviderImpl; import org.alfresco.rest.api.tests.client.UserData; import org.alfresco.rest.api.tests.client.UserDataService; +import org.alfresco.rest.api.tests.client.data.MemberOfSite; import org.alfresco.rest.workflow.api.model.ProcessInfo; import org.alfresco.rest.workflow.api.tests.WorkflowApiClient.ProcessesClient; import org.alfresco.service.ServiceRegistry; @@ -85,7 +88,7 @@ public class EnterpriseWorkflowTestApi extends EnterpriseTestApi }; AuthenticationDetailsProvider authenticationDetailsProvider = new UserAuthenticationDetailsProviderImpl(userDataService, "admin", "admin"); AuthenticatedHttp authenticatedHttp = new AuthenticatedHttp(httpClientProvider, authenticationDetailsProvider); - this.httpClient = new PublicApiHttpClient("localhost", TestFixture.PORT, TestFixture.CONTEXT_PATH, + this.httpClient = new WorkflowApiHttpClient("localhost", TestFixture.PORT, TestFixture.CONTEXT_PATH, TestFixture.PUBLIC_API_SERVLET_NAME, authenticatedHttp); this.publicApiClient = new WorkflowApiClient(httpClient, userDataService); activitiProcessEngine = (ProcessEngine) applicationContext.getBean("activitiProcessEngine"); @@ -116,6 +119,29 @@ public class EnterpriseWorkflowTestApi extends EnterpriseTestApi return requestContext; } + protected TestNetwork getOtherNetwork(String usedNetworkId) throws Exception { + Iterator networkIt = getTestFixture().getNetworksIt(); + while(networkIt.hasNext()) { + TestNetwork network = networkIt.next(); + if(!usedNetworkId.equals(network.getId())) { + return network; + } + } + fail("Need more than one network to test permissions"); + return null; + } + + protected TestPerson getOtherPersonInNetwork(String usedPerson, String networkId) throws Exception { + TestNetwork usedNetwork = getTestFixture().getNetwork(networkId); + for(TestPerson person : usedNetwork.getPeople()) { + if(!person.getId().equals(usedPerson)) { + return person; + } + } + fail("Network doesn't have additonal users, cannot perform test"); + return null; + } + protected Date parseDate(JSONObject entry, String fieldName) { String dateText = (String) entry.get(fieldName); if (dateText!=null) { @@ -188,10 +214,17 @@ public class EnterpriseWorkflowTestApi extends EnterpriseTestApi } /** - * Start an adhoc-process through the public REST-API. + * Start an adhoc-process without business key through the public REST-API. + */ + protected ProcessInfo startAdhocProcess(final RequestContext requestContext, NodeRef[] documentRefs) throws PublicApiException { + return startAdhocProcess(requestContext, documentRefs, null); + } + + /** + * Start an adhoc-process with possible business key through the public REST-API. */ @SuppressWarnings("unchecked") - protected ProcessInfo startAdhocProcess(final RequestContext requestContext, NodeRef[] documentRefs) throws PublicApiException { + protected ProcessInfo startAdhocProcess(final RequestContext requestContext, NodeRef[] documentRefs, String businessKey) throws PublicApiException { org.activiti.engine.repository.ProcessDefinition processDefinition = activitiProcessEngine .getRepositoryService() .createProcessDefinitionQuery() @@ -202,8 +235,13 @@ public class EnterpriseWorkflowTestApi extends EnterpriseTestApi final JSONObject createProcessObject = new JSONObject(); createProcessObject.put("processDefinitionId", processDefinition.getId()); + if (businessKey != null) + { + createProcessObject.put("businessKey", businessKey); + } final JSONObject variablesObject = new JSONObject(); variablesObject.put("bpm_priority", 1); + variablesObject.put("wf_notifyMe", Boolean.FALSE); TenantUtil.runAsUserTenant(new TenantRunAsWork() { @@ -220,7 +258,7 @@ public class EnterpriseWorkflowTestApi extends EnterpriseTestApi final JSONArray itemsObject = new JSONArray(); for (NodeRef nodeRef : documentRefs) { - itemsObject.add(nodeRef.toString()); + itemsObject.add(nodeRef.getId()); } createProcessObject.put("items", itemsObject); } @@ -229,4 +267,43 @@ public class EnterpriseWorkflowTestApi extends EnterpriseTestApi return processesClient.createProcess(createProcessObject.toJSONString()); } + + /** + * Start a review pooled process through the public REST-API. + */ + @SuppressWarnings("unchecked") + protected ProcessInfo startReviewPooledProcess(final RequestContext requestContext) throws PublicApiException { + org.activiti.engine.repository.ProcessDefinition processDefinition = activitiProcessEngine + .getRepositoryService() + .createProcessDefinitionQuery() + .processDefinitionKey("@" + requestContext.getNetworkId() + "@activitiReviewPooled") + .singleResult(); + + ProcessesClient processesClient = publicApiClient.processesClient(); + + final JSONObject createProcessObject = new JSONObject(); + createProcessObject.put("processDefinitionId", processDefinition.getId()); + + final JSONObject variablesObject = new JSONObject(); + variablesObject.put("bpm_priority", 1); + variablesObject.put("wf_notifyMe", Boolean.FALSE); + + TenantUtil.runAsUserTenant(new TenantRunAsWork() + { + @Override + public Void doWork() throws Exception + { + List memberships = getTestFixture().getNetwork(requestContext.getNetworkId()).getSiteMemberships(requestContext.getRunAsUser()); + assertTrue(memberships.size() > 0); + MemberOfSite memberOfSite = memberships.get(0); + String group = "GROUP_site_" + memberOfSite.getSiteId() + "_" + memberOfSite.getRole().name(); + variablesObject.put("bpm_groupAssignee", group); + return null; + } + }, requestContext.getRunAsUser(), requestContext.getNetworkId()); + + createProcessObject.put("variables", variablesObject); + + return processesClient.createProcess(createProcessObject.toJSONString()); + } } diff --git a/source/test-java/org/alfresco/rest/workflow/api/tests/ProcessDefinitionParser.java b/source/test-java/org/alfresco/rest/workflow/api/tests/ProcessDefinitionParser.java index a925cceea0..faa17ffc90 100644 --- a/source/test-java/org/alfresco/rest/workflow/api/tests/ProcessDefinitionParser.java +++ b/source/test-java/org/alfresco/rest/workflow/api/tests/ProcessDefinitionParser.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -34,6 +34,8 @@ public class ProcessDefinitionParser extends ListParser processDefinition.setVersion(((Number) entry.get("version")).intValue()); processDefinition.setName((String) entry.get("name")); processDefinition.setDeploymentId((String) entry.get("deploymentId")); + processDefinition.setTitle((String) entry.get("title")); + processDefinition.setDescription((String) entry.get("description")); processDefinition.setCategory((String) entry.get("category")); processDefinition.setStartFormResourceKey((String) entry.get("startFormResourceKey")); processDefinition.setGraphicNotationDefined((Boolean) entry.get("graphicNotationDefined")); diff --git a/source/test-java/org/alfresco/rest/workflow/api/tests/ProcessDefinitionWorkflowApiTest.java b/source/test-java/org/alfresco/rest/workflow/api/tests/ProcessDefinitionWorkflowApiTest.java index 5d00219f1e..79c7dadeed 100644 --- a/source/test-java/org/alfresco/rest/workflow/api/tests/ProcessDefinitionWorkflowApiTest.java +++ b/source/test-java/org/alfresco/rest/workflow/api/tests/ProcessDefinitionWorkflowApiTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -29,6 +29,9 @@ import java.util.List; import java.util.Map; import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.rest.api.tests.RepoService.TestNetwork; +import org.alfresco.rest.api.tests.client.HttpResponse; import org.alfresco.rest.api.tests.client.PublicApiClient.ListResponse; import org.alfresco.rest.api.tests.client.PublicApiException; import org.alfresco.rest.api.tests.client.RequestContext; @@ -40,8 +43,7 @@ import org.junit.Test; import org.springframework.http.HttpStatus; /** - * - * + * @author Tijs Rademakers * @author Frederik Heremans */ public class ProcessDefinitionWorkflowApiTest extends EnterpriseWorkflowTestApi @@ -57,11 +59,11 @@ public class ProcessDefinitionWorkflowApiTest extends EnterpriseWorkflowTestApi ListResponse processDefinitionsResponse = processDefinitionsClient.getProcessDefinitions(null); Map processDefinitionMap = getProcessDefinitionMapByKey(processDefinitionsResponse.getList()); - assertTrue(processDefinitionMap.containsKey(createProcessDefinitionKey("activitiReviewPooled", requestContext))); - assertTrue(processDefinitionMap.containsKey(createProcessDefinitionKey("activitiReview", requestContext))); - assertTrue(processDefinitionMap.containsKey(createProcessDefinitionKey("activitiParallelGroupReview", requestContext))); - assertTrue(processDefinitionMap.containsKey(createProcessDefinitionKey("activitiParallelReview", requestContext))); - assertTrue(processDefinitionMap.containsKey(createProcessDefinitionKey("activitiAdhoc", requestContext))); + assertTrue(processDefinitionMap.containsKey("activitiReviewPooled")); + assertTrue(processDefinitionMap.containsKey("activitiReview")); + assertTrue(processDefinitionMap.containsKey("activitiParallelGroupReview")); + assertTrue(processDefinitionMap.containsKey("activitiParallelReview")); + assertTrue(processDefinitionMap.containsKey("activitiAdhoc")); assertEquals(5, processDefinitionMap.size()); @@ -74,16 +76,18 @@ public class ProcessDefinitionWorkflowApiTest extends EnterpriseWorkflowTestApi assertNotNull(activitiDefinition); - ProcessDefinition adhocDefinition = processDefinitionMap.get(adhocKey); + ProcessDefinition adhocDefinitionRest = processDefinitionMap.get("activitiAdhoc"); - assertEquals(activitiDefinition.getId(), adhocDefinition.getId()); - assertEquals(activitiDefinition.getKey(), adhocDefinition.getKey()); - assertEquals(activitiDefinition.getDeploymentId(), adhocDefinition.getDeploymentId()); - assertEquals(activitiDefinition.getCategory(), adhocDefinition.getCategory()); - assertEquals(activitiDefinition.getName(), adhocDefinition.getName()); - assertEquals(activitiDefinition.getVersion(), adhocDefinition.getVersion()); - assertEquals(((ProcessDefinitionEntity) activitiDefinition).isGraphicalNotationDefined(), adhocDefinition.isGraphicNotationDefined()); - assertEquals("wf:submitAdhocTask", adhocDefinition.getStartFormResourceKey()); + assertEquals(activitiDefinition.getId(), adhocDefinitionRest.getId()); + assertEquals("activitiAdhoc", adhocDefinitionRest.getKey()); + assertEquals(activitiDefinition.getDeploymentId(), adhocDefinitionRest.getDeploymentId()); + assertEquals(activitiDefinition.getCategory(), adhocDefinitionRest.getCategory()); + assertEquals(activitiDefinition.getName(), adhocDefinitionRest.getName()); + assertEquals(activitiDefinition.getVersion(), adhocDefinitionRest.getVersion()); + assertEquals(((ProcessDefinitionEntity) activitiDefinition).isGraphicalNotationDefined(), adhocDefinitionRest.isGraphicNotationDefined()); + assertEquals("wf:submitAdhocTask", adhocDefinitionRest.getStartFormResourceKey()); + assertEquals("New Task", adhocDefinitionRest.getTitle()); + assertEquals("Assign a new task to yourself or a colleague", adhocDefinitionRest.getDescription()); } @Test @@ -104,11 +108,11 @@ public class ProcessDefinitionWorkflowApiTest extends EnterpriseWorkflowTestApi // Filter on category equals Map processDefinitionMap = getProcessDefinitions(processDefinitionsClient, "(category = 'http://alfresco.org')"); - assertTrue(processDefinitionMap.containsKey(createProcessDefinitionKey("activitiReviewPooled", requestContext))); - assertTrue(processDefinitionMap.containsKey(createProcessDefinitionKey("activitiReview", requestContext))); - assertTrue(processDefinitionMap.containsKey(createProcessDefinitionKey("activitiParallelGroupReview", requestContext))); - assertTrue(processDefinitionMap.containsKey(createProcessDefinitionKey("activitiParallelReview", requestContext))); - assertTrue(processDefinitionMap.containsKey(createProcessDefinitionKey("activitiAdhoc", requestContext))); + assertTrue(processDefinitionMap.containsKey("activitiReviewPooled")); + assertTrue(processDefinitionMap.containsKey("activitiReview")); + assertTrue(processDefinitionMap.containsKey("activitiParallelGroupReview")); + assertTrue(processDefinitionMap.containsKey("activitiParallelReview")); + assertTrue(processDefinitionMap.containsKey("activitiAdhoc")); assertEquals(5, processDefinitionMap.size()); processDefinitionMap = getProcessDefinitions(processDefinitionsClient, "(category = 'unexisting')"); @@ -116,15 +120,15 @@ public class ProcessDefinitionWorkflowApiTest extends EnterpriseWorkflowTestApi // Filter on name equals processDefinitionMap = getProcessDefinitions(processDefinitionsClient, "(name = 'Adhoc Activiti Process')"); - assertTrue(processDefinitionMap.containsKey(createProcessDefinitionKey("activitiAdhoc", requestContext))); + assertTrue(processDefinitionMap.containsKey("activitiAdhoc")); assertEquals(1, processDefinitionMap.size()); processDefinitionMap = getProcessDefinitions(processDefinitionsClient, "(name = 'unexisting')"); assertEquals(0, processDefinitionMap.size()); // Filter on key equals - processDefinitionMap = getProcessDefinitions(processDefinitionsClient, "(key='" + adhocKey +"')"); - assertTrue(processDefinitionMap.containsKey(createProcessDefinitionKey("activitiAdhoc", requestContext))); + processDefinitionMap = getProcessDefinitions(processDefinitionsClient, "(key='activitiAdhoc')"); + assertTrue(processDefinitionMap.containsKey("activitiAdhoc")); assertEquals(1, processDefinitionMap.size()); processDefinitionMap = getProcessDefinitions(processDefinitionsClient, "(key='unexisting')"); @@ -139,7 +143,7 @@ public class ProcessDefinitionWorkflowApiTest extends EnterpriseWorkflowTestApi // Filter on deploymentId equals processDefinitionMap = getProcessDefinitions(processDefinitionsClient, "(deploymentId='" + activitiDefinition.getDeploymentId() + "')"); - assertTrue(processDefinitionMap.containsKey(createProcessDefinitionKey("activitiAdhoc", requestContext))); + assertTrue(processDefinitionMap.containsKey("activitiAdhoc")); assertEquals(1, processDefinitionMap.size()); processDefinitionMap = getProcessDefinitions(processDefinitionsClient, "(deploymentId='unexisting')"); @@ -148,11 +152,11 @@ public class ProcessDefinitionWorkflowApiTest extends EnterpriseWorkflowTestApi // Filter on category matches processDefinitionMap = getProcessDefinitions(processDefinitionsClient, "(category matches('%alfresco.o%'))"); - assertTrue(processDefinitionMap.containsKey(createProcessDefinitionKey("activitiReviewPooled", requestContext))); - assertTrue(processDefinitionMap.containsKey(createProcessDefinitionKey("activitiReview", requestContext))); - assertTrue(processDefinitionMap.containsKey(createProcessDefinitionKey("activitiParallelGroupReview", requestContext))); - assertTrue(processDefinitionMap.containsKey(createProcessDefinitionKey("activitiParallelReview", requestContext))); - assertTrue(processDefinitionMap.containsKey(createProcessDefinitionKey("activitiAdhoc", requestContext))); + assertTrue(processDefinitionMap.containsKey("activitiReviewPooled")); + assertTrue(processDefinitionMap.containsKey("activitiReview")); + assertTrue(processDefinitionMap.containsKey("activitiParallelGroupReview")); + assertTrue(processDefinitionMap.containsKey("activitiParallelReview")); + assertTrue(processDefinitionMap.containsKey("activitiAdhoc")); assertEquals(5, processDefinitionMap.size()); processDefinitionMap = getProcessDefinitions(processDefinitionsClient, "(category matches('unexisting'))"); @@ -160,15 +164,15 @@ public class ProcessDefinitionWorkflowApiTest extends EnterpriseWorkflowTestApi // Filter on name matches processDefinitionMap = getProcessDefinitions(processDefinitionsClient, "(name matches('Adhoc Activiti %'))"); - assertTrue(processDefinitionMap.containsKey(createProcessDefinitionKey("activitiAdhoc", requestContext))); + assertTrue(processDefinitionMap.containsKey("activitiAdhoc")); assertEquals(1, processDefinitionMap.size()); processDefinitionMap = getProcessDefinitions(processDefinitionsClient, "(name matches('unexisting'))"); assertEquals(0, processDefinitionMap.size()); // Filter on key matches - processDefinitionMap = getProcessDefinitions(processDefinitionsClient, "(key matches('" + adhocKey.substring(0, adhocKey.length() - 3) +"%'))"); - assertTrue(processDefinitionMap.containsKey(createProcessDefinitionKey("activitiAdhoc", requestContext))); + processDefinitionMap = getProcessDefinitions(processDefinitionsClient, "(key matches('activitiAd%'))"); + assertTrue(processDefinitionMap.containsKey("activitiAdhoc")); assertEquals(1, processDefinitionMap.size()); } @@ -193,7 +197,7 @@ public class ProcessDefinitionWorkflowApiTest extends EnterpriseWorkflowTestApi // Check fields of a resulting process-definition assertEquals(activitiDefinition.getId(), adhocDefinition.getId()); - assertEquals(activitiDefinition.getKey(), adhocDefinition.getKey()); + assertEquals("activitiAdhoc", adhocDefinition.getKey()); assertEquals(activitiDefinition.getDeploymentId(), adhocDefinition.getDeploymentId()); assertEquals(activitiDefinition.getCategory(), adhocDefinition.getCategory()); assertEquals(activitiDefinition.getName(), adhocDefinition.getName()); @@ -254,10 +258,10 @@ public class ProcessDefinitionWorkflowApiTest extends EnterpriseWorkflowTestApi // Check well-known properties and their types // Validate bpm:description - JSONObject modelEntry = modelFieldsByName.get("bpm_description"); + JSONObject modelEntry = modelFieldsByName.get("bpm_workflowDescription"); assertNotNull(modelEntry); assertEquals("Description", modelEntry.get("title")); - assertEquals("{http://www.alfresco.org/model/bpm/1.0}description", modelEntry.get("qualifiedName")); + assertEquals("{http://www.alfresco.org/model/bpm/1.0}workflowDescription", modelEntry.get("qualifiedName")); assertEquals("d:text", modelEntry.get("dataType")); assertFalse((Boolean)modelEntry.get("required")); @@ -286,13 +290,13 @@ public class ProcessDefinitionWorkflowApiTest extends EnterpriseWorkflowTestApi assertFalse((Boolean)modelEntry.get("required")); // Validate bpm:priority - modelEntry = modelFieldsByName.get("bpm_priority"); + modelEntry = modelFieldsByName.get("bpm_workflowPriority"); assertNotNull(modelEntry); - assertEquals("Priority", modelEntry.get("title")); - assertEquals("{http://www.alfresco.org/model/bpm/1.0}priority", modelEntry.get("qualifiedName")); + assertEquals("Workflow Priority", modelEntry.get("title")); + assertEquals("{http://www.alfresco.org/model/bpm/1.0}workflowPriority", modelEntry.get("qualifiedName")); assertEquals("d:int", modelEntry.get("dataType")); assertEquals("2", modelEntry.get("defaultValue")); - assertTrue((Boolean)modelEntry.get("required")); + assertFalse((Boolean)modelEntry.get("required")); // Validate bpm:package modelEntry = modelFieldsByName.get("bpm_package"); @@ -301,6 +305,196 @@ public class ProcessDefinitionWorkflowApiTest extends EnterpriseWorkflowTestApi assertEquals("{http://www.alfresco.org/model/bpm/1.0}package", modelEntry.get("qualifiedName")); assertEquals("bpm:workflowPackage", modelEntry.get("dataType")); assertFalse((Boolean)modelEntry.get("required")); + + // Validate bpm:status + modelEntry = modelFieldsByName.get("bpm_status"); + assertNotNull(modelEntry); + assertEquals("Status", modelEntry.get("title")); + assertEquals("{http://www.alfresco.org/model/bpm/1.0}status", modelEntry.get("qualifiedName")); + assertEquals("d:text", modelEntry.get("dataType")); + assertEquals("Not Yet Started", modelEntry.get("defaultValue")); + assertTrue((Boolean)modelEntry.get("required")); + JSONArray allowedValues = (JSONArray) modelEntry.get("allowedValues"); + assertNotNull(allowedValues); + assertEquals(5, allowedValues.size()); + assertTrue(allowedValues.contains("Not Yet Started")); + assertTrue(allowedValues.contains("In Progress")); + assertTrue(allowedValues.contains("On Hold")); + assertTrue(allowedValues.contains("Cancelled")); + assertTrue(allowedValues.contains("Completed")); + } + + @Test + public void testGetProcessDefinitionStartModelUnexisting() throws Exception + { + initApiClientWithTestUser(); + ProcessDefinitionsClient processDefinitionsClient = publicApiClient.processDefinitionsClient(); + try + { + processDefinitionsClient.findStartFormModel("unexisting"); + fail("Exception expected"); + } + catch(PublicApiException expected) + { + assertEquals(HttpStatus.NOT_FOUND.value(), expected.getHttpResponse().getStatusCode()); + assertErrorSummary("The entity with id: unexisting was not found", expected.getHttpResponse()); + } + } + + @Test + public void testMethodNotAllowedURIs() throws Exception + { + RequestContext requestContext = initApiClientWithTestUser(); + HttpResponse response = publicApiClient.get("public", "process-definitions", null, null, null, null); + assertEquals(200, response.getStatusCode()); + response = publicApiClient.post("public", "process-definitions", null, null, null, null); + assertEquals(405, response.getStatusCode()); + response = publicApiClient.delete("public", "process-definitions", null, null, null); + assertEquals(405, response.getStatusCode()); + response = publicApiClient.put("public", "process-definitions", null, null, null, null, null); + assertEquals(405, response.getStatusCode()); + + String adhocKey = createProcessDefinitionKey("activitiAdhoc", requestContext); + org.activiti.engine.repository.ProcessDefinition processDefinition = activitiProcessEngine.getRepositoryService() + .createProcessDefinitionQuery() + .processDefinitionKey(adhocKey) + .singleResult(); + + assertNotNull(processDefinition); + + response = publicApiClient.get("public", "process-definitions", processDefinition.getId(), null, null, null); + assertEquals(200, response.getStatusCode()); + response = publicApiClient.post("public", "process-definitions", processDefinition.getId(), null, null, null); + assertEquals(405, response.getStatusCode()); + response = publicApiClient.delete("public", "process-definitions", processDefinition.getId(), null, null); + assertEquals(405, response.getStatusCode()); + response = publicApiClient.put("public", "process-definitions", processDefinition.getId(), null, null, null, null); + assertEquals(405, response.getStatusCode()); + + response = publicApiClient.get("public", "process-definitions", processDefinition.getId(), "start-form-model", null, null); + assertEquals(200, response.getStatusCode()); + response = publicApiClient.post("public", "process-definitions", processDefinition.getId(), "start-form-model", null, null); + assertEquals(405, response.getStatusCode()); + response = publicApiClient.delete("public", "process-definitions", processDefinition.getId(), "start-form-model", null); + assertEquals(405, response.getStatusCode()); + response = publicApiClient.put("public", "process-definitions", processDefinition.getId(), "start-form-model", null, null, null); + assertEquals(405, response.getStatusCode()); + } + + @Test + public void testAuthenticationAndAuthorization() throws Exception + { + // Fetching process definitions as admin should be possible + RequestContext requestContext = initApiClientWithTestUser(); + String tenantAdmin = AuthenticationUtil.getAdminUserName() + "@" + requestContext.getNetworkId(); + publicApiClient.setRequestContext(new RequestContext(requestContext.getNetworkId(), tenantAdmin)); + + ProcessDefinitionsClient processDefinitionsClient = publicApiClient.processDefinitionsClient(); + ListResponse processDefinitionsResponse = processDefinitionsClient.getProcessDefinitions(null); + Map processDefinitionMap = getProcessDefinitionMapByKey(processDefinitionsResponse.getList()); + + assertTrue(processDefinitionMap.containsKey("activitiReviewPooled")); + assertTrue(processDefinitionMap.containsKey("activitiReview")); + assertTrue(processDefinitionMap.containsKey("activitiParallelGroupReview")); + assertTrue(processDefinitionMap.containsKey("activitiParallelReview")); + assertTrue(processDefinitionMap.containsKey("activitiAdhoc")); + assertEquals(5, processDefinitionMap.size()); + + // Fetching process definitions as admin from another tenant shouldn't be possible + TestNetwork anotherNetwork = getOtherNetwork(requestContext.getNetworkId()); + tenantAdmin = AuthenticationUtil.getAdminUserName() + "@" + anotherNetwork.getId(); + RequestContext otherContext = new RequestContext(anotherNetwork.getId(), tenantAdmin); + publicApiClient.setRequestContext(otherContext); + + processDefinitionsResponse = processDefinitionsClient.getProcessDefinitions(null); + processDefinitionMap = getProcessDefinitionMapByKey(processDefinitionsResponse.getList()); + + // the response should contain process definitions from the new tenant + assertTrue(processDefinitionMap.containsKey("activitiReviewPooled")); + assertTrue(processDefinitionMap.containsKey("activitiReview")); + assertTrue(processDefinitionMap.containsKey("activitiParallelGroupReview")); + assertTrue(processDefinitionMap.containsKey("activitiParallelReview")); + assertTrue(processDefinitionMap.containsKey("activitiAdhoc")); + assertEquals(5, processDefinitionMap.size()); + + // Fetching a specific process definitions as admin should be possible + publicApiClient.setRequestContext(requestContext); + + String adhocKey = createProcessDefinitionKey("activitiAdhoc", requestContext); + org.activiti.engine.repository.ProcessDefinition activitiDefinition = activitiProcessEngine.getRepositoryService() + .createProcessDefinitionQuery() + .processDefinitionKey(adhocKey) + .singleResult(); + + assertNotNull(activitiDefinition); + + // Get a single process definitions + ProcessDefinition adhocDefinition = processDefinitionsClient.findProcessDefinitionById(activitiDefinition.getId()); + assertNotNull(adhocDefinition); + + // Check fields of a resulting process-definition + assertEquals(activitiDefinition.getId(), adhocDefinition.getId()); + assertEquals("activitiAdhoc", adhocDefinition.getKey()); + assertEquals(activitiDefinition.getDeploymentId(), adhocDefinition.getDeploymentId()); + assertEquals(activitiDefinition.getCategory(), adhocDefinition.getCategory()); + assertEquals(activitiDefinition.getName(), adhocDefinition.getName()); + assertEquals(activitiDefinition.getVersion(), adhocDefinition.getVersion()); + assertEquals(((ProcessDefinitionEntity) activitiDefinition).isGraphicalNotationDefined(), adhocDefinition.isGraphicNotationDefined()); + assertEquals("wf:submitAdhocTask", adhocDefinition.getStartFormResourceKey()); + + // Fetching a specific process definitions as admin from another tenant should not be possible + publicApiClient.setRequestContext(otherContext); + try + { + adhocDefinition = processDefinitionsClient.findProcessDefinitionById(activitiDefinition.getId()); + fail("not found expected"); + } + catch (PublicApiException e) + { + assertEquals(HttpStatus.NOT_FOUND.value(), e.getHttpResponse().getStatusCode()); + } + + // Fetching the start form model of a process definition as admin should be possible + publicApiClient.setRequestContext(requestContext); + JSONObject model = processDefinitionsClient.findStartFormModel(activitiDefinition.getId()); + assertNotNull(model); + + JSONArray entries = (JSONArray) model.get("entries"); + assertNotNull(entries); + + // Add all entries to a map, to make lookup easier + Map modelFieldsByName = new HashMap(); + JSONObject entry = null; + for(int i=0; i processDefinitionsResponse = processDefinitionsClient.getProcessDefinitions(params); return getProcessDefinitionMapByKey(processDefinitionsResponse.getList()); } diff --git a/source/test-java/org/alfresco/rest/workflow/api/tests/ProcessWorkflowApiTest.java b/source/test-java/org/alfresco/rest/workflow/api/tests/ProcessWorkflowApiTest.java index 927a0f9909..6450186ad8 100644 --- a/source/test-java/org/alfresco/rest/workflow/api/tests/ProcessWorkflowApiTest.java +++ b/source/test-java/org/alfresco/rest/workflow/api/tests/ProcessWorkflowApiTest.java @@ -24,6 +24,7 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import java.util.Calendar; import java.util.Date; import java.util.HashMap; import java.util.List; @@ -31,9 +32,11 @@ import java.util.Map; import org.activiti.engine.history.HistoricProcessInstance; import org.activiti.engine.task.Task; +import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.tenant.TenantUtil; import org.alfresco.repo.tenant.TenantUtil.TenantRunAsWork; import org.alfresco.repo.workflow.activiti.ActivitiScriptNode; +import org.alfresco.rest.api.tests.RepoService.TestNetwork; import org.alfresco.rest.api.tests.client.PublicApiClient.ListResponse; import org.alfresco.rest.api.tests.client.PublicApiException; import org.alfresco.rest.api.tests.client.RequestContext; @@ -47,7 +50,6 @@ import org.json.simple.JSONArray; import org.json.simple.JSONObject; import org.junit.Test; import org.springframework.http.HttpStatus; - /** * Process related Rest api tests using http client to communicate with the rest apis in the repository. * @@ -89,10 +91,41 @@ public class ProcessWorkflowApiTest extends EnterpriseWorkflowTestApi createProcessObject.put("variables", variablesObject); - final ProcessInfo processRest = processesClient.createProcess(createProcessObject.toJSONString()); + ProcessInfo processRest = processesClient.createProcess(createProcessObject.toJSONString()); + assertNotNull(processRest); + assertNotNull(processRest.getId()); + + HistoricProcessInstance processInstance = activitiProcessEngine.getHistoryService().createHistoricProcessInstanceQuery() + .processInstanceId(processRest.getId()).singleResult(); + + assertEquals(processInstance.getId(), processRest.getId()); + assertEquals(processInstance.getStartActivityId(), processRest.getStartActivityId()); + assertEquals(processInstance.getStartUserId(), processRest.getStartUserId()); + assertEquals(processInstance.getStartTime(), processRest.getStartedAt()); + assertEquals(processInstance.getProcessDefinitionId(), processRest.getProcessDefinitionId()); + assertEquals("activitiAdhoc", processRest.getProcessDefinitionKey()); + assertNull(processRest.getBusinessKey()); + assertNull(processRest.getDeleteReason()); + assertNull(processRest.getDurationInMs()); + assertNull(processRest.getEndActivityId()); + assertNull(processRest.getEndedAt()); + assertNull(processRest.getSuperProcessInstanceId()); + + Map variables = activitiProcessEngine.getRuntimeService().getVariables(processRest.getId()); + + assertEquals("test description", variables.get("bpm_description")); + assertEquals(1, variables.get("bpm_priority")); + + cleanupProcessInstance(processRest.getId()); + + // Test same create method with an admin user + String tenantAdmin = AuthenticationUtil.getAdminUserName() + "@" + requestContext.getNetworkId(); + publicApiClient.setRequestContext(new RequestContext(requestContext.getNetworkId(), tenantAdmin)); + + processRest = processesClient.createProcess(createProcessObject.toJSONString()); assertNotNull(processRest); - final Map variables = activitiProcessEngine.getRuntimeService().getVariables(processRest.getId()); + variables = activitiProcessEngine.getRuntimeService().getVariables(processRest.getId()); assertEquals("test description", variables.get("bpm_description")); assertEquals(1, variables.get("bpm_priority")); @@ -100,6 +133,7 @@ public class ProcessWorkflowApiTest extends EnterpriseWorkflowTestApi cleanupProcessInstance(processRest.getId()); // Try with unexisting process definition ID + publicApiClient.setRequestContext(requestContext); createProcessObject = new JSONObject(); createProcessObject.put("processDefinitionId", "unexisting"); try @@ -145,13 +179,29 @@ public class ProcessWorkflowApiTest extends EnterpriseWorkflowTestApi ProcessInfo processRest = processesClient.createProcess(createProcessObject.toJSONString()); assertNotNull(processRest); - final Map variables = activitiProcessEngine.getRuntimeService().getVariables(processRest.getId()); + Map variables = activitiProcessEngine.getRuntimeService().getVariables(processRest.getId()); assertEquals("test description", variables.get("bpm_description")); assertEquals(1, variables.get("bpm_priority")); cleanupProcessInstance(processRest.getId()); + // Test same create method with an admin user + String tenantAdmin = AuthenticationUtil.getAdminUserName() + "@" + requestContext.getNetworkId(); + publicApiClient.setRequestContext(new RequestContext(requestContext.getNetworkId(), tenantAdmin)); + + processRest = processesClient.createProcess(createProcessObject.toJSONString()); + assertNotNull(processRest); + + variables = activitiProcessEngine.getRuntimeService().getVariables(processRest.getId()); + + assertEquals("test description", variables.get("bpm_description")); + assertEquals(1, variables.get("bpm_priority")); + + cleanupProcessInstance(processRest.getId()); + + // Test create process with wrong key + publicApiClient.setRequestContext(requestContext); createProcessObject = new JSONObject(); createProcessObject.put("processDefinitionKey", "activitiAdhoc2"); @@ -168,6 +218,61 @@ public class ProcessWorkflowApiTest extends EnterpriseWorkflowTestApi } } + @Test + public void testCreateProcessInstanceForPooledReview() throws Exception + { + final RequestContext requestContext = initApiClientWithTestUser(); + final ProcessInfo processInfo = startReviewPooledProcess(requestContext); + } + + @Test + @SuppressWarnings("unchecked") + public void testCreateProcessInstanceFormOtherNetwork() throws Exception + { + final RequestContext requestContext = initApiClientWithTestUser(); + + org.activiti.engine.repository.ProcessDefinition processDefinition = activitiProcessEngine + .getRepositoryService() + .createProcessDefinitionQuery() + .processDefinitionKey("@" + requestContext.getNetworkId() + "@activitiAdhoc") + .singleResult(); + + TestNetwork anotherNetwork = getOtherNetwork(requestContext.getNetworkId()); + String tenantAdmin = AuthenticationUtil.getAdminUserName() + "@" + anotherNetwork.getId(); + RequestContext otherContext = new RequestContext(anotherNetwork.getId(), tenantAdmin); + publicApiClient.setRequestContext(otherContext); + + ProcessesClient processesClient = publicApiClient.processesClient(); + + JSONObject createProcessObject = new JSONObject(); + createProcessObject.put("processDefinitionId", processDefinition.getId()); + final JSONObject variablesObject = new JSONObject(); + variablesObject.put("bpm_dueDate", ISO8601DateFormat.format(new Date())); + variablesObject.put("bpm_priority", 1); + variablesObject.put("bpm_description", "test description"); + TenantUtil.runAsUserTenant(new TenantRunAsWork() + { + @Override + public Void doWork() throws Exception + { + variablesObject.put("bpm_assignee", requestContext.getRunAsUser()); + return null; + } + }, requestContext.getRunAsUser(), requestContext.getNetworkId()); + + + createProcessObject.put("variables", variablesObject); + + try + { + processesClient.createProcess(createProcessObject.toJSONString()); + } + catch (PublicApiException e) + { + assertEquals(HttpStatus.BAD_REQUEST.value(), e.getHttpResponse().getStatusCode()); + } + } + @Test public void testCreateProcessInstanceWithItems() throws Exception { @@ -236,15 +341,73 @@ public class ProcessWorkflowApiTest extends EnterpriseWorkflowTestApi ProcessInfo processInfo = processesClient.findProcessById(process.getId()); assertNotNull(processInfo); - assertEquals(process.getId(), processInfo.getId()); - assertEquals(process.getBusinessKey(), processInfo.getBusinessKey()); + final Map variables = activitiProcessEngine.getRuntimeService().getVariables(processInfo.getId()); + assertEquals(1, variables.get("bpm_priority")); + + HistoricProcessInstance processInstance = activitiProcessEngine.getHistoryService().createHistoricProcessInstanceQuery() + .processInstanceId(processInfo.getId()).singleResult(); + + assertNotNull(processInfo.getId()); + assertEquals(processInstance.getId(), processInfo.getId()); + assertNotNull(processInfo.getStartActivityId()); + assertEquals(processInstance.getStartActivityId(), processInfo.getStartActivityId()); + assertNotNull(processInfo.getStartUserId()); + assertEquals(processInstance.getStartUserId(), processInfo.getStartUserId()); + assertNotNull(processInfo.getStartedAt()); + assertEquals(processInstance.getStartTime(), processInfo.getStartedAt()); + assertNotNull(processInfo.getProcessDefinitionId()); + assertEquals(processInstance.getProcessDefinitionId(), processInfo.getProcessDefinitionId()); + assertNotNull(processInfo.getProcessDefinitionKey()); + assertEquals("activitiAdhoc", processInfo.getProcessDefinitionKey()); + assertNull(processInfo.getBusinessKey()); assertNull(processInfo.getDeleteReason()); - assertEquals(process.getDurationInMillis(), processInfo.getDurationInMillis()); - assertEquals(process.getEndedAt(), processInfo.getEndedAt()); - assertEquals(process.getProcessDefinitionId(), processInfo.getProcessDefinitionId()); - assertEquals(process.getProcessDefinitionKey(), processInfo.getProcessDefinitionKey()); - assertEquals(process.getStartedAt(), processInfo.getStartedAt()); - assertEquals(process.getSuperProcessInstanceId(), processInfo.getSuperProcessInstanceId()); + assertNull(processInfo.getDurationInMs()); + assertNull(processInfo.getEndActivityId()); + assertNull(processInfo.getEndedAt()); + assertNull(processInfo.getSuperProcessInstanceId()); + assertFalse(processInfo.isCompleted()); + + TenantUtil.runAsUserTenant(new TenantRunAsWork() + { + @Override + public Void doWork() throws Exception + { + // now complete the process and see if ending info is available in the REST response + Task task = activitiProcessEngine.getTaskService().createTaskQuery().processInstanceId(process.getId()).singleResult(); + activitiProcessEngine.getTaskService().complete(task.getId()); + task = activitiProcessEngine.getTaskService().createTaskQuery().processInstanceId(process.getId()).singleResult(); + activitiProcessEngine.getTaskService().complete(task.getId()); + return null; + } + }, requestContext.getRunAsUser(), requestContext.getNetworkId()); + + processInstance = activitiProcessEngine.getHistoryService().createHistoricProcessInstanceQuery() + .processInstanceId(processInfo.getId()).singleResult(); + + processInfo = processesClient.findProcessById(processInfo.getId()); + + assertNotNull(processInfo.getId()); + assertEquals(processInstance.getId(), processInfo.getId()); + assertNotNull(processInfo.getStartActivityId()); + assertEquals(processInstance.getStartActivityId(), processInfo.getStartActivityId()); + assertNotNull(processInfo.getStartUserId()); + assertEquals(processInstance.getStartUserId(), processInfo.getStartUserId()); + assertNotNull(processInfo.getStartedAt()); + assertEquals(processInstance.getStartTime(), processInfo.getStartedAt()); + assertNotNull(processInfo.getProcessDefinitionId()); + assertEquals(processInstance.getProcessDefinitionId(), processInfo.getProcessDefinitionId()); + assertNotNull(processInfo.getProcessDefinitionKey()); + assertEquals("activitiAdhoc", processInfo.getProcessDefinitionKey()); + assertNull(processInfo.getBusinessKey()); + assertNull(processInfo.getDeleteReason()); + assertNotNull(processInfo.getDurationInMs()); + assertEquals(processInstance.getDurationInMillis(), processInfo.getDurationInMs()); + assertNotNull(processInfo.getEndActivityId()); + assertEquals(processInstance.getEndActivityId(), processInfo.getEndActivityId()); + assertNotNull(processInfo.getEndedAt()); + assertEquals(processInstance.getEndTime(), processInfo.getEndedAt()); + assertNull(processInfo.getSuperProcessInstanceId()); + assertTrue(processInfo.isCompleted()); } finally { @@ -271,9 +434,18 @@ public class ProcessWorkflowApiTest extends EnterpriseWorkflowTestApi public void testDeleteProcessInstanceById() throws Exception { final RequestContext requestContext = initApiClientWithTestUser(); + + String tenantAdmin = AuthenticationUtil.getAdminUserName() + "@" + requestContext.getNetworkId(); + final RequestContext adminContext = new RequestContext(requestContext.getNetworkId(), tenantAdmin); + + TestNetwork anotherNetwork = getOtherNetwork(requestContext.getNetworkId()); + tenantAdmin = AuthenticationUtil.getAdminUserName() + "@" + anotherNetwork.getId(); + final RequestContext otherContext = new RequestContext(anotherNetwork.getId(), tenantAdmin); + ProcessesClient processesClient = publicApiClient.processesClient(); - final ProcessInfo process = startAdhocProcess(requestContext, null); + // delete with user starting the process instance + ProcessInfo process = startAdhocProcess(requestContext, null); try { processesClient.deleteProcessById(process.getId()); @@ -292,6 +464,45 @@ public class ProcessWorkflowApiTest extends EnterpriseWorkflowTestApi { cleanupProcessInstance(process.getId()); } + + // delete with admin in same network as the user starting the process instance + process = startAdhocProcess(requestContext, null); + try + { + publicApiClient.setRequestContext(adminContext); + processesClient.deleteProcessById(process.getId()); + + // Check if the process was actually deleted + assertNull(activitiProcessEngine.getRuntimeService().createProcessInstanceQuery() + .processInstanceId(process.getId()).singleResult()); + + HistoricProcessInstance deletedInstance = activitiProcessEngine.getHistoryService() + .createHistoricProcessInstanceQuery().processInstanceId(process.getId()).singleResult(); + assertNotNull(deletedInstance); + assertNotNull(deletedInstance.getEndTime()); + assertNull(deletedInstance.getDeleteReason()); + } + finally + { + cleanupProcessInstance(process.getId()); + } + + // delete with admin from other network as the user starting the process instance + process = startAdhocProcess(requestContext, null); + try + { + publicApiClient.setRequestContext(otherContext); + processesClient.deleteProcessById(process.getId()); + fail("Expect permission exception"); + } + catch (PublicApiException e) + { + assertEquals(HttpStatus.FORBIDDEN.value(), e.getHttpResponse().getStatusCode()); + } + finally + { + cleanupProcessInstance(process.getId()); + } } @Test @@ -409,20 +620,297 @@ public class ProcessWorkflowApiTest extends EnterpriseWorkflowTestApi // Test the variable where-clause paramMap = new HashMap(); - paramMap.put("where", "(variables/bpm_priority = 'd_int 1'))"); + paramMap.put("where", "(variables/bpm_priority = 'd_int 1')"); processList = processesClient.getProcesses(paramMap); assertNotNull(processList); assertEquals(3, processList.getList().size()); paramMap = new HashMap(); - paramMap.put("where", "(variables/bpm_priority = 'd_int 5'))"); + paramMap.put("where", "(variables/bpm_priority = 'd:int 1')"); + processList = processesClient.getProcesses(paramMap); + assertNotNull(processList); + assertEquals(3, processList.getList().size()); + + paramMap = new HashMap(); + paramMap.put("where", "(variables/bpm_priority = 'd_int 5')"); processList = processesClient.getProcesses(paramMap); assertNotNull(processList); assertEquals(0, processList.getList().size()); + // test with date variable + Calendar dateCal = Calendar.getInstance(); + Map variablesToSet = new HashMap(); + variablesToSet.put("testVarDate", dateCal.getTime()); + + activitiProcessEngine.getRuntimeService().setVariables(process1.getId(), variablesToSet); + paramMap = new HashMap(); + paramMap.put("where", "(variables/testVarDate = 'd_datetime " + ISO8601DateFormat.format(dateCal.getTime())+ "')"); + processList = processesClient.getProcesses(paramMap); + assertNotNull(processList); + assertEquals(1, processList.getList().size()); + cleanupProcessInstance(process1.getId(), process2.getId(), process3.getId()); } + // No sorting support yet + /*@Test + public void testGetProcessInstancesWithSorting() throws Exception + { + final RequestContext requestContext = initApiClientWithTestUser(); + + final ProcessInfo process1 = startAdhocProcess(requestContext, null, "akey"); + final ProcessInfo process2 = startAdhocProcess(requestContext, null, "bkey"); + final ProcessInfo process3 = startAdhocProcess(requestContext, null, "aakey"); + + try + { + // sort on business key ascending + ProcessesClient processesClient = publicApiClient.processesClient(); + Map paramMap = new HashMap(); + paramMap.put("sort", "businessKey"); + paramMap.put("sortOrder", "asc"); + ListResponse processList = processesClient.getProcesses(paramMap); + assertNotNull(processList); + assertEquals(3, processList.getList().size()); + + assertEquals(process3.getId(), processList.getList().get(0).getId()); + assertEquals(process1.getId(), processList.getList().get(1).getId()); + assertEquals(process2.getId(), processList.getList().get(2).getId()); + + // sort on business key descending + paramMap.put("sort", "businessKey"); + paramMap.put("sortOrder", "desc"); + processList = processesClient.getProcesses(paramMap); + assertNotNull(processList); + assertEquals(3, processList.getList().size()); + + assertEquals(process2.getId(), processList.getList().get(0).getId()); + assertEquals(process1.getId(), processList.getList().get(1).getId()); + assertEquals(process3.getId(), processList.getList().get(2).getId()); + + // sort on non existing key + paramMap.put("sort", "businessKey2"); + try + { + processList = processesClient.getProcesses(paramMap); + fail(); + } + catch (PublicApiException e) + { + assertEquals(HttpStatus.BAD_REQUEST.value(), e.getHttpResponse().getStatusCode()); + } + + // sort on non existing sort order + paramMap.put("sort", "businessKey"); + paramMap.put("sortOrder", "asc2"); + try + { + processList = processesClient.getProcesses(paramMap); + fail(); + } + catch (PublicApiException e) + { + assertEquals(HttpStatus.BAD_REQUEST.value(), e.getHttpResponse().getStatusCode()); + } + } + finally + { + cleanupProcessInstance(process1.getId(), process2.getId(), process3.getId()); + } + }*/ + + @Test + public void testGetProcessTasks() throws Exception + { + final RequestContext requestContext = initApiClientWithTestUser(); + + String tenantAdmin = AuthenticationUtil.getAdminUserName() + "@" + requestContext.getNetworkId(); + final RequestContext adminContext = new RequestContext(requestContext.getNetworkId(), tenantAdmin); + + TestNetwork anotherNetwork = getOtherNetwork(requestContext.getNetworkId()); + tenantAdmin = AuthenticationUtil.getAdminUserName() + "@" + anotherNetwork.getId(); + final RequestContext otherContext = new RequestContext(anotherNetwork.getId(), tenantAdmin); + + final ProcessInfo process1 = startAdhocProcess(requestContext, null); + + try + { + ProcessesClient processesClient = publicApiClient.processesClient(); + Map paramMap = new HashMap(); + JSONObject tasksJSON = processesClient.getTasks(process1.getId(), paramMap); + assertNotNull(tasksJSON); + JSONArray entriesJSON = (JSONArray) tasksJSON.get("entries"); + assertNotNull(entriesJSON); + assertTrue(entriesJSON.size() == 1); + JSONObject taskJSONObject = (JSONObject) ((JSONObject) entriesJSON.get(0)).get("entry"); + assertNotNull(taskJSONObject.get("id")); + assertEquals(process1.getId(), taskJSONObject.get("processId")); + assertEquals(process1.getProcessDefinitionId(), taskJSONObject.get("processDefinitionId")); + assertEquals("adhocTask", taskJSONObject.get("activityDefinitionId")); + assertEquals("Adhoc Task", taskJSONObject.get("name")); + assertEquals(requestContext.getRunAsUser(), taskJSONObject.get("assignee")); + assertEquals(2l, taskJSONObject.get("priority")); + assertEquals("wf:adhocTask", taskJSONObject.get("formResourceKey")); + assertNull(taskJSONObject.get("endedAt")); + assertNull(taskJSONObject.get("durationInMs")); + + paramMap = new HashMap(); + paramMap.put("status", "active"); + tasksJSON = processesClient.getTasks(process1.getId(), paramMap); + assertNotNull(tasksJSON); + entriesJSON = (JSONArray) tasksJSON.get("entries"); + assertNotNull(entriesJSON); + assertTrue(entriesJSON.size() == 1); + + paramMap = new HashMap(); + paramMap.put("status", "completed"); + tasksJSON = processesClient.getTasks(process1.getId(), paramMap); + assertNotNull(tasksJSON); + entriesJSON = (JSONArray) tasksJSON.get("entries"); + assertNotNull(entriesJSON); + assertTrue(entriesJSON.size() == 0); + + paramMap = new HashMap(); + try { + processesClient.getTasks("fakeid", paramMap); + fail("Exception expected"); + } catch(PublicApiException expected) { + assertEquals(HttpStatus.NOT_FOUND.value(), expected.getHttpResponse().getStatusCode()); + assertErrorSummary("The entity with id: fakeid was not found", expected.getHttpResponse()); + } + + // get tasks with admin from the same tenant as the process initiator + publicApiClient.setRequestContext(adminContext); + paramMap = new HashMap(); + tasksJSON = processesClient.getTasks(process1.getId(), paramMap); + assertNotNull(tasksJSON); + entriesJSON = (JSONArray) tasksJSON.get("entries"); + assertNotNull(entriesJSON); + assertTrue(entriesJSON.size() == 1); + + // get tasks with admin from another tenant as the process initiator + publicApiClient.setRequestContext(otherContext); + paramMap = new HashMap(); + try + { + tasksJSON = processesClient.getTasks(process1.getId(), paramMap); + fail("forbidden expected"); + } + catch (PublicApiException e) + { + assertEquals(HttpStatus.FORBIDDEN.value(), e.getHttpResponse().getStatusCode()); + } + } + finally + { + cleanupProcessInstance(process1.getId()); + } + } + + @Test + public void testGetProcessActivities() throws Exception + { + final RequestContext requestContext = initApiClientWithTestUser(); + + String tenantAdmin = AuthenticationUtil.getAdminUserName() + "@" + requestContext.getNetworkId(); + final RequestContext adminContext = new RequestContext(requestContext.getNetworkId(), tenantAdmin); + + TestNetwork anotherNetwork = getOtherNetwork(requestContext.getNetworkId()); + tenantAdmin = AuthenticationUtil.getAdminUserName() + "@" + anotherNetwork.getId(); + final RequestContext otherContext = new RequestContext(anotherNetwork.getId(), tenantAdmin); + + final ProcessInfo process1 = startAdhocProcess(requestContext, null); + + try + { + ProcessesClient processesClient = publicApiClient.processesClient(); + Map paramMap = new HashMap(); + JSONObject activitiesJSON = processesClient.getActivities(process1.getId(), paramMap); + assertNotNull(activitiesJSON); + JSONArray entriesJSON = (JSONArray) activitiesJSON.get("entries"); + assertNotNull(entriesJSON); + assertTrue(entriesJSON.size() == 2); + + Map activitiesMap = new HashMap(); + for (Object entry : entriesJSON) { + JSONObject jsonEntry = (JSONObject) entry; + JSONObject activityJSONObject = (JSONObject) jsonEntry.get("entry"); + activitiesMap.put((String) activityJSONObject.get("activityDefinitionId"), activityJSONObject); + } + + JSONObject activityJSONObject = activitiesMap.get("start"); + assertNotNull(activityJSONObject); + assertNotNull(activityJSONObject.get("id")); + assertEquals("start", activityJSONObject.get("activityDefinitionId")); + assertNull(activityJSONObject.get("activityDefinitionName")); + assertEquals("startEvent", activityJSONObject.get("activityDefinitionType")); + assertNotNull(activityJSONObject.get("startedAt")); + assertNotNull(activityJSONObject.get("endedAt")); + assertNotNull(activityJSONObject.get("durationInMs")); + + activityJSONObject = activitiesMap.get("adhocTask"); + assertNotNull(activityJSONObject); + assertNotNull(activityJSONObject.get("id")); + assertEquals("adhocTask", activityJSONObject.get("activityDefinitionId")); + assertEquals("Adhoc Task", activityJSONObject.get("activityDefinitionName")); + assertEquals("userTask", activityJSONObject.get("activityDefinitionType")); + assertNotNull(activityJSONObject.get("startedAt")); + assertNull(activityJSONObject.get("endedAt")); + assertNull(activityJSONObject.get("durationInMs")); + + paramMap = new HashMap(); + paramMap.put("status", "active"); + activitiesJSON = processesClient.getActivities(process1.getId(), paramMap); + assertNotNull(activitiesJSON); + entriesJSON = (JSONArray) activitiesJSON.get("entries"); + assertNotNull(entriesJSON); + assertTrue(entriesJSON.size() == 1); + + paramMap = new HashMap(); + paramMap.put("status", "completed"); + activitiesJSON = processesClient.getActivities(process1.getId(), paramMap); + assertNotNull(activitiesJSON); + entriesJSON = (JSONArray) activitiesJSON.get("entries"); + assertNotNull(entriesJSON); + assertTrue(entriesJSON.size() == 1); + + paramMap = new HashMap(); + try { + processesClient.getActivities("fakeid", paramMap); + fail("Exception expected"); + } catch(PublicApiException expected) { + assertEquals(HttpStatus.NOT_FOUND.value(), expected.getHttpResponse().getStatusCode()); + assertErrorSummary("The entity with id: fakeid was not found", expected.getHttpResponse()); + } + + // get activities with admin from the same tenant as the process initiator + publicApiClient.setRequestContext(adminContext); + paramMap = new HashMap(); + activitiesJSON = processesClient.getActivities(process1.getId(), paramMap); + assertNotNull(activitiesJSON); + entriesJSON = (JSONArray) activitiesJSON.get("entries"); + assertNotNull(entriesJSON); + assertTrue(entriesJSON.size() == 2); + + // get tasks with admin from another tenant as the process initiator + publicApiClient.setRequestContext(otherContext); + paramMap = new HashMap(); + try + { + processesClient.getActivities(process1.getId(), paramMap); + fail("forbidden expected"); + } + catch (PublicApiException e) + { + assertEquals(HttpStatus.FORBIDDEN.value(), e.getHttpResponse().getStatusCode()); + } + } + finally + { + cleanupProcessInstance(process1.getId()); + } + } + @Test public void testGetProcessItems() throws Exception { @@ -447,7 +935,7 @@ public class ProcessWorkflowApiTest extends EnterpriseWorkflowTestApi JSONObject entryJSON = (JSONObject) entryObjectJSON.get("entry"); if (entryJSON.get("name").equals("Test Doc1")) { doc1Found = true; - assertEquals(docNodeRefs[0].toString(), entryJSON.get("id")); + assertEquals(docNodeRefs[0].getId(), entryJSON.get("id")); assertEquals("Test Doc1", entryJSON.get("name")); assertEquals("Test Doc1 Title", entryJSON.get("title")); assertEquals("Test Doc1 Description", entryJSON.get("description")); @@ -459,7 +947,7 @@ public class ProcessWorkflowApiTest extends EnterpriseWorkflowTestApi assertNotNull(entryJSON.get("mimeType")); } else { doc2Found = true; - assertEquals(docNodeRefs[1].toString(), entryJSON.get("id")); + assertEquals(docNodeRefs[1].getId(), entryJSON.get("id")); assertEquals("Test Doc2", entryJSON.get("name")); assertEquals("Test Doc2 Title", entryJSON.get("title")); assertEquals("Test Doc2 Description", entryJSON.get("description")); @@ -488,10 +976,11 @@ public class ProcessWorkflowApiTest extends EnterpriseWorkflowTestApi final String newProcessInstanceId = processRest.getId(); ProcessesClient processesClient = publicApiClient.processesClient(); - JSONObject itemJSON = processesClient.findProcessItem(newProcessInstanceId, docNodeRefs[0].toString()); + System.out.println("node ref " + docNodeRefs[0].toString()); + JSONObject itemJSON = processesClient.findProcessItem(newProcessInstanceId, docNodeRefs[0].getId()); assertNotNull(itemJSON); - assertEquals(docNodeRefs[0].toString(), itemJSON.get("id")); + assertEquals(docNodeRefs[0].getId(), itemJSON.get("id")); assertEquals("Test Doc1", itemJSON.get("name")); assertEquals("Test Doc1 Title", itemJSON.get("title")); assertEquals("Test Doc1 Description", itemJSON.get("description")); @@ -505,6 +994,61 @@ public class ProcessWorkflowApiTest extends EnterpriseWorkflowTestApi cleanupProcessInstance(processRest.getId()); } + @Test + @SuppressWarnings("unchecked") + public void testAddProcessItem() throws Exception + { + final RequestContext requestContext = initApiClientWithTestUser(); + + NodeRef[] docNodeRefs = createTestDocuments(requestContext); + final ProcessInfo processRest = startAdhocProcess(requestContext, null); + try + { + assertNotNull(processRest); + + final String newProcessInstanceId = processRest.getId(); + ProcessesClient processesClient = publicApiClient.processesClient(); + + JSONObject createItemObject = new JSONObject(); + createItemObject.put("id", docNodeRefs[0].getId()); + + // Add the item + processesClient.addProcessItem(newProcessInstanceId, createItemObject.toJSONString()); + + // Fetching the item + JSONObject itemJSON = publicApiClient.processesClient().findProcessItem(newProcessInstanceId, docNodeRefs[0].getId()); + assertEquals(docNodeRefs[0].getId(), itemJSON.get("id")); + assertEquals("Test Doc1", itemJSON.get("name")); + assertEquals("Test Doc1 Title", itemJSON.get("title")); + assertEquals("Test Doc1 Description", itemJSON.get("description")); + assertNotNull(itemJSON.get("createdAt")); + assertEquals(requestContext.getRunAsUser(), itemJSON.get("createdBy")); + assertNotNull(itemJSON.get("modifiedAt")); + assertEquals(requestContext.getRunAsUser(), itemJSON.get("modifiedBy")); + assertNotNull(itemJSON.get("size")); + assertNotNull(itemJSON.get("mimeType")); + + // add non existing item + createItemObject = new JSONObject(); + createItemObject.put("id", "blablabla"); + + // Add the item + try + { + processesClient.addProcessItem(newProcessInstanceId, createItemObject.toJSONString()); + fail("not found expected"); + } + catch (PublicApiException e) + { + assertEquals(HttpStatus.NOT_FOUND.value(), e.getHttpResponse().getStatusCode()); + } + } + finally + { + cleanupProcessInstance(processRest.getId()); + } + } + @Test public void testDeleteProcessItem() throws Exception { @@ -520,16 +1064,31 @@ public class ProcessWorkflowApiTest extends EnterpriseWorkflowTestApi ProcessesClient processesClient = publicApiClient.processesClient(); // Delete the item - processesClient.deleteProcessItem(newProcessInstanceId, docNodeRefs[0].toString()); + processesClient.deleteProcessItem(newProcessInstanceId, docNodeRefs[0].getId()); // Fetching the item should result in 404 - try { - publicApiClient.processesClient().findProcessItem(newProcessInstanceId, docNodeRefs[0].toString()); + try + { + publicApiClient.processesClient().findProcessItem(newProcessInstanceId, docNodeRefs[0].getId()); fail("Exception expected"); - } catch(PublicApiException expected) { + } + catch(PublicApiException expected) + { assertEquals(HttpStatus.NOT_FOUND.value(), expected.getHttpResponse().getStatusCode()); - assertErrorSummary("The entity with id: " + docNodeRefs[0].toString() + " was not found", expected.getHttpResponse()); + assertErrorSummary("The entity with id: " + docNodeRefs[0].getId() + " was not found", expected.getHttpResponse()); } + + // Deleting the item again should give an error + try + { + processesClient.deleteProcessItem(newProcessInstanceId, docNodeRefs[0].getId()); + fail("Expected not found"); + } + catch (PublicApiException e) + { + assertEquals(HttpStatus.NOT_FOUND.value(), e.getHttpResponse().getStatusCode()); + } + } finally { @@ -542,6 +1101,13 @@ public class ProcessWorkflowApiTest extends EnterpriseWorkflowTestApi { RequestContext requestContext = initApiClientWithTestUser(); + String tenantAdmin = AuthenticationUtil.getAdminUserName() + "@" + requestContext.getNetworkId(); + RequestContext adminContext = new RequestContext(requestContext.getNetworkId(), tenantAdmin); + + TestNetwork anotherNetwork = getOtherNetwork(requestContext.getNetworkId()); + tenantAdmin = AuthenticationUtil.getAdminUserName() + "@" + anotherNetwork.getId(); + final RequestContext otherContext = new RequestContext(anotherNetwork.getId(), tenantAdmin); + ProcessInfo processRest = startAdhocProcess(requestContext, null); try @@ -551,46 +1117,36 @@ public class ProcessWorkflowApiTest extends EnterpriseWorkflowTestApi JSONObject processvariables = publicApiClient.processesClient().getProcessvariables(processInstanceId); assertNotNull(processvariables); + validateVariablesResponse(processvariables, requestContext.getRunAsUser()); - // Add process variables to map for easy lookup - Map variablesByName = new HashMap(); - JSONObject entry = null; - JSONArray entries = (JSONArray) processvariables.get("entries"); - assertNotNull(entries); - for(int i=0; i variablesByName = new HashMap(); + JSONObject entry = null; + JSONArray entries = (JSONArray) processvariables.get("entries"); + assertNotNull(entries); + for(int i=0; i processesRest.setProcessDefinitionKey((String) entry.get("processDefinitionKey")); processesRest.setStartedAt(WorkflowApiClient.parseDate(entry, "startedAt")); processesRest.setEndedAt(WorkflowApiClient.parseDate(entry, "endedAt")); - processesRest.setDurationInMillis((Long) entry.get("durationInMillis")); + processesRest.setDurationInMs((Long) entry.get("durationInMs")); processesRest.setDeleteReason((String) entry.get("deleteReason")); processesRest.setBusinessKey((String) entry.get("businessKey")); processesRest.setSuperProcessInstanceId((String) entry.get("superProcessInstanceId")); + processesRest.setStartActivityId((String) entry.get("startActivityId")); + processesRest.setStartUserId((String) entry.get("startUserId")); + processesRest.setEndActivityId((String) entry.get("endActivityId")); + processesRest.setCompleted((Boolean) entry.get("completed")); processesRest.setVariables((Map) entry.get("variables")); processesRest.setItems((Set) entry.get("item")); return processesRest; diff --git a/source/test-java/org/alfresco/rest/workflow/api/tests/TaskWorkflowApiTest.java b/source/test-java/org/alfresco/rest/workflow/api/tests/TaskWorkflowApiTest.java index a1f1113c82..e8e859e365 100644 --- a/source/test-java/org/alfresco/rest/workflow/api/tests/TaskWorkflowApiTest.java +++ b/source/test-java/org/alfresco/rest/workflow/api/tests/TaskWorkflowApiTest.java @@ -31,7 +31,6 @@ import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; @@ -50,7 +49,6 @@ import org.alfresco.repo.workflow.WorkflowConstants; import org.alfresco.repo.workflow.activiti.ActivitiConstants; import org.alfresco.repo.workflow.activiti.ActivitiScriptNode; import org.alfresco.rest.api.tests.RepoService.TestNetwork; -import org.alfresco.rest.api.tests.RepoService.TestPerson; import org.alfresco.rest.api.tests.client.PublicApiException; import org.alfresco.rest.api.tests.client.RequestContext; import org.alfresco.rest.api.tests.client.data.MemberOfSite; @@ -806,6 +804,53 @@ public class TaskWorkflowApiTest extends EnterpriseWorkflowTestApi } } + @Test + @SuppressWarnings("unchecked") + public void testChangeDueDate() throws Exception + { + RequestContext requestContext = initApiClientWithTestUser(); + ProcessInstance processInstance = startAdhocProcess(requestContext.getRunAsUser(), requestContext.getNetworkId(), null); + try + { + Task task = activitiProcessEngine.getTaskService().createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); + + TasksClient tasksClient = publicApiClient.tasksClient(); + JSONObject taskObject = tasksClient.findTaskById(task.getId()); + assertNull(taskObject.get("dueAt")); + + List selectedFields = new ArrayList(); + selectedFields.addAll(Arrays.asList(new String[] { "name", "description", "dueAt", "priority", "assignee", "owner"})); + + // set due date + JSONObject taskBody = new JSONObject(); + String dueAt = formatDate(new Date()); + taskBody.put("dueAt", dueAt); + tasksClient.updateTask(task.getId(), taskBody, selectedFields); + + taskObject = tasksClient.findTaskById(task.getId()); + assertNotNull(taskObject.get("dueAt")); + + taskBody = new JSONObject(); + taskBody.put("dueAt", taskObject.get("dueAt")); + tasksClient.updateTask(task.getId(), taskBody, selectedFields); + + taskObject = tasksClient.findTaskById(task.getId()); + assertNotNull(taskObject.get("dueAt")); + + JSONObject variableBody = new JSONObject(); + variableBody.put("name", "bpm_workflowDueDate"); + variableBody.put("value", formatDate(new Date())); + variableBody.put("type", "d:datetime"); + variableBody.put("scope", "global"); + + tasksClient.updateTaskVariable(task.getId(), "bpm_workflowDueDate", variableBody); + } + finally + { + cleanupProcessInstance(processInstance); + } + } + @Test public void testGetTasks() throws Exception { @@ -922,27 +967,28 @@ public class TaskWorkflowApiTest extends EnterpriseWorkflowTestApi activitiProcessEngine.getTaskService().saveTask(activeTask); activitiProcessEngine.getTaskService().addCandidateUser(activeTask.getId(), anotherUserId); activitiProcessEngine.getTaskService().addCandidateGroup(activeTask.getId(), "sales"); + activitiProcessEngine.getTaskService().setVariableLocal(activeTask.getId(), "numberVar", 10); TasksClient tasksClient = publicApiClient.tasksClient(); // Test status filtering - active Map params = new HashMap(); - params.put("where", "(status = 'active' AND processInstanceId = '" + processInstance.getId() + "')"); + params.put("where", "(status = 'active' AND processId = '" + processInstance.getId() + "')"); assertTasksPresentInTaskQuery(params, tasksClient, false, activeTask.getId()); // Test status filtering - completed params.clear(); - params.put("where", "(status = 'completed' AND processInstanceId = '" + processInstance.getId() + "')"); + params.put("where", "(status = 'completed' AND processId = '" + processInstance.getId() + "')"); assertTasksPresentInTaskQuery(params, tasksClient, false, completedTask.getId()); // Test status filtering - any params.clear(); - params.put("where", "(status = 'any' AND processInstanceId = '" + processInstance.getId() + "')"); + params.put("where", "(status = 'any' AND processId = '" + processInstance.getId() + "')"); assertTasksPresentInTaskQuery(params, tasksClient, false, activeTask.getId(), completedTask.getId()); // Test status filtering - no value should default to 'active' params.clear(); - params.put("where", "(processInstanceId = '" + processInstance.getId() + "')"); + params.put("where", "(processId = '" + processInstance.getId() + "')"); assertTasksPresentInTaskQuery(params, tasksClient, false, activeTask.getId()); // Test status filtering - illegal status @@ -1021,11 +1067,11 @@ public class TaskWorkflowApiTest extends EnterpriseWorkflowTestApi // Candidate group filtering, only available for active tasks. When used with completed/any 400 is returned params.clear(); - params.put("where", "(status = 'active' AND candidateGroup = 'sales' AND processInstanceId='" + processInstance.getId() +"')"); + params.put("where", "(status = 'active' AND candidateGroup = 'sales' AND processId='" + processInstance.getId() +"')"); assertTasksPresentInTaskQuery(params, tasksClient, activeTask.getId()); params.clear(); - params.put("where", "(status = 'completed' AND candidateGroup = 'sales' AND processInstanceId='" + processInstance.getId() +"')"); + params.put("where", "(status = 'completed' AND candidateGroup = 'sales' AND processId='" + processInstance.getId() +"')"); try { tasksClient.findTasks(params); @@ -1038,7 +1084,7 @@ public class TaskWorkflowApiTest extends EnterpriseWorkflowTestApi } params.clear(); - params.put("where", "(status = 'any' AND candidateGroup = 'sales' AND processInstanceId='" + processInstance.getId() +"')"); + params.put("where", "(status = 'any' AND candidateGroup = 'sales' AND processId='" + processInstance.getId() +"')"); try { tasksClient.findTasks(params); @@ -1052,94 +1098,94 @@ public class TaskWorkflowApiTest extends EnterpriseWorkflowTestApi // Name filtering params.clear(); - params.put("where", "(status = 'active' AND name = 'Task name' AND processInstanceId='" + processInstance.getId() +"')"); + params.put("where", "(status = 'active' AND name = 'Task name' AND processId='" + processInstance.getId() +"')"); assertTasksPresentInTaskQuery(params, tasksClient, activeTask.getId()); params.clear(); - params.put("where", "(status = 'completed' AND name = 'Another task name' AND processInstanceId='" + processInstance.getId() +"')"); + params.put("where", "(status = 'completed' AND name = 'Another task name' AND processId='" + processInstance.getId() +"')"); assertTasksPresentInTaskQuery(params, tasksClient, completedTask.getId()); params.clear(); - params.put("where", "(status = 'any' AND name = 'Another task name' AND processInstanceId='" + processInstance.getId() +"')"); + params.put("where", "(status = 'any' AND name = 'Another task name' AND processId='" + processInstance.getId() +"')"); assertTasksPresentInTaskQuery(params, tasksClient, completedTask.getId()); // Description filtering params.clear(); - params.put("where", "(status = 'active' AND description = 'This is a test description' AND processInstanceId='" + processInstance.getId() +"')"); + params.put("where", "(status = 'active' AND description = 'This is a test description' AND processId='" + processInstance.getId() +"')"); assertTasksPresentInTaskQuery(params, tasksClient, activeTask.getId()); params.clear(); - params.put("where", "(status = 'completed' AND description = 'This is another test description' AND processInstanceId='" + processInstance.getId() +"')"); + params.put("where", "(status = 'completed' AND description = 'This is another test description' AND processId='" + processInstance.getId() +"')"); assertTasksPresentInTaskQuery(params, tasksClient, completedTask.getId()); params.clear(); - params.put("where", "(status = 'any' AND description = 'This is another test description' AND processInstanceId='" + processInstance.getId() +"')"); + params.put("where", "(status = 'any' AND description = 'This is another test description' AND processId='" + processInstance.getId() +"')"); assertTasksPresentInTaskQuery(params, tasksClient, completedTask.getId()); // Priority filtering params.clear(); - params.put("where", "(status = 'active' AND priority = 2 AND processInstanceId='" + processInstance.getId() +"')"); + params.put("where", "(status = 'active' AND priority = 2 AND processId='" + processInstance.getId() +"')"); assertTasksPresentInTaskQuery(params, tasksClient, activeTask.getId()); - params.put("where", "(status = 'completed' AND priority = 3 AND processInstanceId='" + processInstance.getId() +"')"); + params.put("where", "(status = 'completed' AND priority = 3 AND processId='" + processInstance.getId() +"')"); assertTasksPresentInTaskQuery(params, tasksClient, completedTask.getId()); - params.put("where", "(status = 'any' AND priority = 3 AND processInstanceId='" + processInstance.getId() +"')"); + params.put("where", "(status = 'any' AND priority = 3 AND processId='" + processInstance.getId() +"')"); assertTasksPresentInTaskQuery(params, tasksClient, completedTask.getId()); // Process instance business-key filtering params.clear(); - params.put("where", "(status = 'active' AND processInstanceBusinessKey = '" + businessKey + "')"); + params.put("where", "(status = 'active' AND processBusinessKey = '" + businessKey + "')"); assertTasksPresentInTaskQuery(params, tasksClient, activeTask.getId()); params.clear(); - params.put("where", "(status = 'completed' AND processInstanceBusinessKey = '" + businessKey + "')"); + params.put("where", "(status = 'completed' AND processBusinessKey = '" + businessKey + "')"); assertTasksPresentInTaskQuery(params, tasksClient, completedTask.getId()); params.clear(); - params.put("where", "(status = 'any' AND processInstanceBusinessKey = '" + businessKey + "')"); + params.put("where", "(status = 'any' AND processBusinessKey = '" + businessKey + "')"); assertTasksPresentInTaskQuery(params, tasksClient, completedTask.getId(), activeTask.getId()); // Activity definition id filtering params.clear(); - params.put("where", "(status = 'active' AND activityDefinitionId = 'verifyTaskDone' AND processInstanceId='" + processInstance.getId() +"')"); + params.put("where", "(status = 'active' AND activityDefinitionId = 'verifyTaskDone' AND processId='" + processInstance.getId() +"')"); assertTasksPresentInTaskQuery(params, tasksClient, activeTask.getId()); params.clear(); - params.put("where", "(status = 'completed' AND activityDefinitionId = 'adhocTask' AND processInstanceId='" + processInstance.getId() +"')"); + params.put("where", "(status = 'completed' AND activityDefinitionId = 'adhocTask' AND processId='" + processInstance.getId() +"')"); assertTasksPresentInTaskQuery(params, tasksClient, completedTask.getId()); params.clear(); - params.put("where", "(status = 'any' AND activityDefinitionId = 'adhocTask' AND processInstanceId='" + processInstance.getId() +"')"); + params.put("where", "(status = 'any' AND activityDefinitionId = 'adhocTask' AND processId='" + processInstance.getId() +"')"); assertTasksPresentInTaskQuery(params, tasksClient, completedTask.getId()); // Process definition id filtering params.clear(); params.put("where", "(status = 'active' AND processDefinitionId = '" + processInstance.getProcessDefinitionId() + - "' AND processInstanceId='" + processInstance.getId() +"')"); + "' AND processId='" + processInstance.getId() +"')"); assertTasksPresentInTaskQuery(params, tasksClient, activeTask.getId()); params.clear(); params.put("where", "(status = 'completed' AND processDefinitionId = '" + processInstance.getProcessDefinitionId() + - "' AND processInstanceId='" + processInstance.getId() +"')"); + "' AND processId='" + processInstance.getId() +"')"); assertTasksPresentInTaskQuery(params, tasksClient, completedTask.getId()); params.clear(); params.put("where", "(status = 'any' AND processDefinitionId = '" + processInstance.getProcessDefinitionId() + - "' AND processInstanceId='" + processInstance.getId() +"')"); + "' AND processId='" + processInstance.getId() +"')"); assertTasksPresentInTaskQuery(params, tasksClient, activeTask.getId(), completedTask.getId()); // Process definition name filerting params.clear(); - params.put("where", "(status = 'active' AND processDefinitionName = 'Adhoc Activiti Process' AND processInstanceId='" + processInstance.getId() +"')"); + params.put("where", "(status = 'active' AND processDefinitionName = 'Adhoc Activiti Process' AND processId='" + processInstance.getId() +"')"); assertTasksPresentInTaskQuery(params, tasksClient, activeTask.getId()); params.clear(); - params.put("where", "(status = 'completed' AND processDefinitionName = 'Adhoc Activiti Process' AND processInstanceId='" + processInstance.getId() +"')"); + params.put("where", "(status = 'completed' AND processDefinitionName = 'Adhoc Activiti Process' AND processId='" + processInstance.getId() +"')"); assertTasksPresentInTaskQuery(params, tasksClient, completedTask.getId()); params.clear(); - params.put("where", "(status = 'any' AND processDefinitionName = 'Adhoc Activiti Process' AND processInstanceId='" + processInstance.getId() +"')"); + params.put("where", "(status = 'any' AND processDefinitionName = 'Adhoc Activiti Process' AND processId='" + processInstance.getId() +"')"); assertTasksPresentInTaskQuery(params, tasksClient, activeTask.getId(), completedTask.getId()); // Due date filtering @@ -1167,6 +1213,38 @@ public class TaskWorkflowApiTest extends EnterpriseWorkflowTestApi params.clear(); params.put("where", "(status = 'any' AND startedAt = '" + ISO8601DateFormat.format(taskCreated.getTime()) +"')"); assertTasksPresentInTaskQuery(params, tasksClient, completedTask.getId(), activeTask.getId()); + + params.clear(); + params.put("where", "(variables/numberVar > 'd:int 5')"); + assertTasksPresentInTaskQuery(params, tasksClient, activeTask.getId()); + + params.clear(); + params.put("where", "(variables/numberVar > 'd:int 10')"); + assertEquals(0, getResultSizeForTaskQuery(params, tasksClient)); + + params.clear(); + params.put("where", "(variables/numberVar >= 'd_int 10')"); + assertTasksPresentInTaskQuery(params, tasksClient, activeTask.getId()); + + params.clear(); + params.put("where", "(variables/numberVar >= 'd:int 11')"); + assertEquals(0, getResultSizeForTaskQuery(params, tasksClient)); + + params.clear(); + params.put("where", "(variables/numberVar <= 'd:int 10')"); + assertTasksPresentInTaskQuery(params, tasksClient, activeTask.getId()); + + params.clear(); + params.put("where", "(variables/numberVar <= 'd:int 9')"); + assertEquals(0, getResultSizeForTaskQuery(params, tasksClient)); + + params.clear(); + params.put("where", "(variables/numberVar < 'd_int 15')"); + assertTasksPresentInTaskQuery(params, tasksClient, activeTask.getId()); + + params.clear(); + params.put("where", "(variables/numberVar < 'd:int 10')"); + assertEquals(0, getResultSizeForTaskQuery(params, tasksClient)); } @Test @@ -1240,7 +1318,7 @@ public class TaskWorkflowApiTest extends EnterpriseWorkflowTestApi TasksClient tasksClient = publicApiClient.tasksClient(); Map params = new HashMap(); - params.put("processInstanceId", processInstance.getId()); + params.put("processId", processInstance.getId()); JSONObject resultingTasks = tasksClient.findTasks(params); assertNotNull(resultingTasks); @@ -1864,6 +1942,64 @@ public class TaskWorkflowApiTest extends EnterpriseWorkflowTestApi } } + @Test + @SuppressWarnings("unchecked") + public void testCreateTaskVariablesPresentInModel() throws Exception + { + RequestContext requestContext = initApiClientWithTestUser(); + + ProcessInstance processInstance = startAdhocProcess(requestContext.getRunAsUser(), requestContext.getNetworkId(), null); + try + { + Task task = activitiProcessEngine.getTaskService().createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); + assertNotNull(task); + + Map actualLocalVariables = activitiProcessEngine.getTaskService().getVariablesLocal(task.getId()); + Map actualGlobalVariables = activitiProcessEngine.getRuntimeService().getVariables(processInstance.getId()); + assertEquals(5, actualGlobalVariables.size()); + assertEquals(7, actualLocalVariables.size()); + + // Update a global value that is present in the model with type given + JSONArray variablesArray = new JSONArray(); + JSONObject variableBody = new JSONObject(); + variableBody.put("name", "bpm_percentComplete"); + variableBody.put("value", 20); + variableBody.put("type", "d:int"); + variableBody.put("scope", "global"); + variablesArray.add(variableBody); + variableBody = new JSONObject(); + variableBody.put("name", "bpm_workflowPriority"); + variableBody.put("value", 50); + variableBody.put("type", "d:int"); + variableBody.put("scope", "local"); + variablesArray.add(variableBody); + + TasksClient tasksClient = publicApiClient.tasksClient(); + JSONObject result = tasksClient.createTaskVariables(task.getId(), variablesArray); + assertNotNull(result); + JSONObject resultObject = (JSONObject) result.get("list"); + JSONArray resultList = (JSONArray) resultObject.get("entries"); + assertEquals(2, resultList.size()); + JSONObject firstResultObject = (JSONObject) ((JSONObject) resultList.get(0)).get("entry"); + assertEquals("bpm_percentComplete", firstResultObject.get("name")); + assertEquals(20L, firstResultObject.get("value")); + assertEquals("d:int", firstResultObject.get("type")); + assertEquals("global", firstResultObject.get("scope")); + assertEquals(20, activitiProcessEngine.getRuntimeService().getVariable(processInstance.getId(), "bpm_percentComplete")); + + JSONObject secondResultObject = (JSONObject) ((JSONObject) resultList.get(1)).get("entry"); + assertEquals("bpm_workflowPriority", secondResultObject.get("name")); + assertEquals(50L, secondResultObject.get("value")); + assertEquals("d:int", secondResultObject.get("type")); + assertEquals("local", secondResultObject.get("scope")); + assertEquals(50, activitiProcessEngine.getTaskService().getVariable(task.getId(), "bpm_workflowPriority")); + } + finally + { + cleanupProcessInstance(processInstance); + } + } + @Test public void testGetTaskVariablesRawVariableTypes() throws Exception { @@ -2244,16 +2380,8 @@ public class TaskWorkflowApiTest extends EnterpriseWorkflowTestApi // Check well-known properties and their types - // Validate bpm:description - JSONObject modelEntry = modelFieldsByName.get("bpm_description"); - assertNotNull(modelEntry); - assertEquals("Description", modelEntry.get("title")); - assertEquals("{http://www.alfresco.org/model/bpm/1.0}description", modelEntry.get("qualifiedName")); - assertEquals("d:text", modelEntry.get("dataType")); - assertFalse((Boolean)modelEntry.get("required")); - - // Validate bpm:description - modelEntry = modelFieldsByName.get("bpm_completionDate"); + // Validate bpm:completionDate + JSONObject modelEntry = modelFieldsByName.get("bpm_completionDate"); assertNotNull(modelEntry); assertEquals("Completion Date", modelEntry.get("title")); assertEquals("{http://www.alfresco.org/model/bpm/1.0}completionDate", modelEntry.get("qualifiedName")); @@ -2268,15 +2396,6 @@ public class TaskWorkflowApiTest extends EnterpriseWorkflowTestApi assertEquals("d:text", modelEntry.get("dataType")); assertFalse((Boolean)modelEntry.get("required")); - // Validate bpm:priority - modelEntry = modelFieldsByName.get("bpm_priority"); - assertNotNull(modelEntry); - assertEquals("Priority", modelEntry.get("title")); - assertEquals("{http://www.alfresco.org/model/bpm/1.0}priority", modelEntry.get("qualifiedName")); - assertEquals("d:int", modelEntry.get("dataType")); - assertEquals("2", modelEntry.get("defaultValue")); - assertTrue((Boolean)modelEntry.get("required")); - // Validate bpm:package modelEntry = modelFieldsByName.get("bpm_package"); assertNotNull(modelEntry); @@ -2322,7 +2441,7 @@ public class TaskWorkflowApiTest extends EnterpriseWorkflowTestApi JSONObject entryJSON = (JSONObject) entryObjectJSON.get("entry"); if (entryJSON.get("name").equals("Test Doc1")) { doc1Found = true; - assertEquals(docNodeRefs[0].toString(), entryJSON.get("id")); + assertEquals(docNodeRefs[0].getId(), entryJSON.get("id")); assertEquals("Test Doc1", entryJSON.get("name")); assertEquals("Test Doc1 Title", entryJSON.get("title")); assertEquals("Test Doc1 Description", entryJSON.get("description")); @@ -2334,7 +2453,7 @@ public class TaskWorkflowApiTest extends EnterpriseWorkflowTestApi assertNotNull(entryJSON.get("mimeType")); } else { doc2Found = true; - assertEquals(docNodeRefs[1].toString(), entryJSON.get("id")); + assertEquals(docNodeRefs[1].getId(), entryJSON.get("id")); assertEquals("Test Doc2", entryJSON.get("name")); assertEquals("Test Doc2 Title", entryJSON.get("title")); assertEquals("Test Doc2 Description", entryJSON.get("description")); @@ -2430,27 +2549,4 @@ public class TaskWorkflowApiTest extends EnterpriseWorkflowTestApi log("Error while cleaning up process instance"); } } - - protected TestNetwork getOtherNetwork(String usedNetworkId) throws Exception { - Iterator networkIt = getTestFixture().getNetworksIt(); - while(networkIt.hasNext()) { - TestNetwork network = networkIt.next(); - if(!usedNetworkId.equals(network.getId())) { - return network; - } - } - fail("Need more than one network to test permissions"); - return null; - } - - protected TestPerson getOtherPersonInNetwork(String usedPerson, String networkId) throws Exception { - TestNetwork usedNetwork = getTestFixture().getNetwork(networkId); - for(TestPerson person : usedNetwork.getPeople()) { - if(!person.getId().equals(usedPerson)) { - return person; - } - } - fail("Network doesn't have additonal users, cannot perform test"); - return null; - } } diff --git a/source/test-java/org/alfresco/rest/workflow/api/tests/WorkflowApiClient.java b/source/test-java/org/alfresco/rest/workflow/api/tests/WorkflowApiClient.java index ac4d34c33b..6e2e558aec 100644 --- a/source/test-java/org/alfresco/rest/workflow/api/tests/WorkflowApiClient.java +++ b/source/test-java/org/alfresco/rest/workflow/api/tests/WorkflowApiClient.java @@ -16,6 +16,7 @@ import org.alfresco.rest.workflow.api.model.Deployment; import org.alfresco.rest.workflow.api.model.ProcessDefinition; import org.alfresco.rest.workflow.api.model.ProcessInfo; import org.apache.commons.lang.StringUtils; +import org.json.simple.JSONArray; import org.json.simple.JSONObject; public class WorkflowApiClient extends PublicApiClient @@ -143,6 +144,20 @@ public class WorkflowApiClient extends PublicApiClient return ProcessesParser.INSTANCE.parseEntry(entry); } + public JSONObject getTasks(String processInstanceId, Map params) throws PublicApiException + { + HttpResponse response = getAll("processes", processInstanceId, "tasks", null, params, "Failed to get task instances of processInstanceId " + processInstanceId); + JSONObject list = (JSONObject) response.getJsonResponse().get("list"); + return list; + } + + public JSONObject getActivities(String processInstanceId, Map params) throws PublicApiException + { + HttpResponse response = getAll("processes", processInstanceId, "activities", null, params, "Failed to get activity instances of processInstanceId " + processInstanceId); + JSONObject list = (JSONObject) response.getJsonResponse().get("list"); + return list; + } + public JSONObject findProcessItems(String processInstanceId) throws PublicApiException { HttpResponse response = getAll("processes", processInstanceId, "items", null, null, @@ -159,6 +174,12 @@ public class WorkflowApiClient extends PublicApiClient return list; } + public JSONObject createVariables(String processId, JSONArray variables) throws PublicApiException + { + HttpResponse response = create("processes", processId, "variables", null, variables.toJSONString(), "Failed to create variables"); + return response.getJsonResponse(); + } + public JSONObject updateVariable(String processId, String variableName, JSONObject variable) throws PublicApiException { HttpResponse response = update("processes", processId, "variables", variableName, variable.toJSONString(), "Failed to update variable"); @@ -170,6 +191,11 @@ public class WorkflowApiClient extends PublicApiClient remove("processes", processId, "variables", variableName, "Failed to delete variable"); } + public void addProcessItem(String processId, String body) throws PublicApiException + { + create("processes", processId, "items", null, body, "Failed to add item"); + } + public void deleteProcessItem(String processId, String itemId) throws PublicApiException { remove("processes", processId, "items", itemId, "Failed to delete item"); @@ -234,6 +260,12 @@ public class WorkflowApiClient extends PublicApiClient return findTaskVariables(taskId, null); } + public JSONObject createTaskVariables(String taskId, JSONArray variables) throws PublicApiException + { + HttpResponse response = create("tasks", taskId, "variables", null, variables.toJSONString(), "Failed to create task variables"); + return response.getJsonResponse(); + } + public JSONObject updateTaskVariable(String taskId, String variableName, JSONObject variable) throws PublicApiException { HttpResponse response = update("tasks", taskId, "variables", variableName, variable.toJSONString(), "Failed to update task variable"); diff --git a/source/test-java/org/alfresco/rest/workflow/api/tests/WorkflowApiHttpClient.java b/source/test-java/org/alfresco/rest/workflow/api/tests/WorkflowApiHttpClient.java new file mode 100644 index 0000000000..4b71072913 --- /dev/null +++ b/source/test-java/org/alfresco/rest/workflow/api/tests/WorkflowApiHttpClient.java @@ -0,0 +1,14 @@ +package org.alfresco.rest.workflow.api.tests; + +import org.alfresco.rest.api.tests.client.AuthenticatedHttp; +import org.alfresco.rest.api.tests.client.PublicApiHttpClient; + +public class WorkflowApiHttpClient extends PublicApiHttpClient +{ + public WorkflowApiHttpClient(String host, int port, String contextPath, String servletName, AuthenticatedHttp authenticatedHttp) + { + super(host, port, contextPath, servletName, authenticatedHttp); + apiName = "workflow"; + } + +}