diff --git a/.gitignore b/.gitignore
index 5c33d5d87..cac4e5c65 100644
--- a/.gitignore
+++ b/.gitignore
@@ -42,4 +42,7 @@ hs_err_pid*
# node.js / frontend
node_modules/
dist/
+cache/
+/frontend/datasafe-ui/package-lock.json
+/datasafe-rest-impl/ROOT_BUCKET/
diff --git a/datasafe-encryption/datasafe-encryption-impl/pom.xml b/datasafe-encryption/datasafe-encryption-impl/pom.xml
index dc95d9b60..fac2cbe00 100644
--- a/datasafe-encryption/datasafe-encryption-impl/pom.xml
+++ b/datasafe-encryption/datasafe-encryption-impl/pom.xml
@@ -98,24 +98,29 @@
test-jar
test
+
com.fasterxml.jackson.core
jackson-databind
+ ${jackson.version}
test
com.fasterxml.jackson.dataformat
jackson-dataformat-yaml
+ ${jackson.version}
test
com.fasterxml.jackson.core
jackson-annotations
+ ${jackson.version}
test
com.fasterxml.jackson.core
jackson-core
+ ${jackson.version}
test
diff --git a/datasafe-rest-impl/1.createDockerimage.sh b/datasafe-rest-impl/1.createDockerimage.sh
index ef7f3d866..539b68814 100755
--- a/datasafe-rest-impl/1.createDockerimage.sh
+++ b/datasafe-rest-impl/1.createDockerimage.sh
@@ -13,4 +13,4 @@ npm install
ng build --deploy-url /static/ --base-href /static/
mv dist ../../datasafe-rest-impl/target/dist
cd ../../datasafe-rest-impl
-docker build . -t datasafe-rest-test:latest --build-arg JAR_FILE=datasafe-rest-impl-*.jar
+docker build . -t datasafe-rest-test:latest --build-arg JAR_FILE=datasafe-rest-impl.jar
diff --git a/datasafe-rest-impl/DEMO.md b/datasafe-rest-impl/DEMO.md
index 9c6cf7dfd..e9095bf9e 100644
--- a/datasafe-rest-impl/DEMO.md
+++ b/datasafe-rest-impl/DEMO.md
@@ -19,7 +19,29 @@ To **run** demo:
## Building and running demo
-### Building
+### Run it with Docker Compose
+The easiest way to run Datasafe Rest Application is using Docker Compose. By default, it works with filesystem with root
+directory `datasafe-rest-impl/ROOT_BUCKET`.
+Build it with:
+``` bash
+# build backend
+mvn clean package
+
+# build frontend
+cd frontend/datasafe-ui
+npm i
+ng build --deploy-url /static/ --base-href /static/
+mv dist ../../datasafe-rest-impl/target/dist
+
+# build image and start datasafe in docker
+docker compose up datasafe
+```
+
+Frontend is available at http://localhost:8080/static/index.html
+
+### Alternatively you can use shell scripts to build and run datasafe with different types of storages
+
+#### Building
- Build from sources
@@ -34,7 +56,7 @@ cd datasafe-rest-impl
docker pull adorsys/datasafe && docker tag adorsys/datasafe datasafe-rest-test:latest
```
-### Running
+#### Running
Run using local filesystem, all data will be stored in `target/ROOT_BUCKET` folder:
```bash
diff --git a/datasafe-rest-impl/Dockerfile b/datasafe-rest-impl/Dockerfile
index 272e36b06..e4aaeddf0 100644
--- a/datasafe-rest-impl/Dockerfile
+++ b/datasafe-rest-impl/Dockerfile
@@ -1,4 +1,4 @@
-FROM openjdk:8-jre-alpine
+FROM ibm-semeru-runtimes:open-21-jre
ARG JAR_FILE
ENV JAR_FILE ${JAR_FILE}
diff --git a/datasafe-rest-impl/compose.yml b/datasafe-rest-impl/compose.yml
new file mode 100644
index 000000000..84345fe71
--- /dev/null
+++ b/datasafe-rest-impl/compose.yml
@@ -0,0 +1,31 @@
+services:
+ datasafe:
+ build:
+ args:
+ JAR_FILE: "datasafe-rest-impl.jar"
+# image: adorsys/datasafe
+ environment:
+ EXPOSE_API_CREDS: true
+ DEFAULT_USER: username
+ DEFAULT_PASSWORD: password
+ USE_FILESYSTEM: file:///usr/app/ROOT_BUCKET
+ ports:
+ - 8080:8080
+ volumes:
+ - "./ROOT_BUCKET:/usr/app/ROOT_BUCKET"
+ db:
+ image: mysql:5.7
+ restart: always
+ environment:
+ MYSQL_DATABASE: 'test_db'
+ MYSQL_USER: 'test'
+ MYSQL_PASSWORD: 'test'
+ MYSQL_ROOT_PASSWORD: 'password'
+ ports:
+ - '3306:3306'
+ expose:
+ - '3306'
+ volumes:
+ - my-db:/var/lib/mysql
+volumes:
+ my-db:
diff --git a/datasafe-rest-impl/datasafe-deployment.yaml b/datasafe-rest-impl/datasafe-deployment.yaml
deleted file mode 100644
index 4fc6b48ac..000000000
--- a/datasafe-rest-impl/datasafe-deployment.yaml
+++ /dev/null
@@ -1,40 +0,0 @@
-kind: "DeploymentConfig"
-apiVersion: "v1"
-metadata:
- name: "datasafe-rest-service"
-spec:
- template:
- metadata:
- labels:
- name: "datasafe-rest-service"
- spec:
- containers:
- - name: "datasafe-rest-service"
- image: "datasafe-rest-service:latest"
- ports:
- - containerPort: 8080
- protocol: "TCP"
-
- replicas: 1
- triggers:
- - type: "ConfigChange"
- - type: "ImageChange"
- imageChangeParams:
- automatic: true
- containerNames:
- - "datasafe-rest-service"
- from:
- kind: "ImageStreamTag"
- name: "datasafe-rest-service:latest"
- - type: "ImageChange"
- imageChangeParams:
- automatic: true
- containerNames:
- - "datasafe-rest-service"
- from:
- kind: "ImageStreamTag"
- name: "datasafe:latest"
- strategy:
- type: "Rolling"
- paused: false
- revisionHistoryLimit: 2
\ No newline at end of file
diff --git a/datasafe-rest-impl/datasafe-image.yaml b/datasafe-rest-impl/datasafe-image.yaml
deleted file mode 100644
index bc6450fff..000000000
--- a/datasafe-rest-impl/datasafe-image.yaml
+++ /dev/null
@@ -1,6 +0,0 @@
-apiVersion: "v1"
-kind: "ImageStream"
-metadata:
- name: "datasafe-rest-service"
-spec:
- dockerImageRepository: "datasafe-rest-service"
\ No newline at end of file
diff --git a/datasafe-rest-impl/datasafe.postman_collection.json b/datasafe-rest-impl/datasafe.postman_collection.json
index 524b1dcfb..47698332a 100644
--- a/datasafe-rest-impl/datasafe.postman_collection.json
+++ b/datasafe-rest-impl/datasafe.postman_collection.json
@@ -1,690 +1,1170 @@
{
- "info": {
- "_postman_id": "54d63510-b402-4a2c-b518-50e79a04d569",
- "name": "Datasafe",
- "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
- },
- "item": [
- {
- "name": "authenticate",
- "request": {
- "method": "POST",
- "header": [
- {
- "key": "Content-Type",
- "name": "Content-Type",
- "value": "application/json",
- "type": "text"
- }
- ],
- "body": {
- "mode": "raw",
- "raw": "{\"userName\":\"username\",\"password\":\"password\"}"
- },
- "url": {
- "raw": "{{host}}/api/authenticate",
- "host": [
- "{{host}}"
- ],
- "path": [
- "api",
- "authenticate"
- ]
- }
- },
- "response": []
- },
- {
- "name": "create user",
- "request": {
- "method": "PUT",
- "header": [
- {
- "key": "Content-Type",
- "name": "Content-Type",
- "value": "application/json",
- "type": "text"
- },
- {
- "key": "token",
- "value": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJpc3MiOiJzZWN1cmUtYXBpIiwiYXVkIjoic2VjdXJlLWFwcCIsInN1YiI6InVzZXJuYW1lIiwiZXhwIjoxNTYxMzA0MTA4LCJyb2wiOlsiUk9MRV9VU0VSIl19.Ungu5hob8mbwb7GfWfvqj1VsrNNv1pN6UEMJYJHSAHBicRlsYVbRAE7Ju_N1nnjdoZ6TSs4hireff92SkXvesQ",
- "type": "text"
- }
- ],
- "body": {
- "mode": "raw",
- "raw": "{\n\t\"userName\":\"ver\",\n\t\"password\":\"ver\"\n}"
- },
- "url": {
- "raw": "{{host}}/user",
- "host": [
- "{{host}}"
- ],
- "path": [
- "user"
- ]
- }
- },
- "response": []
- },
- {
- "name": "delete user",
- "request": {
- "method": "DELETE",
- "header": [
- {
- "key": "Content-Type",
- "name": "Content-Type",
- "value": "application/json",
- "type": "text"
- },
- {
- "key": "user",
- "value": "Max",
- "type": "text"
- },
- {
- "key": "password",
- "value": "123",
- "type": "text"
- },
- {
- "key": "token",
- "value": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJpc3MiOiJzZWN1cmUtYXBpIiwiYXVkIjoic2VjdXJlLWFwcCIsInN1YiI6InVzZXJuYW1lIiwiZXhwIjoxNTYxMzA0MTA4LCJyb2wiOlsiUk9MRV9VU0VSIl19.Ungu5hob8mbwb7GfWfvqj1VsrNNv1pN6UEMJYJHSAHBicRlsYVbRAE7Ju_N1nnjdoZ6TSs4hireff92SkXvesQ",
- "type": "text"
- }
- ],
- "url": {
- "raw": "{{host}}/user/",
- "host": [
- "{{host}}"
- ],
- "path": [
- "user",
- ""
- ]
- }
- },
- "response": []
- },
- {
- "name": "store document",
- "request": {
- "method": "PUT",
- "header": [
- {
- "key": "Content-Type",
- "value": "application/octet-stream",
- "type": "text"
- },
- {
- "key": "user",
- "value": "ver",
- "type": "text"
- },
- {
- "key": "password",
- "value": "ver",
- "type": "text"
- },
- {
- "key": "token",
- "value": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJpc3MiOiJzZWN1cmUtYXBpIiwiYXVkIjoic2VjdXJlLWFwcCIsInN1YiI6InVzZXJuYW1lIiwiZXhwIjoxNTYxMzA0MTA4LCJyb2wiOlsiUk9MRV9VU0VSIl19.Ungu5hob8mbwb7GfWfvqj1VsrNNv1pN6UEMJYJHSAHBicRlsYVbRAE7Ju_N1nnjdoZ6TSs4hireff92SkXvesQ",
- "type": "text"
- }
- ],
- "body": {
- "mode": "file",
- "file": {
- "src": "/Users/maxim/tx/store/a.txt"
- }
- },
- "url": {
- "raw": "{{host}}/document/test11.txt",
- "host": [
- "{{host}}"
- ],
- "path": [
- "document",
- "test11.txt"
- ]
- }
- },
- "response": []
- },
- {
- "name": "list documents",
- "request": {
- "method": "GET",
- "header": [
- {
- "key": "user",
- "value": "vvv",
- "type": "text"
- },
- {
- "key": "password",
- "value": "vvv",
- "type": "text"
- },
- {
- "key": "token",
- "value": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJpc3MiOiJzZWN1cmUtYXBpIiwiYXVkIjoic2VjdXJlLWFwcCIsInN1YiI6InVzZXJuYW1lIiwiZXhwIjoxNTYxMzA0MTA4LCJyb2wiOlsiUk9MRV9VU0VSIl19.Ungu5hob8mbwb7GfWfvqj1VsrNNv1pN6UEMJYJHSAHBicRlsYVbRAE7Ju_N1nnjdoZ6TSs4hireff92SkXvesQ",
- "type": "text"
- }
- ],
- "url": {
- "raw": "{{host}}/documents/",
- "host": [
- "{{host}}"
- ],
- "path": [
- "documents",
- ""
- ]
- }
- },
- "response": []
- },
- {
- "name": "read document",
- "request": {
- "method": "GET",
- "header": [
- {
- "key": "Accept",
- "value": "application/octet-stream",
- "type": "text"
- },
- {
- "key": "user",
- "value": "ver",
- "type": "text"
- },
- {
- "key": "password",
- "value": "ver",
- "type": "text"
- },
- {
- "key": "token",
- "value": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJpc3MiOiJzZWN1cmUtYXBpIiwiYXVkIjoic2VjdXJlLWFwcCIsInN1YiI6InVzZXJuYW1lIiwiZXhwIjoxNTYxMzA0MTA4LCJyb2wiOlsiUk9MRV9VU0VSIl19.Ungu5hob8mbwb7GfWfvqj1VsrNNv1pN6UEMJYJHSAHBicRlsYVbRAE7Ju_N1nnjdoZ6TSs4hireff92SkXvesQ",
- "type": "text"
- }
- ],
- "url": {
- "raw": "{{host}}/document/test11.txt",
- "host": [
- "{{host}}"
- ],
- "path": [
- "document",
- "test11.txt"
- ]
- }
- },
- "response": [
- {
- "name": "read document",
- "originalRequest": {
- "method": "GET",
- "header": [
- {
- "key": "Accept",
- "value": "application/octet-stream",
- "type": "text"
- },
- {
- "key": "user",
- "value": "ddd",
- "type": "text"
- },
- {
- "key": "password",
- "value": "ddd",
- "type": "text"
- }
- ],
- "url": {
- "raw": "{{host}}/document/deep/path/test.txt",
- "host": [
- "{{host}}"
- ],
- "path": [
- "document",
- "deep",
- "path",
- "test.txt"
- ]
- }
- },
- "status": "OK",
- "code": 200,
- "_postman_previewlanguage": "plain",
- "header": [
- {
- "key": "Transfer-Encoding",
- "value": "chunked"
- },
- {
- "key": "Date",
- "value": "Fri, 24 May 2019 11:50:22 GMT"
- }
- ],
- "cookie": [],
- "body": "test"
- }
- ]
- },
- {
- "name": "delete document",
- "request": {
- "method": "DELETE",
- "header": [
- {
- "key": "user",
- "value": "iii",
- "type": "text"
- },
- {
- "key": "password",
- "value": "iii",
- "type": "text"
- },
- {
- "key": "token",
- "value": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJpc3MiOiJzZWN1cmUtYXBpIiwiYXVkIjoic2VjdXJlLWFwcCIsInN1YiI6InVzZXJuYW1lIiwiZXhwIjoxNTYxMzA0MTA4LCJyb2wiOlsiUk9MRV9VU0VSIl19.Ungu5hob8mbwb7GfWfvqj1VsrNNv1pN6UEMJYJHSAHBicRlsYVbRAE7Ju_N1nnjdoZ6TSs4hireff92SkXvesQ",
- "type": "text"
- }
- ],
- "url": {
- "raw": "{{host}}/document/deep/path/test2.txt",
- "host": [
- "{{host}}"
- ],
- "path": [
- "document",
- "deep",
- "path",
- "test2.txt"
- ]
- }
- },
- "response": []
- },
- {
- "name": "send to inbox",
- "request": {
- "method": "PUT",
- "header": [
- {
- "key": "Content-Type",
- "value": "application/octet-stream",
- "type": "text"
- },
- {
- "key": "user",
- "value": "ver",
- "type": "text"
- },
- {
- "key": "token",
- "value": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJpc3MiOiJzZWN1cmUtYXBpIiwiYXVkIjoic2VjdXJlLWFwcCIsInN1YiI6InVzZXJuYW1lIiwiZXhwIjoxNTYxMzA0MTA4LCJyb2wiOlsiUk9MRV9VU0VSIl19.Ungu5hob8mbwb7GfWfvqj1VsrNNv1pN6UEMJYJHSAHBicRlsYVbRAE7Ju_N1nnjdoZ6TSs4hireff92SkXvesQ",
- "type": "text"
- }
- ],
- "body": {
- "mode": "file",
- "file": {
- "src": "/Users/maxim/test.txt"
- }
- },
- "url": {
- "raw": "{{host}}/inbox/test2.txt",
- "host": [
- "{{host}}"
- ],
- "path": [
- "inbox",
- "test2.txt"
- ]
- }
- },
- "response": []
- },
- {
- "name": "inbox read",
- "request": {
- "method": "GET",
- "header": [
- {
- "key": "user",
- "value": "ver",
- "type": "text"
- },
- {
- "key": "password",
- "value": "ver",
- "type": "text"
- },
- {
- "key": "Accept",
- "value": "application/octet-stream",
- "type": "text"
- },
- {
- "key": "token",
- "value": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJpc3MiOiJzZWN1cmUtYXBpIiwiYXVkIjoic2VjdXJlLWFwcCIsInN1YiI6InVzZXJuYW1lIiwiZXhwIjoxNTYxMzA0MTA4LCJyb2wiOlsiUk9MRV9VU0VSIl19.Ungu5hob8mbwb7GfWfvqj1VsrNNv1pN6UEMJYJHSAHBicRlsYVbRAE7Ju_N1nnjdoZ6TSs4hireff92SkXvesQ",
- "type": "text"
- }
- ],
- "url": {
- "raw": "{{host}}/inbox/test2.txt",
- "host": [
- "{{host}}"
- ],
- "path": [
- "inbox",
- "test2.txt"
- ]
- }
- },
- "response": []
- },
- {
- "name": "inbox delete",
- "request": {
- "method": "DELETE",
- "header": [
- {
- "key": "user",
- "value": "iii",
- "type": "text"
- },
- {
- "key": "password",
- "value": "iii",
- "type": "text"
- },
- {
- "key": "token",
- "value": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJpc3MiOiJzZWN1cmUtYXBpIiwiYXVkIjoic2VjdXJlLWFwcCIsInN1YiI6InVzZXJuYW1lIiwiZXhwIjoxNTYxMzA0MTA4LCJyb2wiOlsiUk9MRV9VU0VSIl19.Ungu5hob8mbwb7GfWfvqj1VsrNNv1pN6UEMJYJHSAHBicRlsYVbRAE7Ju_N1nnjdoZ6TSs4hireff92SkXvesQ",
- "type": "text"
- }
- ],
- "url": {
- "raw": "{{host}}/inbox/test2.txt",
- "host": [
- "{{host}}"
- ],
- "path": [
- "inbox",
- "test2.txt"
- ]
- }
- },
- "response": []
- },
- {
- "name": "inbox list",
- "request": {
- "method": "GET",
- "header": [
- {
- "key": "user",
- "value": "ver",
- "type": "text"
- },
- {
- "key": "password",
- "value": "ver",
- "type": "text"
- },
- {
- "key": "Accept",
- "value": "application/json",
- "type": "text"
- },
- {
- "key": "token",
- "value": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJpc3MiOiJzZWN1cmUtYXBpIiwiYXVkIjoic2VjdXJlLWFwcCIsInN1YiI6InVzZXJuYW1lIiwiZXhwIjoxNTYxMzA0MTA4LCJyb2wiOlsiUk9MRV9VU0VSIl19.Ungu5hob8mbwb7GfWfvqj1VsrNNv1pN6UEMJYJHSAHBicRlsYVbRAE7Ju_N1nnjdoZ6TSs4hireff92SkXvesQ",
- "type": "text"
- }
- ],
- "url": {
- "raw": "{{host}}/inbox/",
- "host": [
- "{{host}}"
- ],
- "path": [
- "inbox",
- ""
- ]
- }
- },
- "response": []
- },
- {
- "name": "version list",
- "request": {
- "method": "GET",
- "header": [
- {
- "key": "user",
- "type": "text",
- "value": "ver"
- },
- {
- "key": "password",
- "type": "text",
- "value": "ver"
- },
- {
- "key": "token",
- "value": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJpc3MiOiJzZWN1cmUtYXBpIiwiYXVkIjoic2VjdXJlLWFwcCIsInN1YiI6InVzZXJuYW1lIiwiZXhwIjoxNTYxMzA0MTA4LCJyb2wiOlsiUk9MRV9VU0VSIl19.Ungu5hob8mbwb7GfWfvqj1VsrNNv1pN6UEMJYJHSAHBicRlsYVbRAE7Ju_N1nnjdoZ6TSs4hireff92SkXvesQ",
- "type": "text"
- },
- {
- "key": "Accept",
- "value": "application/json",
- "type": "text"
- },
- {
- "key": "Content-Type",
- "value": "application/json",
- "type": "text"
- }
- ],
- "url": {
- "raw": "{{host}}/versions/list/",
- "host": [
- "{{host}}"
- ],
- "path": [
- "versions",
- "list",
- ""
- ]
- }
- },
- "response": []
- },
- {
- "name": "versioned read",
- "request": {
- "method": "GET",
- "header": [
- {
- "key": "user",
- "value": "ver",
- "type": "text"
- },
- {
- "key": "password",
- "value": "ver",
- "type": "text"
- },
- {
- "key": "token",
- "value": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJpc3MiOiJzZWN1cmUtYXBpIiwiYXVkIjoic2VjdXJlLWFwcCIsInN1YiI6InVzZXJuYW1lIiwiZXhwIjoxNTYxMzA0MTA4LCJyb2wiOlsiUk9MRV9VU0VSIl19.Ungu5hob8mbwb7GfWfvqj1VsrNNv1pN6UEMJYJHSAHBicRlsYVbRAE7Ju_N1nnjdoZ6TSs4hireff92SkXvesQ",
- "type": "text"
- },
- {
- "key": "Accept",
- "value": "application/octet-stream",
- "type": "text"
- },
- {
- "key": "Content-Type",
- "value": "application/json",
- "type": "text"
- }
- ],
- "url": {
- "raw": "{{host}}/versioned/test10.txt",
- "host": [
- "{{host}}"
- ],
- "path": [
- "versioned",
- "test10.txt"
- ]
- }
- },
- "response": []
- },
- {
- "name": "versioned delete",
- "request": {
- "method": "DELETE",
- "header": [
- {
- "key": "user",
- "value": "ver",
- "type": "text"
- },
- {
- "key": "password",
- "value": "ver",
- "type": "text"
- },
- {
- "key": "token",
- "value": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJpc3MiOiJzZWN1cmUtYXBpIiwiYXVkIjoic2VjdXJlLWFwcCIsInN1YiI6InVzZXJuYW1lIiwiZXhwIjoxNTYxMzA0MTA4LCJyb2wiOlsiUk9MRV9VU0VSIl19.Ungu5hob8mbwb7GfWfvqj1VsrNNv1pN6UEMJYJHSAHBicRlsYVbRAE7Ju_N1nnjdoZ6TSs4hireff92SkXvesQ",
- "type": "text"
- },
- {
- "key": "Accept",
- "value": "application/json",
- "type": "text"
- },
- {
- "key": "Content-Type",
- "value": "application/json",
- "type": "text"
- }
- ],
- "url": {
- "raw": "{{host}}/versioned/test4.txt",
- "host": [
- "{{host}}"
- ],
- "path": [
- "versioned",
- "test4.txt"
- ]
- }
- },
- "response": []
- },
- {
- "name": "versioned write",
- "request": {
- "method": "PUT",
- "header": [
- {
- "key": "Content-Type",
- "value": "application/octet-stream",
- "type": "text"
- },
- {
- "key": "user",
- "value": "ver",
- "type": "text"
- },
- {
- "key": "password",
- "value": "ver",
- "type": "text"
- },
- {
- "key": "token",
- "value": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJpc3MiOiJzZWN1cmUtYXBpIiwiYXVkIjoic2VjdXJlLWFwcCIsInN1YiI6InVzZXJuYW1lIiwiZXhwIjoxNTYxMzA0MTA4LCJyb2wiOlsiUk9MRV9VU0VSIl19.Ungu5hob8mbwb7GfWfvqj1VsrNNv1pN6UEMJYJHSAHBicRlsYVbRAE7Ju_N1nnjdoZ6TSs4hireff92SkXvesQ",
- "type": "text"
- }
- ],
- "body": {
- "mode": "file",
- "file": {
- "src": "/Users/maxim/parallel_transactions_merge_logic_fix.patch"
- }
- },
- "url": {
- "raw": "{{host}}/versioned/test10.txt",
- "host": [
- "{{host}}"
- ],
- "path": [
- "versioned",
- "test10.txt"
- ]
- }
- },
- "response": []
- },
- {
- "name": "versioned list",
- "request": {
- "method": "GET",
- "header": [
- {
- "key": "user",
- "value": "ver",
- "type": "text"
- },
- {
- "key": "password",
- "value": "ver",
- "type": "text"
- },
- {
- "key": "token",
- "value": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJpc3MiOiJzZWN1cmUtYXBpIiwiYXVkIjoic2VjdXJlLWFwcCIsInN1YiI6InVzZXJuYW1lIiwiZXhwIjoxNTYxMzA0MTA4LCJyb2wiOlsiUk9MRV9VU0VSIl19.Ungu5hob8mbwb7GfWfvqj1VsrNNv1pN6UEMJYJHSAHBicRlsYVbRAE7Ju_N1nnjdoZ6TSs4hireff92SkXvesQ",
- "type": "text"
- },
- {
- "key": "Accept",
- "value": "application/json",
- "type": "text"
- },
- {
- "key": "Content-Type",
- "value": "application/json",
- "type": "text"
- }
- ],
- "url": {
- "raw": "{{host}}/versioned/",
- "host": [
- "{{host}}"
- ],
- "path": [
- "versioned",
- ""
- ]
- }
- },
- "response": []
- }
- ]
+ "info": {
+ "_postman_id": "64bacc95-26af-47f0-8e77-a9767cc87c5d",
+ "name": "Datasafe",
+ "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
+ "_exporter_id": "1072488",
+ "_collection_link": "https://www.postman.com/max402/workspace/datasafe/collection/1072488-64bacc95-26af-47f0-8e77-a9767cc87c5d?action=share&source=collection_link&creator=1072488"
+ },
+ "item": [
+ {
+ "name": "user",
+ "item": [
+ {
+ "name": "authenticate",
+ "event": [
+ {
+ "listen": "test",
+ "script": {
+ "exec": [
+ "pm.test('Success', function() {",
+ " pm.response.to.have.status(200)",
+ "})",
+ "",
+ "pm.collectionVariables.set(\"token\", pm.response.headers.get('token'))",
+ ""
+ ],
+ "type": "text/javascript"
+ }
+ }
+ ],
+ "request": {
+ "auth": {
+ "type": "noauth"
+ },
+ "method": "POST",
+ "header": [
+ {
+ "key": "Content-Type",
+ "name": "Content-Type",
+ "value": "application/json",
+ "type": "text",
+ "disabled": true
+ },
+ {
+ "key": "Origin",
+ "value": "http://localhost:8080",
+ "type": "text",
+ "disabled": true
+ }
+ ],
+ "body": {
+ "mode": "raw",
+ "raw": "{\"userName\":\"username\",\"password\":\"password\"}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "{{host}}/api/authenticate",
+ "host": [
+ "{{host}}"
+ ],
+ "path": [
+ "api",
+ "authenticate"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "delete user Alice",
+ "event": [
+ {
+ "listen": "test",
+ "script": {
+ "exec": [
+ "pm.test('Success', function() {",
+ " pm.response.to.have.status(200)",
+ "})",
+ ""
+ ],
+ "type": "text/javascript"
+ }
+ }
+ ],
+ "request": {
+ "method": "DELETE",
+ "header": [
+ {
+ "key": "Content-Type",
+ "name": "Content-Type",
+ "value": "application/json",
+ "type": "text"
+ },
+ {
+ "key": "user",
+ "value": "Alice",
+ "type": "text"
+ },
+ {
+ "key": "password",
+ "value": "123",
+ "type": "text"
+ },
+ {
+ "key": "token",
+ "value": "{{token}}",
+ "type": "text"
+ }
+ ],
+ "url": {
+ "raw": "{{host}}/user",
+ "host": [
+ "{{host}}"
+ ],
+ "path": [
+ "user"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "delete user Bob",
+ "event": [
+ {
+ "listen": "test",
+ "script": {
+ "exec": [
+ "pm.test('Success', function() {",
+ " pm.response.to.have.status(200)",
+ "})",
+ ""
+ ],
+ "type": "text/javascript"
+ }
+ }
+ ],
+ "request": {
+ "method": "DELETE",
+ "header": [
+ {
+ "key": "Content-Type",
+ "name": "Content-Type",
+ "value": "application/json",
+ "type": "text"
+ },
+ {
+ "key": "user",
+ "value": "Bob",
+ "type": "text"
+ },
+ {
+ "key": "password",
+ "value": "123",
+ "type": "text"
+ },
+ {
+ "key": "token",
+ "value": "{{token}}",
+ "type": "text"
+ }
+ ],
+ "url": {
+ "raw": "{{host}}/user",
+ "host": [
+ "{{host}}"
+ ],
+ "path": [
+ "user"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "create user Alice",
+ "event": [
+ {
+ "listen": "prerequest",
+ "script": {
+ "exec": [
+ ""
+ ],
+ "type": "text/javascript"
+ }
+ },
+ {
+ "listen": "test",
+ "script": {
+ "exec": [
+ "pm.test('Success', function() {",
+ " pm.response.to.have.status(200)",
+ "})"
+ ],
+ "type": "text/javascript"
+ }
+ }
+ ],
+ "request": {
+ "method": "PUT",
+ "header": [
+ {
+ "key": "Content-Type",
+ "name": "Content-Type",
+ "value": "application/json",
+ "type": "text"
+ },
+ {
+ "key": "token",
+ "value": "{{token}}",
+ "type": "text"
+ }
+ ],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n\t\"userName\":\"Alice\",\n\t\"password\":\"123\"\n}"
+ },
+ "url": {
+ "raw": "{{host}}/user",
+ "host": [
+ "{{host}}"
+ ],
+ "path": [
+ "user"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "create user Bob",
+ "event": [
+ {
+ "listen": "prerequest",
+ "script": {
+ "exec": [
+ ""
+ ],
+ "type": "text/javascript"
+ }
+ },
+ {
+ "listen": "test",
+ "script": {
+ "exec": [
+ "pm.test('Success', function() {",
+ " pm.response.to.have.status(200)",
+ "})"
+ ],
+ "type": "text/javascript"
+ }
+ }
+ ],
+ "request": {
+ "method": "PUT",
+ "header": [
+ {
+ "key": "Content-Type",
+ "name": "Content-Type",
+ "value": "application/json",
+ "type": "text"
+ },
+ {
+ "key": "token",
+ "value": "{{token}}",
+ "type": "text"
+ }
+ ],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n\t\"userName\":\"Bob\",\n\t\"password\":\"123\"\n}"
+ },
+ "url": {
+ "raw": "{{host}}/user",
+ "host": [
+ "{{host}}"
+ ],
+ "path": [
+ "user"
+ ]
+ }
+ },
+ "response": []
+ }
+ ]
+ },
+ {
+ "name": "private",
+ "item": [
+ {
+ "name": "store document",
+ "event": [
+ {
+ "listen": "test",
+ "script": {
+ "exec": [
+ "pm.test('Success', function() {",
+ " pm.response.to.have.status(200)",
+ "})"
+ ],
+ "type": "text/javascript",
+ "packages": {}
+ }
+ }
+ ],
+ "request": {
+ "method": "PUT",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "multipart/form-data",
+ "type": "text"
+ },
+ {
+ "key": "user",
+ "value": "Alice",
+ "type": "text"
+ },
+ {
+ "key": "password",
+ "value": "123",
+ "type": "text"
+ },
+ {
+ "key": "token",
+ "value": "{{token}}",
+ "type": "text"
+ }
+ ],
+ "body": {
+ "mode": "formdata",
+ "formdata": [
+ {
+ "key": "file",
+ "type": "file",
+ "src": "postman-cloud:///1eefbe22-4f28-4bc0-8682-38c006661c24"
+ }
+ ]
+ },
+ "url": {
+ "raw": "{{host}}/document",
+ "host": [
+ "{{host}}"
+ ],
+ "path": [
+ "document"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "list documents",
+ "event": [
+ {
+ "listen": "test",
+ "script": {
+ "exec": [
+ "pm.test('Success', function() {",
+ " pm.response.to.have.status(200)",
+ "})",
+ "",
+ "var rb = JSON.parse(responseBody);",
+ "",
+ "pm.test(\"response contains one file\", () => {",
+ " pm.expect(rb.length).to.eql(1);",
+ "})"
+ ],
+ "type": "text/javascript"
+ }
+ }
+ ],
+ "request": {
+ "auth": {
+ "type": "noauth"
+ },
+ "method": "GET",
+ "header": [
+ {
+ "key": "user",
+ "value": "Alice",
+ "type": "text"
+ },
+ {
+ "key": "password",
+ "value": "123",
+ "type": "text"
+ },
+ {
+ "key": "token",
+ "value": "{{token}}",
+ "type": "text"
+ }
+ ],
+ "url": {
+ "raw": "{{host}}/documents",
+ "host": [
+ "{{host}}"
+ ],
+ "path": [
+ "documents"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "read document",
+ "event": [
+ {
+ "listen": "test",
+ "script": {
+ "exec": [
+ "pm.test('Success', function() {",
+ " pm.response.to.have.status(200)",
+ "})",
+ "",
+ "pm.test(\"file content is read\", () => {",
+ " pm.expect(responseBody).to.eql(\"hello world!\");",
+ "})"
+ ],
+ "type": "text/javascript"
+ }
+ }
+ ],
+ "request": {
+ "method": "GET",
+ "header": [
+ {
+ "key": "Accept",
+ "value": "application/octet-stream",
+ "type": "text"
+ },
+ {
+ "key": "user",
+ "value": "Alice",
+ "type": "text"
+ },
+ {
+ "key": "password",
+ "value": "123",
+ "type": "text"
+ },
+ {
+ "key": "token",
+ "value": "{{token}}",
+ "type": "text"
+ }
+ ],
+ "url": {
+ "raw": "{{host}}/document/test.txt",
+ "host": [
+ "{{host}}"
+ ],
+ "path": [
+ "document",
+ "test.txt"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "delete document",
+ "event": [
+ {
+ "listen": "test",
+ "script": {
+ "exec": [
+ "pm.test('Success', function() {",
+ " pm.response.to.have.status(200)",
+ "})"
+ ],
+ "type": "text/javascript"
+ }
+ }
+ ],
+ "request": {
+ "method": "DELETE",
+ "header": [
+ {
+ "key": "user",
+ "value": "Alice",
+ "type": "text"
+ },
+ {
+ "key": "password",
+ "value": "123",
+ "type": "text"
+ },
+ {
+ "key": "token",
+ "value": "{{token}}",
+ "type": "text"
+ }
+ ],
+ "url": {
+ "raw": "{{host}}/document/test.txt",
+ "host": [
+ "{{host}}"
+ ],
+ "path": [
+ "document",
+ "test.txt"
+ ]
+ }
+ },
+ "response": []
+ }
+ ]
+ },
+ {
+ "name": "versioned",
+ "item": [
+ {
+ "name": "versioned write",
+ "event": [
+ {
+ "listen": "test",
+ "script": {
+ "exec": [
+ "pm.test('Success', function() {",
+ " pm.response.to.have.status(200)",
+ "})"
+ ],
+ "type": "text/javascript",
+ "packages": {}
+ }
+ }
+ ],
+ "request": {
+ "method": "PUT",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "multipart/form-data",
+ "type": "text"
+ },
+ {
+ "key": "user",
+ "value": "Alice",
+ "type": "text"
+ },
+ {
+ "key": "password",
+ "value": "123",
+ "type": "text"
+ },
+ {
+ "key": "token",
+ "value": "{{token}}",
+ "type": "text"
+ }
+ ],
+ "body": {
+ "mode": "formdata",
+ "formdata": [
+ {
+ "key": "file",
+ "type": "file",
+ "src": "postman-cloud:///1eefbe22-4f28-4bc0-8682-38c006661c24"
+ }
+ ]
+ },
+ "url": {
+ "raw": "{{host}}/versioned/test.txt",
+ "host": [
+ "{{host}}"
+ ],
+ "path": [
+ "versioned",
+ "test.txt"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "versioned write 2",
+ "event": [
+ {
+ "listen": "test",
+ "script": {
+ "exec": [
+ "pm.test('Success', function() {",
+ " pm.response.to.have.status(200)",
+ "})"
+ ],
+ "type": "text/javascript",
+ "packages": {}
+ }
+ }
+ ],
+ "request": {
+ "method": "PUT",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "multipart/form-data",
+ "type": "text"
+ },
+ {
+ "key": "user",
+ "value": "Alice",
+ "type": "text"
+ },
+ {
+ "key": "password",
+ "value": "123",
+ "type": "text"
+ },
+ {
+ "key": "token",
+ "value": "{{token}}",
+ "type": "text"
+ }
+ ],
+ "body": {
+ "mode": "formdata",
+ "formdata": [
+ {
+ "key": "file",
+ "type": "file",
+ "src": "postman-cloud:///1eefbe32-6174-4030-8d62-d50e2f6b243a"
+ }
+ ]
+ },
+ "url": {
+ "raw": "{{host}}/versioned/test.txt",
+ "host": [
+ "{{host}}"
+ ],
+ "path": [
+ "versioned",
+ "test.txt"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "versioned list (last version)",
+ "event": [
+ {
+ "listen": "test",
+ "script": {
+ "exec": [
+ "pm.test('Success', function() {",
+ " pm.response.to.have.status(200)",
+ "})",
+ "",
+ "var rb = JSON.parse(responseBody);",
+ "",
+ "pm.test(\"response contains one file\", () => {",
+ " pm.expect(rb.length).to.eql(1);",
+ "})"
+ ],
+ "type": "text/javascript"
+ }
+ }
+ ],
+ "request": {
+ "method": "GET",
+ "header": [
+ {
+ "key": "user",
+ "value": "Alice",
+ "type": "text"
+ },
+ {
+ "key": "password",
+ "value": "123",
+ "type": "text"
+ },
+ {
+ "key": "token",
+ "value": "{{token}}",
+ "type": "text"
+ },
+ {
+ "key": "Accept",
+ "value": "application/json",
+ "type": "text"
+ },
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "type": "text"
+ }
+ ],
+ "url": {
+ "raw": "{{host}}/versioned/test.txt",
+ "host": [
+ "{{host}}"
+ ],
+ "path": [
+ "versioned",
+ "test.txt"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "versions list",
+ "event": [
+ {
+ "listen": "test",
+ "script": {
+ "exec": [
+ "pm.test('Success', function() {",
+ " pm.response.to.have.status(200)",
+ "})",
+ "",
+ "var rb = JSON.parse(responseBody);",
+ "",
+ "pm.test(\"response contains one file\", () => {",
+ " pm.expect(rb.length).to.eql(2);",
+ "})"
+ ],
+ "type": "text/javascript"
+ }
+ }
+ ],
+ "request": {
+ "method": "GET",
+ "header": [
+ {
+ "key": "user",
+ "type": "text",
+ "value": "Alice"
+ },
+ {
+ "key": "password",
+ "type": "text",
+ "value": "123"
+ },
+ {
+ "key": "token",
+ "value": "{{token}}",
+ "type": "text"
+ },
+ {
+ "key": "Accept",
+ "value": "application/json",
+ "type": "text"
+ },
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "type": "text"
+ }
+ ],
+ "url": {
+ "raw": "{{host}}/versions/list/test.txt",
+ "host": [
+ "{{host}}"
+ ],
+ "path": [
+ "versions",
+ "list",
+ "test.txt"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "versioned read (last version)",
+ "event": [
+ {
+ "listen": "test",
+ "script": {
+ "exec": [
+ "pm.test('Success', function() {",
+ " pm.response.to.have.status(200)",
+ "})",
+ "",
+ "pm.test(\"file content is read\", () => {",
+ " pm.expect(responseBody).to.eql(\"yet another file\");",
+ "})"
+ ],
+ "type": "text/javascript"
+ }
+ }
+ ],
+ "request": {
+ "method": "GET",
+ "header": [
+ {
+ "key": "user",
+ "value": "Alice",
+ "type": "text"
+ },
+ {
+ "key": "password",
+ "value": "123",
+ "type": "text"
+ },
+ {
+ "key": "token",
+ "value": "{{token}}",
+ "type": "text"
+ },
+ {
+ "key": "Accept",
+ "value": "application/octet-stream",
+ "type": "text"
+ },
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "type": "text"
+ }
+ ],
+ "url": {
+ "raw": "{{host}}/versioned/test.txt",
+ "host": [
+ "{{host}}"
+ ],
+ "path": [
+ "versioned",
+ "test.txt"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "versioned delete",
+ "event": [
+ {
+ "listen": "test",
+ "script": {
+ "exec": [
+ "pm.test('Success', function() {",
+ " pm.response.to.have.status(200)",
+ "})",
+ ""
+ ],
+ "type": "text/javascript"
+ }
+ }
+ ],
+ "request": {
+ "method": "DELETE",
+ "header": [
+ {
+ "key": "user",
+ "value": "Alice",
+ "type": "text"
+ },
+ {
+ "key": "password",
+ "value": "123",
+ "type": "text"
+ },
+ {
+ "key": "token",
+ "value": "{{token}}",
+ "type": "text"
+ },
+ {
+ "key": "Accept",
+ "value": "application/json",
+ "type": "text"
+ },
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "type": "text"
+ }
+ ],
+ "url": {
+ "raw": "{{host}}/versioned/test.txt",
+ "host": [
+ "{{host}}"
+ ],
+ "path": [
+ "versioned",
+ "test.txt"
+ ]
+ }
+ },
+ "response": []
+ }
+ ]
+ },
+ {
+ "name": "inbox",
+ "item": [
+ {
+ "name": "send to inbox",
+ "event": [
+ {
+ "listen": "test",
+ "script": {
+ "exec": [
+ "pm.test('Success', function() {",
+ " pm.response.to.have.status(200)",
+ "})",
+ ""
+ ],
+ "type": "text/javascript",
+ "packages": {}
+ }
+ }
+ ],
+ "request": {
+ "method": "PUT",
+ "header": [
+ {
+ "key": "user",
+ "value": "Alice",
+ "type": "text",
+ "disabled": true
+ },
+ {
+ "key": "password",
+ "value": "123",
+ "type": "text",
+ "disabled": true
+ },
+ {
+ "key": "token",
+ "value": "{{token}}",
+ "type": "text"
+ },
+ {
+ "key": "users",
+ "value": "Bob",
+ "type": "text"
+ }
+ ],
+ "body": {
+ "mode": "formdata",
+ "formdata": [
+ {
+ "key": "file",
+ "type": "file",
+ "src": "postman-cloud:///1eefbe22-4f28-4bc0-8682-38c006661c24"
+ }
+ ]
+ },
+ "url": {
+ "raw": "{{host}}/inbox/document",
+ "host": [
+ "{{host}}"
+ ],
+ "path": [
+ "inbox",
+ "document"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "inbox list",
+ "event": [
+ {
+ "listen": "test",
+ "script": {
+ "exec": [
+ "pm.test('Success', function() {",
+ " pm.response.to.have.status(200)",
+ "})",
+ "",
+ "var rb = JSON.parse(responseBody);",
+ "",
+ "pm.test(\"response contains one file\", () => {",
+ " pm.expect(rb.length).to.eql(1);",
+ "})"
+ ],
+ "type": "text/javascript"
+ }
+ }
+ ],
+ "request": {
+ "method": "GET",
+ "header": [
+ {
+ "key": "user",
+ "value": "Bob",
+ "type": "text"
+ },
+ {
+ "key": "password",
+ "value": "123",
+ "type": "text"
+ },
+ {
+ "key": "Accept",
+ "value": "application/json",
+ "type": "text"
+ },
+ {
+ "key": "token",
+ "value": "{{token}}",
+ "type": "text"
+ }
+ ],
+ "url": {
+ "raw": "{{host}}/inbox/documents",
+ "host": [
+ "{{host}}"
+ ],
+ "path": [
+ "inbox",
+ "documents"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "inbox read",
+ "event": [
+ {
+ "listen": "test",
+ "script": {
+ "exec": [
+ "pm.test('Success', function() {",
+ " pm.response.to.have.status(200)",
+ "})",
+ "",
+ "pm.test(\"file content is read\", () => {",
+ " pm.expect(responseBody).to.eql(\"hello world!\");",
+ "})"
+ ],
+ "type": "text/javascript"
+ }
+ }
+ ],
+ "request": {
+ "method": "GET",
+ "header": [
+ {
+ "key": "user",
+ "value": "Bob",
+ "type": "text"
+ },
+ {
+ "key": "password",
+ "value": "123",
+ "type": "text"
+ },
+ {
+ "key": "Accept",
+ "value": "application/octet-stream",
+ "type": "text"
+ },
+ {
+ "key": "token",
+ "value": "{{token}}",
+ "type": "text"
+ }
+ ],
+ "url": {
+ "raw": "{{host}}/inbox/document/test.txt",
+ "host": [
+ "{{host}}"
+ ],
+ "path": [
+ "inbox",
+ "document",
+ "test.txt"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "inbox delete",
+ "event": [
+ {
+ "listen": "test",
+ "script": {
+ "exec": [
+ "pm.test('Success', function() {",
+ " pm.response.to.have.status(200)",
+ "})",
+ ""
+ ],
+ "type": "text/javascript"
+ }
+ }
+ ],
+ "request": {
+ "method": "DELETE",
+ "header": [
+ {
+ "key": "user",
+ "value": "Bob",
+ "type": "text"
+ },
+ {
+ "key": "password",
+ "value": "123",
+ "type": "text"
+ },
+ {
+ "key": "token",
+ "value": "{{token}}",
+ "type": "text"
+ }
+ ],
+ "url": {
+ "raw": "{{host}}/inbox/document/test.txt",
+ "host": [
+ "{{host}}"
+ ],
+ "path": [
+ "inbox",
+ "document",
+ "test.txt"
+ ]
+ }
+ },
+ "response": []
+ }
+ ]
+ }
+ ],
+ "auth": {
+ "type": "bearer",
+ "bearer": [
+ {
+ "key": "token",
+ "value": "{{token}}",
+ "type": "string"
+ }
+ ]
+ },
+ "event": [
+ {
+ "listen": "prerequest",
+ "script": {
+ "type": "text/javascript",
+ "exec": [
+ ""
+ ]
+ }
+ },
+ {
+ "listen": "test",
+ "script": {
+ "type": "text/javascript",
+ "exec": [
+ ""
+ ]
+ }
+ }
+ ],
+ "variable": [
+ {
+ "key": "host",
+ "value": "http://localhost:8080",
+ "type": "string"
+ },
+ {
+ "key": "token",
+ "value": ""
+ }
+ ]
}
\ No newline at end of file
diff --git a/datasafe-rest-impl/docker-compose.yml b/datasafe-rest-impl/docker-compose.yml
deleted file mode 100644
index 838c605fb..000000000
--- a/datasafe-rest-impl/docker-compose.yml
+++ /dev/null
@@ -1,18 +0,0 @@
-version: '3.3'
-services:
- db:
- image: mysql:5.7
- restart: always
- environment:
- MYSQL_DATABASE: 'test_db'
- MYSQL_USER: 'test'
- MYSQL_PASSWORD: 'test'
- MYSQL_ROOT_PASSWORD: 'password'
- ports:
- - '3306:3306'
- expose:
- - '3306'
- volumes:
- - my-db:/var/lib/mysql
-volumes:
- my-db:
diff --git a/datasafe-rest-impl/pom.xml b/datasafe-rest-impl/pom.xml
index 666cbd40e..33dd14c4b 100644
--- a/datasafe-rest-impl/pom.xml
+++ b/datasafe-rest-impl/pom.xml
@@ -20,9 +20,15 @@
2.2.4
1.6.0
3.0.2
+ 2.3.0
+
+ org.springdoc
+ springdoc-openapi-starter-webmvc-ui
+ ${springdoc-openapi-starter-webmvc-ui.version}
+
de.adorsys
datasafe-business
@@ -157,7 +163,6 @@
true
- ${project.artifactId}-${project.version}
org.springframework.boot
diff --git a/datasafe-rest-impl/run.sh b/datasafe-rest-impl/run.sh
index d13c46e48..01eadff0c 100755
--- a/datasafe-rest-impl/run.sh
+++ b/datasafe-rest-impl/run.sh
@@ -7,7 +7,7 @@ if [[ -z "$API_URL" ]]; then
fi
# Bind API url and credentials, sed -i won't work because of OC user permissions
-sed 's!${API_URL}!'"$API_URL"'!g' "$APP_HOME/frontend/env.js" > /tmp/env.js && mv /tmp/env.js "$APP_HOME/frontend/env.js"
+sed 's!${API_URL}!'"$API_URL"'!g' "$APP_HOME/frontend/env.prod.js" > /tmp/env.js && mv /tmp/env.js "$APP_HOME/frontend/env.js"
# do not expose sensitive data by default
LOGIN=""
diff --git a/datasafe-rest-impl/src/main/java/de/adorsys/datasafe/rest/impl/DatasafeRestApplication.java b/datasafe-rest-impl/src/main/java/de/adorsys/datasafe/rest/impl/DatasafeRestApplication.java
index cf581b75a..bdb527dc8 100644
--- a/datasafe-rest-impl/src/main/java/de/adorsys/datasafe/rest/impl/DatasafeRestApplication.java
+++ b/datasafe-rest-impl/src/main/java/de/adorsys/datasafe/rest/impl/DatasafeRestApplication.java
@@ -1,22 +1,15 @@
package de.adorsys.datasafe.rest.impl;
-import de.adorsys.datasafe.rest.impl.config.DatasafeProperties;
-import de.adorsys.datasafe.rest.impl.security.SecurityProperties;
import org.springframework.boot.SpringApplication;
-import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
-import org.springframework.boot.context.properties.EnableConfigurationProperties;
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class, HibernateJpaAutoConfiguration.class})
-@EnableConfigurationProperties({DatasafeProperties.class, SecurityProperties.class})
public class DatasafeRestApplication {
public static void main(String[] args) {
- System.setProperty("org.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH", "true");
SpringApplication.run(DatasafeRestApplication.class, args);
}
-
}
diff --git a/datasafe-rest-impl/src/main/java/de/adorsys/datasafe/rest/impl/config/DatasafeProperties.java b/datasafe-rest-impl/src/main/java/de/adorsys/datasafe/rest/impl/config/DatasafeProperties.java
index ddd7e8e01..8df0e212d 100644
--- a/datasafe-rest-impl/src/main/java/de/adorsys/datasafe/rest/impl/config/DatasafeProperties.java
+++ b/datasafe-rest-impl/src/main/java/de/adorsys/datasafe/rest/impl/config/DatasafeProperties.java
@@ -3,9 +3,11 @@
import de.adorsys.datasafe.encrypiton.api.types.encryption.MutableEncryptionConfig;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
-@ConfigurationProperties(prefix = "datasafe")
@Data
+@Configuration
+@ConfigurationProperties(prefix = "datasafe")
public class DatasafeProperties {
/**
diff --git a/datasafe-rest-impl/src/main/java/de/adorsys/datasafe/rest/impl/config/MvcConfig.java b/datasafe-rest-impl/src/main/java/de/adorsys/datasafe/rest/impl/config/MvcConfig.java
new file mode 100644
index 000000000..a0696d97a
--- /dev/null
+++ b/datasafe-rest-impl/src/main/java/de/adorsys/datasafe/rest/impl/config/MvcConfig.java
@@ -0,0 +1,32 @@
+package de.adorsys.datasafe.rest.impl.config;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.lang.NonNull;
+import org.springframework.util.StringUtils;
+import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
+
+@Slf4j
+@Configuration
+@RequiredArgsConstructor
+public class MvcConfig extends WebMvcConfigurationSupport {
+
+ private final DatasafeProperties datasafeProperties;
+
+ /**
+ * Register static resources - frontend UI.
+ */
+ @Override
+ public void addResourceHandlers(@NonNull ResourceHandlerRegistry registry) {
+ if (!StringUtils.hasLength(datasafeProperties.getStaticResources())) {
+ return;
+ }
+
+ log.info("Serving static resources from {} as /static/**", datasafeProperties.getStaticResources());
+ registry
+ .addResourceHandler("/static/**")
+ .addResourceLocations(datasafeProperties.getStaticResources());
+ }
+}
diff --git a/datasafe-rest-impl/src/main/java/de/adorsys/datasafe/rest/impl/controller/InboxController.java b/datasafe-rest-impl/src/main/java/de/adorsys/datasafe/rest/impl/controller/InboxController.java
index 2967e61d8..0415df7b8 100644
--- a/datasafe-rest-impl/src/main/java/de/adorsys/datasafe/rest/impl/controller/InboxController.java
+++ b/datasafe-rest-impl/src/main/java/de/adorsys/datasafe/rest/impl/controller/InboxController.java
@@ -55,6 +55,7 @@ public class InboxController {
public void writeToInbox(@RequestHeader Set users,
@PathVariable String path,
@RequestParam("file") MultipartFile file) {
+ path = path.replaceAll("^/", "");
Set toUsers = users.stream().map(UserID::new).collect(Collectors.toSet());
try (OutputStream os = dataSafeService.inboxService().write(WriteRequest.forDefaultPublic(toUsers, path));
InputStream is = file.getInputStream()) {
@@ -72,6 +73,7 @@ public void readFromInbox(@RequestHeader String user,
@RequestHeader String password,
@PathVariable String path,
HttpServletResponse response) {
+ path = path.replaceAll("^/", "");
UserIDAuth userIDAuth = new UserIDAuth(new UserID(user), ReadKeyPasswordHelper.getForString(password));
PrivateResource resource = BasePrivateResource.forPrivate(path);
// this is needed for swagger, produces is just a directive:
@@ -91,6 +93,7 @@ public void readFromInbox(@RequestHeader String user,
public void deleteFromInbox(@RequestHeader String user,
@RequestHeader String password,
@PathVariable String path) {
+ path = path.replaceAll("^/", "");
UserIDAuth userIDAuth = new UserIDAuth(new UserID(user), ReadKeyPasswordHelper.getForString(password));
PrivateResource resource = BasePrivateResource.forPrivate(path);
RemoveRequest request = RemoveRequest.forPrivate(userIDAuth, resource);
@@ -105,6 +108,7 @@ public void deleteFromInbox(@RequestHeader String user,
public List listInbox(@RequestHeader String user,
@RequestHeader String password,
@PathVariable(required = false) String path) {
+ path = path.replaceAll("^/", "");
UserIDAuth userIDAuth = new UserIDAuth(new UserID(user), ReadKeyPasswordHelper.getForString(password));
path = Optional.ofNullable(path)
.map(it -> it.replaceAll("^\\.$", ""))
diff --git a/datasafe-rest-impl/src/main/java/de/adorsys/datasafe/rest/impl/security/SecurityConfig.java b/datasafe-rest-impl/src/main/java/de/adorsys/datasafe/rest/impl/security/SecurityConfig.java
index ed5677088..b6bc4e98a 100644
--- a/datasafe-rest-impl/src/main/java/de/adorsys/datasafe/rest/impl/security/SecurityConfig.java
+++ b/datasafe-rest-impl/src/main/java/de/adorsys/datasafe/rest/impl/security/SecurityConfig.java
@@ -26,10 +26,13 @@
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
+import java.util.Arrays;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import static de.adorsys.datasafe.rest.impl.security.SecurityConstants.TOKEN_HEADER;
+import static org.springframework.security.config.Customizer.withDefaults;
@Configuration
@EnableWebSecurity
@@ -44,7 +47,7 @@ public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http, MvcRequestMatcher.Builder mvc, AuthenticationManager authenticationManager) throws Exception {
- MvcRequestMatcher[] SWAGGER_RESOURCES = {
+ MvcRequestMatcher[] swaggerResources = {
mvc.pattern("/v2/api-docs"),
mvc.pattern("/configuration/ui"),
mvc.pattern("/swagger-resources"),
@@ -54,14 +57,13 @@ public SecurityFilterChain filterChain(HttpSecurity http, MvcRequestMatcher.Buil
mvc.pattern("/swagger-resources/configuration/ui"),
mvc.pattern("/swagger-ui.html")
};
-
- http.cors(AbstractHttpConfigurer::disable)
+ http.cors(withDefaults())
.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(authz -> authz
- .requestMatchers(SWAGGER_RESOURCES).permitAll()
+ .requestMatchers(swaggerResources).permitAll()
.requestMatchers(mvc.pattern("/static/**")).permitAll()
.requestMatchers(mvc.pattern(SecurityConstants.AUTH_LOGIN_URL)).permitAll()
- .requestMatchers(mvc.pattern(HttpMethod.OPTIONS, "/**")).permitAll()
+ .requestMatchers(mvc.pattern(HttpMethod.GET, "/**")).permitAll()
.anyRequest().authenticated()
)
.addFilter(new JwtAuthorizationFilter(authenticationManager, securityProperties))
@@ -77,8 +79,8 @@ MvcRequestMatcher.Builder mvc(HandlerMappingIntrospector introspector) {
}
@Bean
- public InMemoryUserDetailsManager userDetailsService(PasswordEncoder passwordEncoder) {
- UserDetails user = User.withDefaultPasswordEncoder()
+ public InMemoryUserDetailsManager userDetailsService(PasswordEncoder encoder) {
+ UserDetails user = User.builder().passwordEncoder(encoder::encode)
.username(securityProperties.getDefaultUser())
.password(securityProperties.getDefaultPassword())
.authorities("ROLE_USER")
@@ -101,19 +103,15 @@ public PasswordEncoder passwordEncoder() {
}
@Bean
- public CorsConfigurationSource corsConfigurationSource() {
+ CorsConfigurationSource corsConfigurationSource() {
+ CorsConfiguration configuration = new CorsConfiguration();
+ configuration.setAllowedOrigins(List.of("http://localhost:4200"));
+ configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE"));
+ configuration.setAllowedHeaders(List.of("*"));
+ configuration.setAllowCredentials(true);
+ configuration.addExposedHeader(TOKEN_HEADER);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
-
- CorsConfiguration authConfig = new CorsConfiguration().applyPermitDefaultValues();
- authConfig.addExposedHeader(TOKEN_HEADER);
- source.registerCorsConfiguration(SecurityConstants.AUTH_LOGIN_URL, authConfig);
-
- CorsConfiguration globalConfig = new CorsConfiguration().applyPermitDefaultValues();
- globalConfig.addAllowedMethod(HttpMethod.OPTIONS);
- globalConfig.addAllowedMethod(HttpMethod.PUT);
- globalConfig.addAllowedMethod(HttpMethod.DELETE);
- source.registerCorsConfiguration("/**", globalConfig);
-
+ source.registerCorsConfiguration("/**", configuration);
return source;
}
diff --git a/datasafe-rest-impl/src/main/resources/jwt-config.properties b/datasafe-rest-impl/src/main/resources/jwt-config.properties
index 97cd74c18..d0b6f7474 100644
--- a/datasafe-rest-impl/src/main/resources/jwt-config.properties
+++ b/datasafe-rest-impl/src/main/resources/jwt-config.properties
@@ -1,5 +1,5 @@
-jwt_secret=${JWT_SECRET}
-default_user=${DEFAULT_USER}
-default_password=${DEFAULT_PASSWORD}
+jwt_secret=${JWT_SECRET:n2r5u8x/A%D*G-KaPdSgVkYp3s6v9y$B&E(H+MbQeThWmZq4t7w!z%C*F-J@NcRf}
+default_user=${DEFAULT_USER:username}
+default_password=${DEFAULT_PASSWORD:password}
#10 Days in ms
token_expiration=${TOKEN_EXPIRATION:864000000}
diff --git a/datasafe-rest-impl/src/test/resources/jwt-config.properties b/datasafe-rest-impl/src/test/resources/jwt-config.properties
deleted file mode 100644
index 7402462a6..000000000
--- a/datasafe-rest-impl/src/test/resources/jwt-config.properties
+++ /dev/null
@@ -1,7 +0,0 @@
-debug=false
-management.endpoints.web.exposure.include=*
-
-jwt_secret=n2r5u8x/A%D*G-KaPdSgVkYp3s6v9y$B&E(H+MbQeThWmZq4t7w!z%C*F-J@NcRf
-default_user=username
-default_password=password
-token_expiration=864000000
diff --git a/frontend/README.md b/frontend/README.md
index ca8275e3a..174a51035 100644
--- a/frontend/README.md
+++ b/frontend/README.md
@@ -3,6 +3,8 @@
Use `npm run-script ng:serve:web` for local development
Use `npm run-script start` for electron development
+Use `ng serve -c dev` if you need to debug
+
### Notes
- API url and credentials are provided by env.js file (API_URL, API_USERNAME, API_PASSWORD).
Credentials (API_USERNAME, API_PASSWORD) are intended for local use only.
diff --git a/frontend/datasafe-ui/angular.json b/frontend/datasafe-ui/angular.json
index 544edcee8..8d763c103 100755
--- a/frontend/datasafe-ui/angular.json
+++ b/frontend/datasafe-ui/angular.json
@@ -40,7 +40,6 @@
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
- "extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
@@ -53,24 +52,38 @@
"maximumError": "5mb"
}
]
+ },
+ "dev": {
+ "fileReplacements": [
+ {
+ "replace": "src/environments/environment.ts",
+ "with": "src/environments/environment.dev.ts"
+ }
+ ],
+ "optimization": false,
+ "extractLicenses": false,
+ "sourceMap": true
}
}
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
- "browserTarget": "my-app:build"
+ "buildTarget": "my-app:build"
},
"configurations": {
"production": {
- "browserTarget": "my-app:build:production"
+ "buildTarget": "my-app:build:production"
+ },
+ "dev": {
+ "buildTarget": "my-app:build:dev"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
- "browserTarget": "my-app:build"
+ "buildTarget": "my-app:build"
}
},
"test": {
@@ -105,5 +118,7 @@
}
}
},
- "defaultProject": "datasafe-ui"
+ "cli": {
+ "analytics": false
+ }
}
\ No newline at end of file
diff --git a/frontend/datasafe-ui/main.js b/frontend/datasafe-ui/main.js
index 49966638b..c6746dce1 100644
--- a/frontend/datasafe-ui/main.js
+++ b/frontend/datasafe-ui/main.js
@@ -21,7 +21,7 @@ function createWindow() {
});
if (serve) {
require('electron-reload')(__dirname, {
- electron: require(__dirname + "/node_modules/electron")
+ electron: require("".concat(__dirname, "/node_modules/electron"))
});
win.loadURL('http://localhost:4200');
}
diff --git a/frontend/datasafe-ui/main.js.map b/frontend/datasafe-ui/main.js.map
index f967e92ff..0723729af 100644
--- a/frontend/datasafe-ui/main.js.map
+++ b/frontend/datasafe-ui/main.js.map
@@ -1 +1 @@
-{"version":3,"file":"main.js","sourceRoot":"","sources":["main.ts"],"names":[],"mappings":";;AAAA,qCAAsD;AACtD,2BAA6B;AAC7B,yBAA2B;AAE3B,IAAI,GAAG,EAAE,KAAK,CAAC;AACf,IAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACnC,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,UAAA,GAAG,IAAI,OAAA,GAAG,KAAK,SAAS,EAAjB,CAAiB,CAAC,CAAC;AAE5C,SAAS,YAAY;IAEnB,IAAM,cAAc,GAAG,iBAAM,CAAC;IAC9B,IAAM,IAAI,GAAG,cAAc,CAAC,iBAAiB,EAAE,CAAC,YAAY,CAAC;IAE7D,6BAA6B;IAC7B,GAAG,GAAG,IAAI,wBAAa,CAAC;QACtB,CAAC,EAAE,CAAC;QACJ,CAAC,EAAE,CAAC;QACJ,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,cAAc,EAAE;YACd,eAAe,EAAE,IAAI;SACtB;KACF,CAAC,CAAC;IAEH,IAAI,KAAK,EAAE;QACT,OAAO,CAAC,iBAAiB,CAAC,CAAC,SAAS,EAAE;YACpC,QAAQ,EAAE,OAAO,CAAI,SAAS,2BAAwB,CAAC;SACxD,CAAC,CAAC;QACH,GAAG,CAAC,OAAO,CAAC,uBAAuB,CAAC,CAAC;KACtC;SAAM;QACL,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC;YACrB,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,iBAAiB,CAAC;YACjD,QAAQ,EAAE,OAAO;YACjB,OAAO,EAAE,IAAI;SACd,CAAC,CAAC,CAAC;KACL;IAED,IAAI,KAAK,EAAE;QACT,GAAG,CAAC,WAAW,CAAC,YAAY,EAAE,CAAC;KAChC;IAED,qCAAqC;IACrC,GAAG,CAAC,EAAE,CAAC,QAAQ,EAAE;QACf,gEAAgE;QAChE,mEAAmE;QACnE,oDAAoD;QACpD,GAAG,GAAG,IAAI,CAAC;IACb,CAAC,CAAC,CAAC;AAEL,CAAC;AAED,IAAI;IAEF,wDAAwD;IACxD,yDAAyD;IACzD,sDAAsD;IACtD,cAAG,CAAC,EAAE,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;IAE9B,oCAAoC;IACpC,cAAG,CAAC,EAAE,CAAC,mBAAmB,EAAE;QAC1B,2DAA2D;QAC3D,8DAA8D;QAC9D,IAAI,OAAO,CAAC,QAAQ,KAAK,QAAQ,EAAE;YACjC,cAAG,CAAC,IAAI,EAAE,CAAC;SACZ;IACH,CAAC,CAAC,CAAC;IAEH,cAAG,CAAC,EAAE,CAAC,UAAU,EAAE;QACjB,gEAAgE;QAChE,4DAA4D;QAC5D,IAAI,GAAG,KAAK,IAAI,EAAE;YAChB,YAAY,EAAE,CAAC;SAChB;IACH,CAAC,CAAC,CAAC;CAEJ;AAAC,OAAO,CAAC,EAAE;IACV,cAAc;IACd,WAAW;CACZ"}
\ No newline at end of file
+{"version":3,"file":"main.js","sourceRoot":"","sources":["main.ts"],"names":[],"mappings":";;AAAA,qCAAoD;AACpD,2BAA6B;AAC7B,yBAA2B;AAE3B,IAAI,GAAG,EAAE,KAAK,CAAC;AACf,IAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACnC,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,UAAA,GAAG,IAAI,OAAA,GAAG,KAAK,SAAS,EAAjB,CAAiB,CAAC,CAAC;AAE5C,SAAS,YAAY;IAEnB,IAAM,cAAc,GAAG,iBAAM,CAAC;IAC9B,IAAM,IAAI,GAAG,cAAc,CAAC,iBAAiB,EAAE,CAAC,YAAY,CAAC;IAE7D,6BAA6B;IAC7B,GAAG,GAAG,IAAI,wBAAa,CAAC;QACtB,CAAC,EAAE,CAAC;QACJ,CAAC,EAAE,CAAC;QACJ,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,cAAc,EAAE;YACd,eAAe,EAAE,IAAI;SACtB;KACF,CAAC,CAAC;IAEH,IAAI,KAAK,EAAE;QACT,OAAO,CAAC,iBAAiB,CAAC,CAAC,SAAS,EAAE;YACpC,QAAQ,EAAE,OAAO,CAAC,UAAG,SAAS,2BAAwB,CAAC;SACxD,CAAC,CAAC;QACH,GAAG,CAAC,OAAO,CAAC,uBAAuB,CAAC,CAAC;KACtC;SAAM;QACL,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC;YACrB,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,iBAAiB,CAAC;YACjD,QAAQ,EAAE,OAAO;YACjB,OAAO,EAAE,IAAI;SACd,CAAC,CAAC,CAAC;KACL;IAED,IAAI,KAAK,EAAE;QACT,GAAG,CAAC,WAAW,CAAC,YAAY,EAAE,CAAC;KAChC;IAED,qCAAqC;IACrC,GAAG,CAAC,EAAE,CAAC,QAAQ,EAAE;QACf,gEAAgE;QAChE,mEAAmE;QACnE,oDAAoD;QACpD,GAAG,GAAG,IAAI,CAAC;IACb,CAAC,CAAC,CAAC;AAEL,CAAC;AAED,IAAI;IAEF,wDAAwD;IACxD,yDAAyD;IACzD,sDAAsD;IACtD,cAAG,CAAC,EAAE,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;IAE9B,oCAAoC;IACpC,cAAG,CAAC,EAAE,CAAC,mBAAmB,EAAE;QAC1B,2DAA2D;QAC3D,8DAA8D;QAC9D,IAAI,OAAO,CAAC,QAAQ,KAAK,QAAQ,EAAE;YACjC,cAAG,CAAC,IAAI,EAAE,CAAC;SACZ;IACH,CAAC,CAAC,CAAC;IAEH,cAAG,CAAC,EAAE,CAAC,UAAU,EAAE;QACjB,gEAAgE;QAChE,4DAA4D;QAC5D,IAAI,GAAG,KAAK,IAAI,EAAE;YAChB,YAAY,EAAE,CAAC;SAChB;IACH,CAAC,CAAC,CAAC;CAEJ;AAAC,OAAO,CAAC,EAAE;IACV,cAAc;IACd,WAAW;CACZ"}
\ No newline at end of file
diff --git a/frontend/datasafe-ui/package.json b/frontend/datasafe-ui/package.json
index 3195a72de..33f011d8c 100644
--- a/frontend/datasafe-ui/package.json
+++ b/frontend/datasafe-ui/package.json
@@ -4,7 +4,7 @@
"description": "datasafe-ui with web and electron interface",
"keywords": [
"angular",
- "angular 8",
+ "angular 17",
"electron",
"typescript",
"sass"
@@ -32,29 +32,28 @@
"e2e": "npm run build:prod && ../datasafe-ui/node_modules/.bin/mocha --timeout 300000 --require ts-node/register e2e/**/*.spec.ts"
},
"devDependencies": {
- "@angular-devkit/build-angular": "15.2.9",
- "@angular/animations": "15.2.9",
- "@angular/cdk": "15.2.9",
- "@angular/cli": "15.2.9",
- "@angular/common": "15.2.9",
- "@angular/compiler": "15.2.9",
- "@angular/compiler-cli": "15.2.9",
- "@angular/core": "15.2.9",
-
- "@angular/forms": "15.2.9",
- "@angular/material": "15.2.9",
- "@angular/material-moment-adapter": "15.2.9",
- "@angular/platform-browser": "15.2.9",
- "@angular/platform-browser-dynamic": "15.2.9",
- "@angular/router": "15.2.9",
+ "@angular-devkit/build-angular": "^17.3.5",
+ "@angular/animations": "^17.3.5",
+ "@angular/cdk": "^17.3.5",
+ "@angular/cli": "^17.3.5",
+ "@angular/common": "^17.3.5",
+ "@angular/compiler": "^17.3.5",
+ "@angular/compiler-cli": "^17.3.5",
+ "@angular/core": "^17.3.5",
+ "@angular/forms": "^17.3.5",
+ "@angular/material": "^17.3.5",
+ "@angular/material-moment-adapter": "^17.3.5",
+ "@angular/platform-browser": "^17.3.5",
+ "@angular/platform-browser-dynamic": "^17.3.5",
+ "@angular/router": "^17.3.5",
"@types/jasmine": "4.3.6",
"@types/jasminewd2": "2.0.11",
"@types/mocha": "10.0.2",
"@types/node": "20.8.0",
"codelyzer": "6.0.2",
"core-js": "3.33.0",
- "electron": "26.2.4",
- "electron-builder": "24.7.0",
+ "electron": "^26.6.9",
+ "electron-builder": "24.13.3",
"electron-reload": "1.5.0",
"hammerjs": "2.0.8",
"jasmine-core": "5.1.1",
@@ -71,13 +70,13 @@
"spectron": "19.0.0",
"ts-node": "10.9.1",
"tslint": "5.17.0",
- "typescript": "4.9.4",
- "wait-on": "7.0.1",
+ "typescript": "5.4.5",
+ "wait-on": "^7.2.0",
"web-animations-js": "2.3.2",
"webdriver-manager": "13.0.2",
- "zone.js": "0.14.0"
+ "zone.js": "^0.14.3"
},
"engines": {
"node": ">=18.10.0"
}
-}
+}
\ No newline at end of file
diff --git a/frontend/datasafe-ui/src/app/app.component.ts b/frontend/datasafe-ui/src/app/app.component.ts
index 788f93502..bee20cf9a 100644
--- a/frontend/datasafe-ui/src/app/app.component.ts
+++ b/frontend/datasafe-ui/src/app/app.component.ts
@@ -1,6 +1,6 @@
-import {Component, OnInit} from '@angular/core';
-import {ErrorStateMatcher} from "@angular/material";
-import {FormControl, FormGroupDirective, NgForm} from "@angular/forms";
+import {Component} from '@angular/core';
+import {ErrorStateMatcher} from '@angular/material/core';
+import {FormControl, FormGroupDirective, NgForm} from '@angular/forms';
export class Env {
@@ -8,6 +8,10 @@ export class Env {
private static browserWindow = window || {};
private static browserWindowEnv = Env.browserWindow['__env'] || {};
+ static apiUrl = Env.get('apiUrl');
+ static apiUsername = Env.get('apiUsername');
+ static apiPassword = Env.get('apiPassword');
+
static get(key): string {
if (this.browserWindowEnv.hasOwnProperty(key)) {
return window['__env'][key];
@@ -15,10 +19,6 @@ export class Env {
return null;
}
-
- static apiUrl = Env.get('apiUrl');
- static apiUsername = Env.get('apiUsername');
- static apiPassword = Env.get('apiPassword');
}
export class FieldErrorStateMatcher implements ErrorStateMatcher {
@@ -30,8 +30,8 @@ export class FieldErrorStateMatcher implements ErrorStateMatcher {
export class ParentOrFieldErrorStateMatcher implements ErrorStateMatcher {
isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
- const invalidCtrl = !!(control && control.invalid && control.parent.dirty);
- const invalidParent = !!(control && control.parent && control.parent.invalid && control.parent.dirty);
+ const invalidCtrl = !!(control?.invalid && control?.parent?.dirty);
+ const invalidParent = !!(control?.parent?.invalid && control?.parent?.dirty);
return (invalidCtrl || invalidParent);
}
@@ -40,12 +40,12 @@ export class ParentOrFieldErrorStateMatcher implements ErrorStateMatcher {
export class ErrorMessageUtil {
static extract(error): string {
- let errMsg = "Failed " + error.message;
- if (error && error.error && error.error.message) {
+ let errMsg = 'Failed ' + error.message;
+ if (error?.error?.message) {
errMsg = error.error.message;
}
- return errMsg.substring(0, 32) + (errMsg.length >= 32 ? "..." : "");
+ return errMsg.substring(0, 32) + (errMsg.length >= 32 ? '...' : '');
}
}
@@ -54,10 +54,7 @@ export class ErrorMessageUtil {
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
-export class AppComponent implements OnInit {
+export class AppComponent {
constructor() { }
-
- ngOnInit() {
- }
}
diff --git a/frontend/datasafe-ui/src/app/app.module.ts b/frontend/datasafe-ui/src/app/app.module.ts
index b582f3904..374db1559 100755
--- a/frontend/datasafe-ui/src/app/app.module.ts
+++ b/frontend/datasafe-ui/src/app/app.module.ts
@@ -34,7 +34,7 @@ const appRoutes: Routes = [
ReactiveFormsModule,
RouterModule.forRoot(appRoutes)
],
- entryComponents: [AppComponent, AddFolderDialog, ConfigureApiDialog],
+
declarations: [AppComponent, UserComponent, LoginComponent, RegisterComponent, FiletreeComponent, AddFolderDialog, ConfigureApiDialog],
bootstrap: [AppComponent],
providers: []
diff --git a/frontend/datasafe-ui/src/app/component/filetree/filetree.component.ts b/frontend/datasafe-ui/src/app/component/filetree/filetree.component.ts
index 57aee9256..9e055c08c 100644
--- a/frontend/datasafe-ui/src/app/component/filetree/filetree.component.ts
+++ b/frontend/datasafe-ui/src/app/component/filetree/filetree.component.ts
@@ -3,11 +3,11 @@ import {FlatTreeControl} from '@angular/cdk/tree';
import {Component, Inject, Injectable} from '@angular/core';
import {BehaviorSubject, merge, Observable} from 'rxjs';
import {map} from 'rxjs/operators';
-import {ApiService} from "../../service/api/api.service";
-import {CredentialsService} from "../../service/credentials/credentials.service";
-import {Router} from "@angular/router";
-import {ErrorMessageUtil} from "../../app.component";
-import {MAT_DIALOG_DATA, MatDialog, MatDialogRef} from "@angular/material";
+import {ApiService} from '../../service/api/api.service';
+import {CredentialsService} from '../../service/credentials/credentials.service';
+import {Router} from '@angular/router';
+import {ErrorMessageUtil} from '../../app.component';
+import {MAT_DIALOG_DATA, MatDialog, MatDialogRef} from '@angular/material/dialog';
class UserFileSystem {
@@ -19,42 +19,43 @@ class UserFileSystem {
this.fs.clear();
// maintain consistent order
- files.concat(Array.from(this.uiCreatedFolders).map(it => it + "/"))
+ files.concat(Array.from(this.uiCreatedFolders).map(it => it + '/'))
.sort()
.forEach(it => this.addEntry(it));
}
- rootLevelNodes() : string[] {
- let res = new Set();
+ rootLevelNodes(): string[] {
+ const res = new Set();
this.fs.forEach((value, key) => {
- let split = key.split("/", 2);
- res.add(split[0] + (split.length > 1 ? "/" : ""));
+ const split = key.split('/', 2);
+ res.add(split[0] + (split.length > 1 ? '/' : ''));
});
- return Array.from(res)
+ return Array.from(res);
}
private addEntry(path: string) {
- var fullPath = "";
- var folder = "";
- path.split("/").forEach(segment => {
+ let fullPath = '';
+ let folder = '';
+ path = (path.startsWith('/')) ? path.substring(1) : path;
+ path.split('/').forEach(segment => {
fullPath += segment;
- fullPath += (fullPath === path ? "" : "/");
+ fullPath += (fullPath === path ? '' : '/');
- let name = (((fullPath === path) && (!path.endsWith("/"))) ? segment : segment + "/");
+ const name = (((fullPath === path) && (!path.endsWith('/'))) ? segment : segment + '/');
this.putToFolder(folder, name);
- folder = fullPath
- })
+ folder = fullPath;
+ });
}
private putToFolder(folder: string, name: string) {
- if ("" === name || "/" === name) {
+ if ('' === name || '/' === name) {
name = null;
}
- if (folder === "") {
+ if (folder === '') {
folder = name;
name = null;
}
@@ -75,15 +76,15 @@ export class DynamicFlatNode {
isLoading: boolean;
constructor(path: string) {
- let level = path.split("/").length - 1;
- if (path.endsWith("/")) {
+ let level = path.split('/').length - 1;
+ if (path.endsWith('/')) {
level = level - 1;
}
- this.name = path.replace(/\/$/, "").match(/(.+\/)*([^\/]+)$/)[2];
+ this.name = path.replace(/\/$/, '').match(/(.+\/)*([^\/]+)$/)[2];
this.path = path;
this.level = level;
- this.expandable = path.endsWith("/");
+ this.expandable = path.endsWith('/');
}
}
@@ -92,7 +93,7 @@ export class DynamicDatabase {
storageTree = new UserFileSystem();
loadData(api: ApiService, creds: CredentialsService, filetreeComponent: FiletreeComponent, router: Router) {
- api.listDocuments("", creds.getCredentialsForApi())
+ api.listDocuments('', creds.getCredentialsForApi())
.then(res => {
this.storageTree.buildFs(> res);
@@ -110,7 +111,7 @@ export class DynamicDatabase {
}
rebuildView(filetreeComponent: FiletreeComponent) {
- let paths = this.memoizedFs();
+ const paths = this.memoizedFs();
this.storageTree.buildFs(Array.from(paths));
filetreeComponent.dataSource.data = this.storageTree.rootLevelNodes()
@@ -118,14 +119,14 @@ export class DynamicDatabase {
}
private memoizedFs() {
- let paths = new Set();
+ const paths = new Set();
this.storageTree.fs.forEach((values, key) => {
paths.add(key);
values.forEach(file => {
if (null != file) {
paths.add(key + file);
}
- })
+ });
});
return paths;
}
@@ -157,7 +158,7 @@ export class DynamicDataSource {
}
private keepExpandedNodesState() {
- let toExpand = new Set(this.expandedMemoize);
+ const toExpand = new Set(this.expandedMemoize);
let expanded = false;
do {
expanded = false;
@@ -168,7 +169,7 @@ export class DynamicDataSource {
expanded = true;
toExpand.delete(node.path);
});
- } while (toExpand.size != 0 && expanded);
+ } while (toExpand.size !== 0 && expanded);
}
constructor(private treeControl: FlatTreeControl,
@@ -253,10 +254,10 @@ export class FiletreeComponent {
treeControl: FlatTreeControl;
dataSource: DynamicDataSource;
+ error: any;
getLevel = (node: DynamicFlatNode) => node.level;
isExpandable = (node: DynamicFlatNode) => node.expandable;
hasChild = (_: number, _nodeData: DynamicFlatNode) => _nodeData.expandable;
- error: any;
constructor(private database: DynamicDatabase, private api: ApiService, private creds: CredentialsService,
private router: Router, public dialog: MatDialog) {
@@ -269,19 +270,19 @@ export class FiletreeComponent {
addUiFolderWithPath(path: string) {
const dialogRef = this.dialog.open(AddFolderDialog, {
width: '250px',
- data: {folderPath: ""}
+ data: {folderPath: ''}
});
dialogRef.afterClosed().subscribe(result => {
if (result !== undefined) {
- this.database.storageTree.uiCreatedFolders.add("" !== path ? path + result : result);
+ this.database.storageTree.uiCreatedFolders.add('' !== path ? path + result : result);
this.database.rebuildView(this);
}
});
}
addUiFolder() {
- this.addUiFolderWithPath("");
+ this.addUiFolderWithPath('');
}
addUiFolderWithpathFromName(event) {
@@ -302,13 +303,13 @@ export class FiletreeComponent {
this.error = '';
this.removePathFromUiCreatedFolders(path);
this.api.deleteDocument(path, this.creds.getCredentialsForApi())
- .then(res => this.loadTree())
+ .then(_ => this.loadTree())
.catch(err => this.error = 'Delete failed: ' + ErrorMessageUtil.extract(err));
}
private removePathFromUiCreatedFolders(path: string) {
- let pathPrefix = path.replace(/\/$/, "");
- let toRemove = Array.from(this.database.storageTree.uiCreatedFolders)
+ const pathPrefix = path.replace(/\/$/, '');
+ const toRemove = Array.from(this.database.storageTree.uiCreatedFolders)
.filter(it => it.startsWith(pathPrefix));
toRemove.forEach(remove => this.database.storageTree.uiCreatedFolders.delete(remove));
}
@@ -316,7 +317,7 @@ export class FiletreeComponent {
uploadFile(event) {
this.error = '';
this.api.uploadDocument(event.target.files[0], event.target.files[0].name, this.creds.getCredentialsForApi())
- .then(res => this.loadTree())
+ .then(_ => this.loadTree())
.catch(err => {
this.error = 'Upload failed: ' + ErrorMessageUtil.extract(err);
});
@@ -328,7 +329,7 @@ export class FiletreeComponent {
event.currentTarget.files[0],
event.currentTarget.name + event.currentTarget.files[0].name,
this.creds.getCredentialsForApi())
- .then(res => this.loadTree())
+ .then(_ => this.loadTree())
.catch(err => {
this.error = 'Upload failed: ' + ErrorMessageUtil.extract(err);
});
diff --git a/frontend/datasafe-ui/src/app/component/login/login.component.ts b/frontend/datasafe-ui/src/app/component/login/login.component.ts
index f943deeff..b2c947f34 100644
--- a/frontend/datasafe-ui/src/app/component/login/login.component.ts
+++ b/frontend/datasafe-ui/src/app/component/login/login.component.ts
@@ -3,7 +3,7 @@ import {Router} from '@angular/router';
import {FormBuilder, FormControl, Validators} from '@angular/forms';
import {CredentialsService} from '../../service/credentials/credentials.service';
import {Env, FieldErrorStateMatcher} from '../../app.component';
-import {MAT_DIALOG_DATA, MatDialog, MatDialogRef} from '@angular/material';
+import {MAT_DIALOG_DATA, MatDialog, MatDialogRef} from '@angular/material/dialog';
export interface ApiConfigData {
apiUrl: string;
diff --git a/frontend/datasafe-ui/src/app/component/register/register.component.ts b/frontend/datasafe-ui/src/app/component/register/register.component.ts
index 4aff54c12..38edd7acc 100644
--- a/frontend/datasafe-ui/src/app/component/register/register.component.ts
+++ b/frontend/datasafe-ui/src/app/component/register/register.component.ts
@@ -1,14 +1,14 @@
-import {Component, OnInit} from '@angular/core';
-import {FormBuilder, FormControl, FormGroup, Validators} from "@angular/forms";
-import {Router} from "@angular/router";
-import {ApiService} from "../../service/api/api.service";
-import {CredentialsService} from "../../service/credentials/credentials.service";
-import {ErrorMessageUtil, FieldErrorStateMatcher, ParentOrFieldErrorStateMatcher} from "../../app.component";
+import {Component} from '@angular/core';
+import {FormBuilder, FormControl, FormGroup, ValidationErrors, ValidatorFn, Validators} from '@angular/forms';
+import {Router} from '@angular/router';
+import {ApiService} from '../../service/api/api.service';
+import {CredentialsService} from '../../service/credentials/credentials.service';
+import {ErrorMessageUtil, FieldErrorStateMatcher, ParentOrFieldErrorStateMatcher} from '../../app.component';
class PasswordsMatchControl extends FormControl {
constructor(private hidden: boolean) {
- super('', [])
+ super('', []);
}
get Hidden(): boolean {
@@ -29,7 +29,11 @@ class PasswordsMatchControl extends FormControl {
templateUrl: './register.component.html',
styleUrls: ['./register.component.css']
})
-export class RegisterComponent implements OnInit {
+export class RegisterComponent {
+
+ constructor(public router: Router, private api: ApiService, private fb: FormBuilder,
+ private creds: CredentialsService) {
+ }
userNameControl = new FormControl('', [
Validators.required,
@@ -47,37 +51,33 @@ export class RegisterComponent implements OnInit {
username: this.userNameControl,
passwords: this.passwordControl,
matchPasswords: this.passwordMatchControl
- }, {validator: RegisterComponent.checkPasswords});
-
+ }, {
+ validators: [RegisterComponent.checkPasswords]
+ });
fieldMatcher = new FieldErrorStateMatcher();
parentOrFieldMatcher = new ParentOrFieldErrorStateMatcher();
- constructor(public router: Router, private api: ApiService, private fb: FormBuilder,
- private creds: CredentialsService) {
- }
+ private static checkPasswords(): ValidatorFn { // here we have the 'passwords' group
+ return (group: FormGroup): ValidationErrors | null => {
+ const matchControl = group.controls.matchPasswords;
+ const pass = group.controls.passwords.value;
+ const confirmPass = matchControl.value;
- ngOnInit() {
+ return (matchControl.Hidden || pass === confirmPass) ? null : {notSame: true};
+ };
}
public handleCreateUserClick() {
if (!this.registerForm.valid) {
- return
+ return;
}
this.api.createUser(this.userNameControl.value, this.passwordControl.value)
- .then(res => {
+ .then(() => {
this.creds.setCredentials(this.userNameControl.value, this.passwordControl.value);
- this.router.navigate(['/user'])
+ this.router.navigate(['/user']);
})
.catch(error => this.registerForm.setErrors({'createFailed': ErrorMessageUtil.extract(error)}));
}
-
- private static checkPasswords(group: FormGroup) { // here we have the 'passwords' group
- let matchControl = group.controls.matchPasswords;
- let pass = group.controls.passwords.value;
- let confirmPass = matchControl.value;
-
- return (matchControl.Hidden || pass === confirmPass) ? null : {notSame: true}
- }
}
diff --git a/frontend/datasafe-ui/src/app/component/user/user.component.ts b/frontend/datasafe-ui/src/app/component/user/user.component.ts
index e9f0191ff..4a976da5a 100644
--- a/frontend/datasafe-ui/src/app/component/user/user.component.ts
+++ b/frontend/datasafe-ui/src/app/component/user/user.component.ts
@@ -1,7 +1,7 @@
import {Component, OnInit} from '@angular/core';
-import {Router} from "@angular/router";
-import {CredentialsService} from "../../service/credentials/credentials.service";
-import {ApiService} from "../../service/api/api.service";
+import {Router} from '@angular/router';
+import {CredentialsService} from '../../service/credentials/credentials.service';
+import {ApiService} from '../../service/api/api.service';
@Component({
selector: 'app-user',
@@ -19,11 +19,11 @@ export class UserComponent implements OnInit {
ngOnInit() {
if (null == this.creds.getCredentialsForApi()) {
- this.router.navigate([''])
+ this.router.navigate(['']);
}
}
doLogout() {
- this.router.navigate([''])
+ this.router.navigate(['']);
}
}
diff --git a/frontend/datasafe-ui/src/app/polyfills.ts b/frontend/datasafe-ui/src/app/polyfills.ts
index 5134b24e9..d42447b83 100755
--- a/frontend/datasafe-ui/src/app/polyfills.ts
+++ b/frontend/datasafe-ui/src/app/polyfills.ts
@@ -1,5 +1,4 @@
-import 'core-js/es6/reflect';
-import 'core-js/es7/reflect';
-import 'zone.js/dist/zone';
+import 'core-js/features/reflect';
+import 'zone.js';
import 'hammerjs';
-import 'web-animations-js';
\ No newline at end of file
+import 'web-animations-js';
diff --git a/frontend/datasafe-ui/src/app/service/api/api.service.ts b/frontend/datasafe-ui/src/app/service/api/api.service.ts
index 14db1ed7d..7b78662d5 100644
--- a/frontend/datasafe-ui/src/app/service/api/api.service.ts
+++ b/frontend/datasafe-ui/src/app/service/api/api.service.ts
@@ -1,137 +1,126 @@
import {Injectable} from '@angular/core';
-import {HttpClient, HttpResponse} from "@angular/common/http";
-import {Observable, of} from "rxjs";
-import {flatMap, map} from "rxjs/operators";
-import {Credentials} from "../credentials/credentials.service";
-import {Env} from "../../app.component";
+import {HttpClient, HttpResponse} from '@angular/common/http';
+import {lastValueFrom, Observable, of} from 'rxjs';
+import {mergeMap, map} from 'rxjs/operators';
+import {Credentials} from '../credentials/credentials.service';
+import {Env} from '../../app.component';
@Injectable({providedIn: 'root'})
export class ApiService {
- private static TOKEN_HEADER = "token";
+ private static TOKEN_HEADER = 'token';
apiUserName = Env.apiUsername;
apiPassword = Env.apiPassword;
private uri = Env.apiUrl;
- private authorizeUri = this.uri + "/api/authenticate";
- private createUserUri = this.uri + "/user";
- private listDocumentUri = this.uri + "/documents/";
- private putDocumentUri = this.uri + "/document/";
- private getDocumentUri = this.uri + "/document/";
- private deleteDocumentUri = this.uri + "/document/";
+ private authorizeUri = this.uri + '/api/authenticate';
+ private createUserUri = this.uri + '/user';
+ private listDocumentUri = this.uri + '/documents/';
+ private putDocumentUri = this.uri + '/document/';
+ private getDocumentUri = this.uri + '/document/';
+ private deleteDocumentUri = this.uri + '/document/';
private token: string;
+ private static headers(token: string) {
+ return {'headers': {[ApiService.TOKEN_HEADER]: token}};
+ }
+
+ private static headersWithAuth(token: string, creds: Credentials) {
+ return {'headers': {
+ [ApiService.TOKEN_HEADER]: token,
+ 'user': creds.username,
+ 'password': creds.password}
+ };
+ }
+
+ private static extractToken(response: HttpResponse<{}>): string {
+ return response.headers.get(ApiService.TOKEN_HEADER);
+ }
+
constructor(private httpClient: HttpClient) {
}
authorize() {
- let result = this.httpClient.post(
+ const result = this.httpClient.post(
this.authorizeUri,
- {"userName": this.apiUserName, "password": this.apiPassword},
+ {'userName': this.apiUserName, 'password': this.apiPassword},
{observe: 'response'}
);
result.subscribe(res => {
- this.token = ApiService.extractToken(res)
+ this.token = ApiService.extractToken(res);
});
return result;
}
- createUser(username: string, password: string) {
+ async createUser(username: string, password: string) {
+ // tslint:disable-next-line:no-console
console.info(`Creating user using api URL '${this.uri}'`);
- return this.withAuthorization()
- .pipe(flatMap(token =>
- this.httpClient.put(
- this.createUserUri,
- {"userName": username, "password": password},
- ApiService.headers(token)
- ))).toPromise();
+ return await lastValueFrom(this.withAuthorization()
+ .pipe(mergeMap(token =>
+ this.httpClient.put(this.createUserUri, {'userName': username, 'password': password}, ApiService.headers(token))
+ )));
}
- listDocuments(path: string, creds: Credentials) {
- return this.withAuthorization()
- .pipe(flatMap(token =>
- this.httpClient.get(
- this.listDocumentUri + path,
- ApiService.headersWithAuth(token, creds)
- ))).toPromise();
+ async listDocuments(path: string, creds: Credentials) {
+ return await lastValueFrom(this.withAuthorization()
+ .pipe(mergeMap(token =>
+ this.httpClient.get(this.listDocumentUri + path, ApiService.headersWithAuth(token, creds))
+ )));
}
- uploadDocument(document, path: string, creds: Credentials) {
- let formData: FormData = new FormData();
+ async uploadDocument(document: string | Blob, path: string, creds: Credentials) {
+ const formData: FormData = new FormData();
formData.append('file', document);
- return this.withAuthorization()
- .pipe(flatMap(token =>
+ return await lastValueFrom(this.withAuthorization()
+ .pipe(mergeMap(token =>
this.httpClient.put(
this.putDocumentUri + path,
formData,
- {
- "headers": ApiService.headersWithAuth(token, creds)["headers"],
- responseType: 'blob' as 'json'
- })
- )).toPromise();
+ {'headers': ApiService.headersWithAuth(token, creds)['headers'], responseType: 'blob' as 'json'}
+ )
+ )));
}
downloadDocument(path: string, creds: Credentials) {
this.withAuthorization()
- .pipe(flatMap(token =>
+ .pipe(mergeMap(token =>
this.httpClient.get(
this.getDocumentUri + path,
- {
- "headers": ApiService.headersWithAuth(token, creds)["headers"],
- responseType: 'blob' as 'json'
- }
+ {'headers': ApiService.headersWithAuth(token, creds)['headers'], responseType: 'blob' as 'json'}
)
)).subscribe(
(response: any) => {
- let dataType = response.type;
- let binaryData = [];
+ const dataType = response.type;
+ const binaryData = [];
binaryData.push(response);
- let downloadLink = document.createElement('a');
+ const downloadLink = document.createElement('a');
downloadLink.href = window.URL.createObjectURL(new Blob(binaryData, {type: dataType}));
- downloadLink.setAttribute('download', path.match(/(.+\/)*([^/]+)$/)[2]);
+ downloadLink.setAttribute('download', RegExp(/(.+\/)*([^/]+)$/).exec(path)[2]);
document.body.appendChild(downloadLink);
downloadLink.click();
}
- )
+ );
}
- deleteDocument(path: string, creds: Credentials) {
- return this.withAuthorization()
- .pipe(flatMap(token =>
- this.httpClient.delete(
- this.deleteDocumentUri + path,
- ApiService.headersWithAuth(token, creds)
- ))).toPromise();
+ async deleteDocument(path: string, creds: Credentials) {
+ return await lastValueFrom(this.withAuthorization()
+ .pipe(mergeMap(token =>
+ this.httpClient.delete(this.deleteDocumentUri + path, ApiService.headersWithAuth(token, creds))
+ )));
}
- private withAuthorization() : Observable {
+ private withAuthorization(): Observable {
if (!this.token) {
return this.authorize()
- .pipe(map((res) => ApiService.extractToken(res)))
+ .pipe(map((res) => ApiService.extractToken(res)));
}
- return of(this.token)
- }
-
- private static headers(token: string) {
- return {"headers": {[ApiService.TOKEN_HEADER]: token}};
- }
-
- private static headersWithAuth(token: string, creds: Credentials) {
- return {"headers": {
- [ApiService.TOKEN_HEADER]: token,
- "user": creds.username,
- "password": creds.password}
- };
- }
-
- private static extractToken(response: HttpResponse<{}>) : string {
- return response.headers.get(ApiService.TOKEN_HEADER)
+ return of(this.token);
}
}
diff --git a/frontend/datasafe-ui/src/app/service/credentials/credentials.service.ts b/frontend/datasafe-ui/src/app/service/credentials/credentials.service.ts
index b98faf8c5..545b9e60c 100644
--- a/frontend/datasafe-ui/src/app/service/credentials/credentials.service.ts
+++ b/frontend/datasafe-ui/src/app/service/credentials/credentials.service.ts
@@ -20,10 +20,10 @@ export class CredentialsService {
constructor() { }
setCredentials(username: string, password: string) {
- this.credentials = new Credentials(username, password)
+ this.credentials = new Credentials(username, password);
}
- getCredentialsForApi() : Credentials {
- return this.credentials
+ getCredentialsForApi(): Credentials {
+ return this.credentials;
}
}
diff --git a/frontend/datasafe-ui/src/env.js b/frontend/datasafe-ui/src/env.js
index 6d0d1f1c2..5526ce23a 100644
--- a/frontend/datasafe-ui/src/env.js
+++ b/frontend/datasafe-ui/src/env.js
@@ -1,9 +1,9 @@
(function (window) {
window.__env = window.__env || {};
- window.__env.apiUrl = '${API_URL}';
+ window.__env.apiUrl = 'http://localhost:8080';
// ideally these are not necessary, but API is protected, so supplying it for docker-local deployment
- window.__env.apiUsername = '${API_USERNAME}';
- window.__env.apiPassword = '${API_PASSWORD}';
+ window.__env.apiUsername = 'username';
+ window.__env.apiPassword = 'password';
}(this));
\ No newline at end of file
diff --git a/frontend/datasafe-ui/src/env.prod.js b/frontend/datasafe-ui/src/env.prod.js
new file mode 100644
index 000000000..fc7e7a057
--- /dev/null
+++ b/frontend/datasafe-ui/src/env.prod.js
@@ -0,0 +1,9 @@
+(function (window) {
+ window.__env = window.__env || {};
+
+ window.__env.apiUrl = `${API_URL}`;
+ // ideally these are not necessary, but API is protected, so supplying it for docker-local deployment
+ window.__env.apiUsername = '${API_USERNAME}';
+ window.__env.apiPassword = '${API_PASSWORD}';
+
+}(this));
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index b15b12999..cc049524c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -333,27 +333,6 @@
jaxb-api
${jaxb-api.version}
-
- com.fasterxml.jackson.core
- jackson-core
- ${jackson.version}
-
-
- com.fasterxml.jackson.core
- jackson-databind
- ${jackson.version}
-
-
- com.fasterxml.jackson.core
- jackson-annotations
- ${jackson.version}
-
-
- com.fasterxml.jackson.dataformat
- jackson-dataformat-yaml
- ${jackson.version}
- test
-