diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 132aec20..6e307d49 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -77,9 +77,9 @@ jobs:
github_token: ${{ secrets.GITHUB_TOKEN }}
detailed_summary: true
- name: Publish
- if: ${{ startsWith(github.ref, 'refs/heads/master') || startsWith(github.ref, 'refs/heads/release') }}"
+ if: ${{ startsWith(github.ref, 'refs/heads/master') || startsWith(github.ref, 'refs/heads/release') }}
uses: gradle/gradle-build-action@v2.4.2
with:
arguments: >-
--info -PsigningKeyId=DF8285F0
- :alfresco:${{ matrix.alfresco_version }}:publish
\ No newline at end of file
+ :alfresco:${{ matrix.alfresco_version }}:publish
diff --git a/.github/workflows/website-publish.yml b/.github/workflows/website-publish.yml
new file mode 100644
index 00000000..4f6c3f7d
--- /dev/null
+++ b/.github/workflows/website-publish.yml
@@ -0,0 +1,51 @@
+name: 'Publish website'
+on:
+ workflow_dispatch:
+env:
+ BRANCH_NAME: ${{ github.ref_name }}
+ ALFRESCO_NEXUS_USERNAME: ${{ secrets.ALFRESCO_NEXUS_USERNAME }}
+ ALFRESCO_NEXUS_PASSWORD: ${{ secrets.ALFRESCO_NEXUS_PASSWORD }}
+jobs:
+ alfred-api-docs:
+ if: ${{ startsWith(github.ref, 'refs/heads/master') || startsWith(github.ref, 'refs/heads/release') }}
+ runs-on: ubuntu-latest
+ steps:
+ - name: Check out
+ uses: actions/checkout@v4
+ - name: Login to Docker
+ uses: docker/login-action@v2
+ with:
+ registry: private.docker.xenit.eu
+ username: ${{ secrets.CLOUDSMITH_USER }}
+ password: ${{ secrets.CLOUDSMITH_APIKEY }}
+ - name: Setup Java
+ uses: actions/setup-java@v4
+ with:
+ distribution: 'temurin'
+ java-version: '17'
+ - name: Setup Gradle
+ uses: gradle/actions/setup-gradle@v3
+ - name: Build website
+ run: ./gradlew :docs:buildWebsiteScript --info
+ - name: Upload website artifact
+ if: success()
+ uses: actions/upload-artifact@v3
+ with:
+ name: website-alfred-api.tar.gz
+ path: /home/runner/work/alfred-api/docs/build/website-*.tar.gz
+ retention-days: 2
+ - name: Write ssh key to file
+ env:
+ RAW_KEY: ${{ secrets.A2_HOSTING_SSH_KEY }}
+ run: |
+ mkdir -p /home/runner/.ssh/
+ echo "$RAW_KEY" > /home/runner/.ssh/a2hostingKey
+ chmod 600 /home/runner/.ssh/a2hostingKey
+ - name: Deploy tar to webhost
+ env:
+ GRADLE_OPTS: >-
+ -Dorg.gradle.project.webHostAddress=nl1-ts102.a2hosting.com
+ -Dorg.gradle.project.webHostPort=7822
+ -Dorg.gradle.project.webHostUser=xeniteu
+ -Dorg.gradle.project.webHostSshKey=/home/runner/.ssh/a2hostingKey
+ run: ./gradlew :docs:deployWebsiteToWebHost --info
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e3563e0c..beb1f46a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,10 @@
# Alfred API - Changelog
+## 5.0.4 (Unreleased)
+
+### Fixed
+* [ALFREDAPI-552](https://xenitsupport.jira.com/browse/ALFREDAPI-552) Make swagger spec Open Api v2 compliant
+
## 5.0.3 (2024-06-17)
### Fixed
diff --git a/build.gradle b/build.gradle
index 2adcd545..6d1cca5a 100644
--- a/build.gradle
+++ b/build.gradle
@@ -4,8 +4,8 @@ plugins {
id 'eu.xenit.de' version '3.1.0' apply false
id 'eu.xenit.amp' version '1.1.0' apply false
id 'eu.xenit.alfresco' version '1.1.0' apply false
- id 'eu.xenit.docker-alfresco' version '5.3.1' apply false
- id 'eu.xenit.docker-compose' version '5.3.1' apply false
+ id 'eu.xenit.docker-alfresco' version '5.5.0' apply false
+ id 'eu.xenit.docker-compose' version '5.5.0' apply false
id 'eu.xenit.alfresco-remote-testrunner' version '2.0.1' apply false
}
diff --git a/docs/README.md b/docs/README.md
index f230edc1..fd01a5f7 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -1,5 +1,7 @@
# Documentation Alfred API
+**Process is automated through GHA. Trigger the appropriate workflow.**
+
## Generate website
This directory generates the product documentation website at https://docs.xenit.eu/alfred-api/
diff --git a/docs/build-website.sh b/docs/build-website.sh
index f08cfb5e..68e1ec78 100755
--- a/docs/build-website.sh
+++ b/docs/build-website.sh
@@ -62,7 +62,7 @@ build_javadoc() {
local alfredapidir=".."
pushd "$alfredapidir"
- ./gradlew clean :apix-interface:javadoc
+ ./gradlew :apix-interface:javadoc
popd
local outputdir="build/website/$productName/"
diff --git a/docs/build.gradle b/docs/build.gradle
new file mode 100644
index 00000000..a727b0ce
--- /dev/null
+++ b/docs/build.gradle
@@ -0,0 +1,54 @@
+import java.text.SimpleDateFormat
+
+plugins {
+ id "base"
+ id "org.hidetake.ssh" version "2.11.2"
+}
+
+def date = new Date()
+def format = new SimpleDateFormat("yyyy-MM-dd")
+
+tasks.register("buildWebsiteScript", Exec) {
+ setGroup "documentation"
+ commandLine 'bash', './build-website.sh'
+}
+assemble.dependsOn buildWebsiteScript
+
+remotes {
+ a2hosting {
+ host = project.findProperty("webHostAddress")
+ port = project.hasProperty("webHostPort") ? (project.getProperty("webHostPort") as Integer) : 22
+ user = project.findProperty("webHostUser")
+ def keyFileLocation = project.hasProperty("webHostSshKey") ? project.getProperty("webHostSshKey") : "No Key"
+ identity = file(keyFileLocation)
+ }
+}
+
+ssh.settings {
+ knownHosts = allowAnyHosts
+}
+
+tasks.register("deployWebsiteToWebHost") {
+ setGroup "publishing"
+ dependsOn buildWebsiteScript
+ doLast {
+ ssh.run {
+ session(remotes.a2hosting) {
+ def remoteHomeDir = execute 'pwd'
+ def websiteDir = "${remoteHomeDir}/docs.xenit.eu"
+ def today = format.format(date)
+ def websiteTar = "website-alfred-api_${today}.tar.gz"
+ def backupDir = "alfred-api_${today}_back"
+ // Upload
+ put from: file("${layout.buildDirectory.get()}/${websiteTar}"), into: "${websiteDir}/"
+ // Backup old website component to tar
+ execute "mv ${websiteDir}/alfred-api ${remoteHomeDir}/${backupDir}"
+ execute "tar vczf alfred-api_${today}_back.tar.gz -C ${remoteHomeDir} ${backupDir}"
+ // Deploy new website component from tar
+ execute "tar vxzf ${websiteDir}/${websiteTar} -C ${websiteDir}"
+ // Cleanup uncompressed backup
+ remove "${remoteHomeDir}/${backupDir}"
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/docs/swagger-ui/swagger.json b/docs/swagger-ui/swagger.json
index e22e46ab..9fefa92d 100644
--- a/docs/swagger-ui/swagger.json
+++ b/docs/swagger-ui/swagger.json
@@ -1,9 +1,9 @@
{
"swagger": "2.0",
"info": {
- "description": "This is the swagger specification for Api-X REST API\n\nExamples can be found at: https://docs.xenit.eu/alfred-api",
- "version": "5.0.0",
- "title": "Api-X REST API",
+ "description": "This is the swagger specification for Alfred REST API\n\nExamples can be found at: https://docs.xenit.eu/alfred-api",
+ "version": "5.0.3",
+ "title": "Alfred REST Api",
"contact": {
"name": "XeniT",
"url": "http://www.xenit.eu",
@@ -288,10 +288,10 @@
{
"name": "fields",
"in": "query",
- "description": "Comma separated field names to include.",
+ "description": "Comma separated field names to include. Example: \"content,nodeRef\"",
"required": true,
"type": "string",
- "default": "content,nodeRef",
+ "default": "nodeRef",
"enum": [
"content",
"nodeRef",
@@ -503,7 +503,7 @@
],
"summary": "The Swagger Spec for Alfred API",
"description": "",
- "operationId": "execute",
+ "operationId": "v1.getDocumentation",
"parameters": [],
"responses": {
"200": {
@@ -536,7 +536,7 @@
],
"summary": "Creates or copies a node",
"description": "Example of POST body:\n\n```\nPOST /apix/v1/nodes\n{\n\"parent\" : \"workspace://SpacesStore/d5dac928-e581-4507-9be7-9a2416adc318\", \n\"name\" : \"mydocument.txt\", \n\"type\" : \"{http://www.alfresco.org/model/content/1.0}content\", \n\"properties\" : {\n \"{namespace}property1\": [\n \"string\"\n ],\n \"{namespace}property2\": [\n \"string\"\n ],\n \"{namespace}property3\": [\n \"string\"\n ]\n}, \n\"aspectsToAdd\" : [\n \"{namespace}aspect1\"\n], \n\"aspectsToRemove\" : [\n \"{namespace}aspect1\"\n], \n\"copyFrom\" : \"workspace://SpacesStore/f0d15919-3841-4170-807f-b81d2ebdeb80\", \n}\n```\n\"aspectsToRemove\" is only relevant when copying a node.\n",
- "operationId": "createNode",
+ "operationId": "v1.createNode",
"parameters": [
{
"in": "body",
@@ -1431,6 +1431,7 @@
"type": "file"
}
],
+ "consumes": ["multipart/form-data"],
"responses": {
"200": {
"description": "Success"
@@ -1658,7 +1659,7 @@
],
"summary": "Retrieve current user's permissions for a node",
"description": "Returns a key-value map of permissions keys to a value of 'DENY' or 'ALLOW'. Possible keys are: Read, Write, Delete, CreateChildren, ReadPermissions, ChangePermissions, or custom permissions",
- "operationId": "getPermissions",
+ "operationId": "v1.getPermissions",
"parameters": [
{
"name": "space",
@@ -1828,7 +1829,7 @@
],
"summary": "Returns person information",
"description": "",
- "operationId": "getPerson",
+ "operationId": "v1.getPerson",
"parameters": [
{
"name": "space",
@@ -1866,7 +1867,7 @@
],
"summary": "Return the definition of a property",
"description": "",
- "operationId": "getPropertyDefinition",
+ "operationId": "v1.getPropertyDefinition",
"parameters": [
{
"name": "qname",
@@ -1898,7 +1899,7 @@
],
"summary": "Performs a search for nodes",
"description": "# Request components\n\n## query\nObject containing subcomponents that build the requested query.\nInfo about the Search query syntax can be found here: https://docs.xenit.eu/alfred-api/user/rest-api\n### special search terms:\n- type: searches for nodes of that type (for example: \"type\" : \"cm:content\")\n- aspect: searches for nodes with that aspect (for example: \"aspect\" : \"cm:titled\")\n- noderef: searches the node with that noderef (for example: \"noderef\" : \"workspace://SpacesStore/f0d15919-3841-4170-807f-b81d2ebdeb80\")\n- parent: searches the nodes with that parent (for example: \"parent\" : \"workspace://SpacesStore/f0d15919-3841-4170-807f-b81d2ebdeb80\")\n- path: searches the nodes with that path (for example: \"path\" : \"/\")\n- category: searches the nodes with that category (for example: \"category\" : \"workspace://SpacesStore/f0d15919-3841-4170-807f-b81d2ebdeb80\")\n- text: searches the nodes with content containing that text (for example: \"text\" : \"this text\")\n- all: searches the nodes with content, cm:name, cm:creator, cm:modifier or cm:author containing the value (for example: \"all\" : \"search term\")\n- isunset: searches the nodes with where the value of the property is not set (for example: \"isunset\" : \"cm:author\")\n- isnull: searches the nodes with where the value of the property is null (for example: \"isnull\" : \"cm:author\")\n- isnotnull: searches the nodes with where the value of the property is not null (for example: \"isnotnull\" : \"cm:author\")\n- exists: searches the nodes that have the property (for example: \"exists\" : \"cm:author\")\n\n## paging\n`Optional`\n\nOptions to skip over results starting from the top of the result and to limit the total number of results.\n\n## facets\n`Optional`\n\nOptions to enable, limit the total amount, minimum number of hits and customize input of facets.\n\nNote: facets with 0 hits are not returned in the result\n\n## orderBy\n`Optional`\n\nOptions to select the property to order by and the direction of sorting.\n\n## consistency\n`Optional`\n\nOption to request specific consistency\n\n## locale\n`Optional`\n\nOptions to request specific locale and encoding options\n\n## workspace\n`Optional`\n\nOptions to change the target alfresco workspace\n\n## highlight\n`5.2 and up`\n\n`Optional`\n\nOptions to change the highlight configuration.\nMinimal requirement is the `fields` array, which takes object containing at least the `field` property. Each list element specifies a property on which higlighting needs to be applied. When no fields are specified, the call defaults to `cm:content` as field.\nFull documentation can be found on the alfresco [documentation](https://docs.alfresco.com/5.2/concepts/search-api-highlight.html) page.\n\n# Examples\n\nSearch for the first 10 nodes in the `cm:content` namespace:\n```json\n{\n \"query\": {\"type\":\"{http://www.alfresco.org/model/content/1.0}content\"},\n \"paging\": {\n \"limit\": 10,\n \"skip\": 0\n },\n \"facets\": {\n \"enabled\": false\n }\n}\n```\n\nSearch for all nodes with the term 'budget' in the `cm:content` property (fulltext), and show two highlighted hits with delimiter \\\\:\n```json\n{\n \"query\": {\n \"property\": {\n \"exact\": false,\n \"name\": \"cm:content\",\n \"value\": \"budget\"\n }\n },\n \"highlight\":{\n \"prefix\":\"\",\n \"postfix\":\"\",\n \"snippetCount\":2,\n\t\t\"fields\":[{\"field\":\"cm:content\"}]\n }\n}\n```",
- "operationId": "execute",
+ "operationId": "v1.search",
"parameters": [
{
"in": "body",
@@ -2658,7 +2659,7 @@
],
"summary": "Creates or copies a node",
"description": "",
- "operationId": "createNode",
+ "operationId": "v2.createNode",
"parameters": [
{
"in": "body",
@@ -2746,7 +2747,7 @@
],
"summary": "Retrieve current user's permissions for a node",
"description": "Returns a key-value map of permissions keys to a value of 'DENY' or 'ALLOW'. Possible keys are: Read, Write, Delete, CreateChildren, ReadPermissions, ChangePermissions, or custom permissions",
- "operationId": "getPermissions",
+ "operationId": "v2.getPermissions",
"parameters": [
{
"name": "space",
@@ -2862,7 +2863,7 @@
],
"summary": "Returns person information",
"description": "",
- "operationId": "getPerson",
+ "operationId": "v2.getPerson",
"parameters": [
{
"name": "space",
@@ -3160,12 +3161,10 @@
"type": "object",
"properties": {
"comment": {
- "type": "string",
- "readOnly": true
+ "type": "string"
},
"majorVersion": {
"type": "boolean",
- "readOnly": true,
"default": false
}
}
@@ -3233,7 +3232,11 @@
},
"type": {
"description": "Defaults to cm:content",
- "$ref": "#/definitions/QName"
+ "allOf": [
+ {
+ "$ref": "#/definitions/QName"
+ }
+ ]
}
}
},
@@ -3668,20 +3671,16 @@
],
"properties": {
"parent": {
- "type": "string",
- "readOnly": true
+ "type": "string"
},
"name": {
- "type": "string",
- "readOnly": true
+ "type": "string"
},
"type": {
- "type": "string",
- "readOnly": true
+ "type": "string"
},
"properties": {
"type": "object",
- "readOnly": true,
"additionalProperties": {
"type": "array",
"items": {
@@ -3691,21 +3690,18 @@
},
"aspectsToAdd": {
"type": "array",
- "readOnly": true,
"items": {
"$ref": "#/definitions/QName"
}
},
"aspectsToRemove": {
"type": "array",
- "readOnly": true,
"items": {
"$ref": "#/definitions/QName"
}
},
"copyFrom": {
- "type": "string",
- "readOnly": true
+ "type": "string"
}
}
},
@@ -3763,8 +3759,7 @@
],
"properties": {
"parent": {
- "type": "string",
- "readOnly": true
+ "type": "string"
}
}
},
@@ -3775,13 +3770,15 @@
],
"properties": {
"original": {
- "readOnly": true,
"$ref": "#/definitions/NodeRef"
},
"destinationFolder": {
"description": "Optional, if not specified uses the original node's folder. If current user does not have permissions for this folder, the working copy is created in the user's home folder",
- "readOnly": true,
- "$ref": "#/definitions/NodeRef"
+ "allOf": [
+ {
+ "$ref": "#/definitions/NodeRef"
+ }
+ ]
}
}
},
@@ -3804,7 +3801,6 @@
"properties": {
"users": {
"type": "array",
- "readOnly": true,
"items": {
"type": "string"
}
@@ -4172,7 +4168,6 @@
"properties": {
"subgroups": {
"type": "array",
- "readOnly": true,
"items": {
"type": "string"
}
diff --git a/settings.gradle b/settings.gradle
index 4a170df9..00c6e31d 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -7,6 +7,7 @@ include ':alfresco'
include ':apix-docker'
include ':apix-integrationtests'
include ':apix-integrationtests:model-amp'
+include ':docs'
for (String version : ['7.0', '7.1', '7.2', '7.3', '7.4']) {
def shortVersion = version.replaceAll('\\.', '')