diff --git a/domismp-tests/domismp-tests-api/groovy/oracle-4.1_integration_test_data.sql b/domismp-tests/domismp-tests-api/groovy/oracle-4.1_integration_test_data.sql index 8d0e7f054b29de275c7523e98c15d069e7f95ccf..941441d2d7ed27665aaa7c94db616e0178588779 100644 --- a/domismp-tests/domismp-tests-api/groovy/oracle-4.1_integration_test_data.sql +++ b/domismp-tests/domismp-tests-api/groovy/oracle-4.1_integration_test_data.sql @@ -19,6 +19,8 @@ DELETE FROM SMP_SUBRESOURCE; DELETE FROM SMP_SUBRESOURCE_AUD; DELETE FROM SMP_RESOURCE; DELETE FROM SMP_RESOURCE_AUD; +DELETE FROM SMP_DOCUMENT_PROPERTY; +DELETE FROM SMP_DOCUMENT_PROPERTY_AUD; DELETE FROM SMP_DOCUMENT_VERSION; DELETE FROM SMP_DOCUMENT_VERSION_AUD; DELETE FROM SMP_DOCUMENT; diff --git a/smp-angular/package-lock.json b/smp-angular/package-lock.json index eb0db505958e31d36b78244ced33b36c178056ad..9fd3da8f8fe4a48170d3ae7466fb5e1be26fbee2 100644 --- a/smp-angular/package-lock.json +++ b/smp-angular/package-lock.json @@ -24,14 +24,14 @@ "@angular/platform-browser-dynamic": "^16.2.12", "@angular/platform-server": "^16.2.12", "@angular/router": "^16.2.12", - "@codemirror/commands": "^6.5.0", - "@codemirror/language": "^6.10.1", + "@codemirror/commands": "^6.6.0", + "@codemirror/language": "^6.10.2", "@codemirror/language-data": "^6.5.1", - "@codemirror/merge": "^6.6.2", + "@codemirror/merge": "^6.6.3", + "@codemirror/search": "^6.5.6", "@codemirror/theme-one-dark": "^6.1.2", - "@codemirror/view": "^6.26.3", + "@codemirror/view": "^6.28.1", "@swimlane/ngx-datatable": "^20.1.0", - "codemirror": "^6.0.1", "file-saver": "^2.0.5", "rxjs": "^7.8.1", "ts-helpers": "^1.1.2", @@ -2792,13 +2792,13 @@ } }, "node_modules/@codemirror/commands": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.5.0.tgz", - "integrity": "sha512-rK+sj4fCAN/QfcY9BEzYMgp4wwL/q5aj/VfNSoH1RWPF9XS/dUwBkvlL3hpWgEjOqlpdN1uLC9UkjJ4tmyjJYg==", + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.6.0.tgz", + "integrity": "sha512-qnY+b7j1UNcTS31Eenuc/5YJB6gQOzkUoNmJQc0rznwqSRpeaWWpjkWy2C/MPTcePpsKJEM26hXrOXl1+nceXg==", "dependencies": { "@codemirror/language": "^6.0.0", "@codemirror/state": "^6.4.0", - "@codemirror/view": "^6.0.0", + "@codemirror/view": "^6.27.0", "@lezer/common": "^1.1.0" } }, @@ -3043,9 +3043,9 @@ } }, "node_modules/@codemirror/language": { - "version": "6.10.1", - "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.10.1.tgz", - "integrity": "sha512-5GrXzrhq6k+gL5fjkAwt90nYDmjlzTIJV8THnxNFtNKWotMIlzzN+CpqxqwXOECnUdOndmSeWntVrVcv5axWRQ==", + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.10.2.tgz", + "integrity": "sha512-kgbTYTo0Au6dCSc/TFy7fK3fpJmgHDv1sG1KNQKJXVi+xBTEeBPY/M30YXiU6mMXeH+YIDLsbrT4ZwNRdtF+SA==", "dependencies": { "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.23.0", @@ -3103,9 +3103,9 @@ } }, "node_modules/@codemirror/merge": { - "version": "6.6.2", - "resolved": "https://registry.npmjs.org/@codemirror/merge/-/merge-6.6.2.tgz", - "integrity": "sha512-g6ltz4OLYbudvZfgLUp30yQngL+jMXrb9EHKXKT0lkWm6NjWxmjG4I2gcPqMlyb43UCPgEVlylKRBbj9AJ7H9A==", + "version": "6.6.3", + "resolved": "https://registry.npmjs.org/@codemirror/merge/-/merge-6.6.3.tgz", + "integrity": "sha512-sui2Y/qKyvpPs1VJysAC4Q4w9Oe6MQM8LPOLphaQzhP6wfY28Flq/xuaBBUGDRP1Rtu7Ksf6zwdHPLbMn3zuBw==", "dependencies": { "@codemirror/language": "^6.0.0", "@codemirror/state": "^6.0.0", @@ -3141,9 +3141,9 @@ } }, "node_modules/@codemirror/view": { - "version": "6.26.3", - "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.26.3.tgz", - "integrity": "sha512-gmqxkPALZjkgSxIeeweY/wGQXBfwTUaLs8h7OKtSwfbj9Ct3L11lD+u1sS7XHppxFQoMDiMDp07P9f3I2jWOHw==", + "version": "6.28.1", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.28.1.tgz", + "integrity": "sha512-BUWr+zCJpMkA/u69HlJmR+YkV4yPpM81HeMkOMZuwFa8iM5uJdEPKAs1icIRZKkKmy0Ub1x9/G3PQLTXdpBxrQ==", "dependencies": { "@codemirror/state": "^6.4.0", "style-mod": "^4.1.0", @@ -5392,9 +5392,9 @@ "dev": true }, "node_modules/@types/cors": { - "version": "2.8.13", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.13.tgz", - "integrity": "sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==", + "version": "2.8.17", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", + "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", "dev": true, "dependencies": { "@types/node": "*" @@ -6565,12 +6565,12 @@ } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -6979,20 +6979,6 @@ "node": ">=6" } }, - "node_modules/codemirror": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.1.tgz", - "integrity": "sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==", - "dependencies": { - "@codemirror/autocomplete": "^6.0.0", - "@codemirror/commands": "^6.0.0", - "@codemirror/language": "^6.0.0", - "@codemirror/lint": "^6.0.0", - "@codemirror/search": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0" - } - }, "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -8026,9 +8012,9 @@ } }, "node_modules/engine.io": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.4.2.tgz", - "integrity": "sha512-FKn/3oMiJjrOEOeUub2WCox6JhxBXq/Zn3fZOMCBxKnNYtsdKjxhl7yR3fZhM9PV+rdE75SU5SYMc+2PGzo+Tg==", + "version": "6.5.5", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.5.tgz", + "integrity": "sha512-C5Pn8Wk+1vKBoHghJODM63yk8MvrO9EWZUfkAt5HAqIgPE4/8FF0PEGHXtEd40l223+cE5ABWuPzm38PHFXfMA==", "dev": true, "dependencies": { "@types/cookie": "^0.4.1", @@ -8039,17 +8025,17 @@ "cookie": "~0.4.1", "cors": "~2.8.5", "debug": "~4.3.1", - "engine.io-parser": "~5.0.3", - "ws": "~8.11.0" + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1" }, "engines": { - "node": ">=10.0.0" + "node": ">=10.2.0" } }, "node_modules/engine.io-parser": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.6.tgz", - "integrity": "sha512-tjuoZDMAdEhVnSFleYPCtdL2GXwVTGtNjoeJd9IhIG3C1xs9uwxqRNEu5WpnDZCaozwVlK/nuQhpodhXSIMaxw==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.2.tgz", + "integrity": "sha512-RcyUFKA93/CXH20l4SoVvzZfrSDMOTUS3bWVpTt2FuFP+XYrL8i8oonHP7WInRyVHXh0n/ORtoeiE1os+8qkSw==", "dev": true, "engines": { "node": ">=10.0.0" @@ -8913,9 +8899,9 @@ "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==" }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "dependencies": { "to-regex-range": "^5.0.1" @@ -10789,9 +10775,9 @@ } }, "node_modules/jsdom/node_modules/ws": { - "version": "7.5.9", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", - "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", "dev": true, "engines": { "node": ">=8.3.0" @@ -16454,29 +16440,31 @@ } }, "node_modules/socket.io": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.6.1.tgz", - "integrity": "sha512-KMcaAi4l/8+xEjkRICl6ak8ySoxsYG+gG6/XfRCPJPQ/haCRIJBTL4wIl8YCsmtaBovcAXGLOShyVWQ/FG8GZA==", + "version": "4.7.5", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.5.tgz", + "integrity": "sha512-DmeAkF6cwM9jSfmp6Dr/5/mfMwb5Z5qRrSXLpo3Fq5SqyU8CMF15jIN4ZhfSwu35ksM1qmHZDQ/DK5XTccSTvA==", "dev": true, "dependencies": { "accepts": "~1.3.4", "base64id": "~2.0.0", + "cors": "~2.8.5", "debug": "~4.3.2", - "engine.io": "~6.4.1", + "engine.io": "~6.5.2", "socket.io-adapter": "~2.5.2", - "socket.io-parser": "~4.2.1" + "socket.io-parser": "~4.2.4" }, "engines": { - "node": ">=10.0.0" + "node": ">=10.2.0" } }, "node_modules/socket.io-adapter": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz", - "integrity": "sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==", + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz", + "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==", "dev": true, "dependencies": { - "ws": "~8.11.0" + "debug": "~4.3.4", + "ws": "~8.17.1" } }, "node_modules/socket.io-parser": { @@ -18191,27 +18179,6 @@ "webpack": "^4.0.0 || ^5.0.0" } }, - "node_modules/webpack-dev-server/node_modules/ws": { - "version": "8.17.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.0.tgz", - "integrity": "sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow==", - "dev": true, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, "node_modules/webpack-merge": { "version": "5.9.0", "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.9.0.tgz", @@ -18539,16 +18506,16 @@ "dev": true }, "node_modules/ws": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", - "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", "dev": true, "engines": { "node": ">=10.0.0" }, "peerDependencies": { "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" + "utf-8-validate": ">=5.0.2" }, "peerDependenciesMeta": { "bufferutil": { @@ -20530,13 +20497,13 @@ } }, "@codemirror/commands": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.5.0.tgz", - "integrity": "sha512-rK+sj4fCAN/QfcY9BEzYMgp4wwL/q5aj/VfNSoH1RWPF9XS/dUwBkvlL3hpWgEjOqlpdN1uLC9UkjJ4tmyjJYg==", + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.6.0.tgz", + "integrity": "sha512-qnY+b7j1UNcTS31Eenuc/5YJB6gQOzkUoNmJQc0rznwqSRpeaWWpjkWy2C/MPTcePpsKJEM26hXrOXl1+nceXg==", "requires": { "@codemirror/language": "^6.0.0", "@codemirror/state": "^6.4.0", - "@codemirror/view": "^6.0.0", + "@codemirror/view": "^6.27.0", "@lezer/common": "^1.1.0" } }, @@ -20781,9 +20748,9 @@ } }, "@codemirror/language": { - "version": "6.10.1", - "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.10.1.tgz", - "integrity": "sha512-5GrXzrhq6k+gL5fjkAwt90nYDmjlzTIJV8THnxNFtNKWotMIlzzN+CpqxqwXOECnUdOndmSeWntVrVcv5axWRQ==", + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.10.2.tgz", + "integrity": "sha512-kgbTYTo0Au6dCSc/TFy7fK3fpJmgHDv1sG1KNQKJXVi+xBTEeBPY/M30YXiU6mMXeH+YIDLsbrT4ZwNRdtF+SA==", "requires": { "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.23.0", @@ -20841,9 +20808,9 @@ } }, "@codemirror/merge": { - "version": "6.6.2", - "resolved": "https://registry.npmjs.org/@codemirror/merge/-/merge-6.6.2.tgz", - "integrity": "sha512-g6ltz4OLYbudvZfgLUp30yQngL+jMXrb9EHKXKT0lkWm6NjWxmjG4I2gcPqMlyb43UCPgEVlylKRBbj9AJ7H9A==", + "version": "6.6.3", + "resolved": "https://registry.npmjs.org/@codemirror/merge/-/merge-6.6.3.tgz", + "integrity": "sha512-sui2Y/qKyvpPs1VJysAC4Q4w9Oe6MQM8LPOLphaQzhP6wfY28Flq/xuaBBUGDRP1Rtu7Ksf6zwdHPLbMn3zuBw==", "requires": { "@codemirror/language": "^6.0.0", "@codemirror/state": "^6.0.0", @@ -20879,9 +20846,9 @@ } }, "@codemirror/view": { - "version": "6.26.3", - "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.26.3.tgz", - "integrity": "sha512-gmqxkPALZjkgSxIeeweY/wGQXBfwTUaLs8h7OKtSwfbj9Ct3L11lD+u1sS7XHppxFQoMDiMDp07P9f3I2jWOHw==", + "version": "6.28.1", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.28.1.tgz", + "integrity": "sha512-BUWr+zCJpMkA/u69HlJmR+YkV4yPpM81HeMkOMZuwFa8iM5uJdEPKAs1icIRZKkKmy0Ub1x9/G3PQLTXdpBxrQ==", "requires": { "@codemirror/state": "^6.4.0", "style-mod": "^4.1.0", @@ -22678,9 +22645,9 @@ "dev": true }, "@types/cors": { - "version": "2.8.13", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.13.tgz", - "integrity": "sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==", + "version": "2.8.17", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", + "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", "dev": true, "requires": { "@types/node": "*" @@ -23652,12 +23619,12 @@ } }, "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "requires": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" } }, "browser-process-hrtime": { @@ -23939,20 +23906,6 @@ "shallow-clone": "^3.0.0" } }, - "codemirror": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.1.tgz", - "integrity": "sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==", - "requires": { - "@codemirror/autocomplete": "^6.0.0", - "@codemirror/commands": "^6.0.0", - "@codemirror/language": "^6.0.0", - "@codemirror/lint": "^6.0.0", - "@codemirror/search": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0" - } - }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -24769,9 +24722,9 @@ } }, "engine.io": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.4.2.tgz", - "integrity": "sha512-FKn/3oMiJjrOEOeUub2WCox6JhxBXq/Zn3fZOMCBxKnNYtsdKjxhl7yR3fZhM9PV+rdE75SU5SYMc+2PGzo+Tg==", + "version": "6.5.5", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.5.tgz", + "integrity": "sha512-C5Pn8Wk+1vKBoHghJODM63yk8MvrO9EWZUfkAt5HAqIgPE4/8FF0PEGHXtEd40l223+cE5ABWuPzm38PHFXfMA==", "dev": true, "requires": { "@types/cookie": "^0.4.1", @@ -24782,14 +24735,14 @@ "cookie": "~0.4.1", "cors": "~2.8.5", "debug": "~4.3.1", - "engine.io-parser": "~5.0.3", - "ws": "~8.11.0" + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1" } }, "engine.io-parser": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.6.tgz", - "integrity": "sha512-tjuoZDMAdEhVnSFleYPCtdL2GXwVTGtNjoeJd9IhIG3C1xs9uwxqRNEu5WpnDZCaozwVlK/nuQhpodhXSIMaxw==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.2.tgz", + "integrity": "sha512-RcyUFKA93/CXH20l4SoVvzZfrSDMOTUS3bWVpTt2FuFP+XYrL8i8oonHP7WInRyVHXh0n/ORtoeiE1os+8qkSw==", "dev": true }, "enhanced-resolve": { @@ -25449,9 +25402,9 @@ "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==" }, "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "requires": { "to-regex-range": "^5.0.1" @@ -26877,9 +26830,9 @@ "dev": true }, "ws": { - "version": "7.5.9", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", - "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", "dev": true, "requires": {} } @@ -31141,26 +31094,28 @@ "dev": true }, "socket.io": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.6.1.tgz", - "integrity": "sha512-KMcaAi4l/8+xEjkRICl6ak8ySoxsYG+gG6/XfRCPJPQ/haCRIJBTL4wIl8YCsmtaBovcAXGLOShyVWQ/FG8GZA==", + "version": "4.7.5", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.5.tgz", + "integrity": "sha512-DmeAkF6cwM9jSfmp6Dr/5/mfMwb5Z5qRrSXLpo3Fq5SqyU8CMF15jIN4ZhfSwu35ksM1qmHZDQ/DK5XTccSTvA==", "dev": true, "requires": { "accepts": "~1.3.4", "base64id": "~2.0.0", + "cors": "~2.8.5", "debug": "~4.3.2", - "engine.io": "~6.4.1", + "engine.io": "~6.5.2", "socket.io-adapter": "~2.5.2", - "socket.io-parser": "~4.2.1" + "socket.io-parser": "~4.2.4" } }, "socket.io-adapter": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz", - "integrity": "sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==", + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz", + "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==", "dev": true, "requires": { - "ws": "~8.11.0" + "debug": "~4.3.4", + "ws": "~8.17.1" } }, "socket.io-parser": { @@ -32483,13 +32438,6 @@ "range-parser": "^1.2.1", "schema-utils": "^4.0.0" } - }, - "ws": { - "version": "8.17.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.0.tgz", - "integrity": "sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow==", - "dev": true, - "requires": {} } } }, @@ -32680,9 +32628,9 @@ "dev": true }, "ws": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", - "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", "dev": true, "requires": {} }, diff --git a/smp-angular/package.json b/smp-angular/package.json index 8fe7ce8d5e8dd3b0c6f9a7fd80613dddc04b3dbb..ce9ed65b29789115c65bdef4da0590bd30d4cd41 100644 --- a/smp-angular/package.json +++ b/smp-angular/package.json @@ -31,13 +31,13 @@ "@swimlane/ngx-datatable": "^20.1.0", "@angular-material-components/datetime-picker": "^16.0.1", "@angular-material-components/moment-adapter": "^16.0.1", - "codemirror": "^6.0.1", - "@codemirror/view": "^6.26.3", - "@codemirror/commands": "^6.5.0", - "@codemirror/language": "^6.10.1", + "@codemirror/view": "^6.28.1", + "@codemirror/search": "^6.5.6", + "@codemirror/commands": "^6.6.0", + "@codemirror/language": "^6.10.2", "@codemirror/language-data": "^6.5.1", "@codemirror/theme-one-dark": "^6.1.2", - "@codemirror/merge": "^6.6.2", + "@codemirror/merge": "^6.6.3", "file-saver": "^2.0.5", "rxjs": "^7.8.1", "ts-helpers": "^1.1.2", diff --git a/smp-angular/src/_smp-all-themes.scss b/smp-angular/src/_smp-all-themes.scss index 5e919e9fde8faa3451e898e738839a3d098bbc42..6deaa632784c6d80eaf1380dd85d63dc64e3e6ba 100644 --- a/smp-angular/src/_smp-all-themes.scss +++ b/smp-angular/src/_smp-all-themes.scss @@ -38,9 +38,9 @@ } .mat-mdc-table .mat-mdc-header-cell { - padding: 5px !important; + padding:2px 5px !important; box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 1px 2px 0 rgba(0, 0, 0, 0.24); - background-color: smp.get-theme-color($theme, primary, 800, 0.1) !important; + background-color: smp.get-theme-color($theme, primary, 200, 1) !important; text-align: center; } diff --git a/smp-angular/src/app/app.module.ts b/smp-angular/src/app/app.module.ts index d649da40fd94addffeddbdb6883f6165a1d69ac1..d72cf927d8e4b2bc120b68ae604fdca3c2c5ff06 100644 --- a/smp-angular/src/app/app.module.ts +++ b/smp-angular/src/app/app.module.ts @@ -121,7 +121,7 @@ import {ResourceDetailsPanelComponent} from "./edit/edit-resources/resource-deta import {ResourceDocumentPanelComponent} from "./edit/edit-resources/resource-document-panel/resource-document-panel.component"; import {DocumentWizardDialogComponent} from "./edit/edit-resources/document-wizard-dialog/document-wizard-dialog.component"; import {SubresourcePanelComponent} from "./edit/edit-resources/subresource-panel/subresource-panel.component"; -import {SubresourceDialogComponent} from "./edit/edit-resources/subresource-panel/resource-dialog/subresource-dialog.component"; +import {SubresourceDialogComponent} from "./edit/edit-resources/subresource-panel/subresource-dialog/subresource-dialog.component"; import {SubresourceDocumentPanelComponent} from "./edit/edit-resources/subresource-document-panel/subresource-document-panel.component"; import {SubresourceDocumentWizardComponent} from "./edit/edit-resources/subresource-document-wizard-dialog/subresource-document-wizard.component"; import {SmpWarningPanelComponent} from "./common/components/smp-warning-panel/smp-warning-panel.component"; @@ -158,6 +158,12 @@ import {HttpSessionInterceptor} from "./http/http-session-interceptor"; import { SessionExpirationDialogComponent } from "./common/dialogs/session-expiration-dialog/session-expiration-dialog.component"; +import { + DocumentPropertiesPanelComponent +} from "./common/panels/document-properties-panel/document-properties-panel.component"; +import { + DocumentPropertyDialogComponent +} from "./common/dialogs/document-property-dialog/document-property-dialog.component"; @NgModule({ @@ -190,6 +196,8 @@ import { DnsToolsComponent, DnsQueryPanelComponent, DocumentWizardDialogComponent, + DocumentPropertiesPanelComponent, + DocumentPropertyDialogComponent, DomainGroupComponent, DomainPanelComponent, DomainResourceTypePanelComponent, diff --git a/smp-angular/src/app/common/components/smp-editor/smp-editor.component.css b/smp-angular/src/app/common/components/smp-editor/smp-editor.component.css index 40f877779fd4d6868d776a3d5096024c43410169..ab91721a1c58e910fb232b3d30fd3a74ce6f0061 100644 --- a/smp-angular/src/app/common/components/smp-editor/smp-editor.component.css +++ b/smp-angular/src/app/common/components/smp-editor/smp-editor.component.css @@ -1,3 +1,9 @@ -.cm-gutters { - padding-top: 1.4em; + +.smp-document-editor { + display:block; + overflow: auto; + flex: 1; + align-self: stretch; + flex-direction: column; + border: ridge 3px #b0bec5 } diff --git a/smp-angular/src/app/common/components/smp-editor/smp-editor.component.html b/smp-angular/src/app/common/components/smp-editor/smp-editor.component.html index b11a0efcc28880bce42a12170fbb6f64dd0de7bc..2349abfd26064ddbe3471b1026bd9e859b0a1fbe 100644 --- a/smp-angular/src/app/common/components/smp-editor/smp-editor.component.html +++ b/smp-angular/src/app/common/components/smp-editor/smp-editor.component.html @@ -1,2 +1,3 @@ +<!-- The nested root element --> <div #editorRoot id="editorRoot"> </div> diff --git a/smp-angular/src/app/common/components/smp-editor/smp-editor.component.ts b/smp-angular/src/app/common/components/smp-editor/smp-editor.component.ts index c3228be08f0383d62e9e88bf7ff2cc77fa37d404..a3238c96351aa1d8a0579851f92ca28aa5c086bc 100644 --- a/smp-angular/src/app/common/components/smp-editor/smp-editor.component.ts +++ b/smp-angular/src/app/common/components/smp-editor/smp-editor.component.ts @@ -8,16 +8,38 @@ import { ViewChild, } from '@angular/core'; import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms'; -import {EditorView} from "@codemirror/view"; -import {basicSetup} from "codemirror"; +import { + EditorView, + lineNumbers, + keymap, + highlightActiveLineGutter, + highlightSpecialChars, + drawSelection, + dropCursor, + rectangularSelection, crosshairCursor, highlightActiveLine +} from "@codemirror/view"; import {Compartment, EditorState, Extension} from "@codemirror/state" -import {LanguageSupport} from "@codemirror/language" +import { + bracketMatching, + defaultHighlightStyle, + foldGutter, foldKeymap, indentOnInput, + LanguageSupport, + syntaxHighlighting +} from "@codemirror/language" import {javascript} from "@codemirror/lang-javascript" import {xml} from "@codemirror/lang-xml" import {html} from "@codemirror/lang-html" import {json} from "@codemirror/lang-json" import {ThemeService} from "../../theme-service/theme.service"; import {oneDark} from "@codemirror/theme-one-dark"; +import {defaultKeymap, history, historyKeymap} from "@codemirror/commands"; +import { + autocompletion, + closeBrackets, + closeBracketsKeymap, completionKeymap +} from "@codemirror/autocomplete"; +import {highlightSelectionMatches, searchKeymap} from "@codemirror/search"; +import {lintKeymap} from "@codemirror/lint"; function normalizeLineEndings(str: string): string { if (!str) { @@ -46,7 +68,7 @@ export class SmpEditorComponent @Input() name = 'smpEditor'; @ViewChild('editorRoot') ref!: ElementRef<HTMLDivElement>; value = ''; - _readOnly:boolean = false; + _readOnly: boolean = false; _mimeType: string = "text/xml"; codeMirror: EditorView; @@ -54,7 +76,7 @@ export class SmpEditorComponent documentLanguage = new Compartment; themeConfig = new Compartment; - constructor( private themeService: ThemeService) { + constructor(private themeService: ThemeService) { } ngAfterViewInit() { @@ -65,16 +87,41 @@ export class SmpEditorComponent } }); // configure the default extensions - let initExtensions: Extension[] = [ - basicSetup, - EditorView.lineWrapping, - updateListenerExtension, - this.documentLanguage.of(xml()), - this.readOnlyDocument.of(EditorState.readOnly.of(false)) - ]; + let initExtensions: Extension[] = [ + lineNumbers(), + highlightActiveLineGutter(), + highlightSpecialChars(), + history(), + foldGutter(), + drawSelection(), + dropCursor(), + EditorState.allowMultipleSelections.of(true), + indentOnInput(), + syntaxHighlighting(defaultHighlightStyle, { fallback: true }), + bracketMatching(), + closeBrackets(), + autocompletion(), + rectangularSelection(), + crosshairCursor(), + highlightActiveLine(), + highlightSelectionMatches(), + keymap.of([ + ...closeBracketsKeymap, + ...defaultKeymap, + ...searchKeymap, + ...historyKeymap, + ...foldKeymap, + ...completionKeymap, + ...lintKeymap + ]), + EditorView.lineWrapping, + updateListenerExtension, + this.documentLanguage.of(xml()), + this.readOnlyDocument.of(EditorState.readOnly.of(false)) + ]; // add the dark theme extension if (this.themeService.currentTheme === "pink_blue-grey_theme" - || this.themeService.currentTheme === "purple_green_theme" ) { + || this.themeService.currentTheme === "purple_green_theme") { initExtensions.push(this.themeConfig.of(oneDark)); } this.codeMirror = new EditorView({ @@ -126,7 +173,6 @@ export class SmpEditorComponent this.codeMirror.dispatch({selection: {anchor: length, head: length}}) } - ngOnDestroy() { // is there a lighter-weight way to remove the cm instance? if (this.codeMirror) { diff --git a/smp-angular/src/app/common/components/spacer/spacer.component.css b/smp-angular/src/app/common/components/spacer/spacer.component.css new file mode 100644 index 0000000000000000000000000000000000000000..4f8f11b1a480af7b0ba1734429440f6e66588881 --- /dev/null +++ b/smp-angular/src/app/common/components/spacer/spacer.component.css @@ -0,0 +1,13 @@ +.vertical-spacer { + margin-left:5px; + width: 10px; + border-left: 2px dotted gray +} + +.horizontal-spacer { + margin-top:5px; + height: 10px; + width: 100%; + display: block; + border-top: 2px dotted gray; +} diff --git a/smp-angular/src/app/common/components/spacer/spacer.component.ts b/smp-angular/src/app/common/components/spacer/spacer.component.ts index 321bbf8d57090bb41a8b87ea3798533b7c56632f..658ba9fcffc9c9d8971808dbfcc48954d6b7f13f 100644 --- a/smp-angular/src/app/common/components/spacer/spacer.component.ts +++ b/smp-angular/src/app/common/components/spacer/spacer.component.ts @@ -2,9 +2,11 @@ import { Component, Input } from '@angular/core'; @Component({ selector: 'tool-button-spacer', + styleUrls: ['./spacer.component.css'], template: - `<span style="margin-left:5px; width: 10px;border-left: solid black"> </span> + `<span [ngClass]="vertical?'vertical-spacer':'horizontal-spacer'"> </span> ` }) export class SpacerComponent { + @Input() vertical: boolean=true; } diff --git a/smp-angular/src/app/common/dialogs/document-property-dialog/document-property-dialog.component.css b/smp-angular/src/app/common/dialogs/document-property-dialog/document-property-dialog.component.css new file mode 100644 index 0000000000000000000000000000000000000000..e60b7f9d448fca96e25305f31f33adc60c7c8b9d --- /dev/null +++ b/smp-angular/src/app/common/dialogs/document-property-dialog/document-property-dialog.component.css @@ -0,0 +1,9 @@ +.mat-form-field { + width: 100%; +} + +.property-title { + font-size: 1em; + word-wrap: break-word; + font-weight: bold +} diff --git a/smp-angular/src/app/common/dialogs/document-property-dialog/document-property-dialog.component.html b/smp-angular/src/app/common/dialogs/document-property-dialog/document-property-dialog.component.html new file mode 100644 index 0000000000000000000000000000000000000000..f3e078aeb884067fb128b2755702998ba8076bcf --- /dev/null +++ b/smp-angular/src/app/common/dialogs/document-property-dialog/document-property-dialog.component.html @@ -0,0 +1,66 @@ +<h2 mat-dialog-title>{{ formTitle }}</h2> +<mat-dialog-content style="min-height:100px;width:780px"> + <form [formGroup]="propertyForm"> + <mat-form-field appearance="fill" + style="width: 100%" + > + <mat-label>Property name:</mat-label> + <input style="width: 100%;padding: 5px" + matInput + formControlName="property"/> + <div + *ngIf="propertyForm.controls['property'].hasError('notInList')" + style="color:red; font-size: 70%"> + The property name already exists! + </div> + </mat-form-field> + <mat-form-field + *ngIf="propertyForm.controls['type'].value !== PropertyValueTypeEnum.BOOLEAN" + appearance="fill" style="width: 100%"> + <mat-label>Property value:</mat-label> + <input style="width: 100%;padding: 5px" + matInput [type]="getInputType(propertyForm.controls['type'].value)" + formControlName="value" + /> + </mat-form-field> + <mat-checkbox + *ngIf="propertyForm.controls['type'].value === PropertyValueTypeEnum.BOOLEAN" + formControlName="value" + style="width: 100%"> + {{ propertyForm.controls['property'].value }} + </mat-checkbox> + <mat-form-field appearance="fill" style="width: 100%"> + <mat-label>Property Description:</mat-label> + <input style="width: 100%;padding: 5px" + matInput + formControlName="desc" + /> + + </mat-form-field> + <mat-form-field appearance="fill" style="width: 100%"> + <mat-label>Property Type:</mat-label> + <mat-select formControlName="type" style="width: 100%"> + <mat-option *ngFor="let type of propertyTypes" [value]="type"> + {{ propertyValueTypeDescription(type) }} + </mat-option> + </mat-select> + </mat-form-field> + </form> +</mat-dialog-content> + +<mat-dialog-actions> + <button id="updatePropertyButton" + *ngIf="!isReadOnly" + mat-raised-button color="primary" (click)="submitForm()" + [disabled]="!isDirty || !propertyForm.valid"> + <mat-icon>check_circle</mat-icon> + <span>OK</span> + </button> + <button mat-raised-button color="primary" mat-dialog-close> + <mat-icon>cancel</mat-icon> + <span *ngIf="isReadOnly; else notReadOnly">Close</span> + <ng-template #notReadOnly> + <span>Cancel</span> + </ng-template> + </button> +</mat-dialog-actions> diff --git a/smp-angular/src/app/common/dialogs/document-property-dialog/document-property-dialog.component.ts b/smp-angular/src/app/common/dialogs/document-property-dialog/document-property-dialog.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..67799a5bb4f0dca4139b85a98487cd2beddbd738 --- /dev/null +++ b/smp-angular/src/app/common/dialogs/document-property-dialog/document-property-dialog.component.ts @@ -0,0 +1,218 @@ +import {Component, Inject} from '@angular/core'; +import { + MAT_DIALOG_DATA, + MatDialog, + MatDialogRef +} from '@angular/material/dialog'; +import { + AbstractControl, + UntypedFormBuilder, + UntypedFormControl, + UntypedFormGroup +} from "@angular/forms"; +import { + AlertMessageService +} from "../../../common/alert-message/alert-message.service"; +import {EntityStatus} from "../../../common/enums/entity-status.enum"; +import {HttpClient} from "@angular/common/http"; +import {DocumentPropertyRo} from "../../model/document-property-ro.model"; +import {PropertyValueTypeEnum} from "../../enums/property-value-type.enum"; +import { + PropertyValueTypeEnumUtil +} from "../../enums/utils/PropertyValueTypeEnumUtil"; + +@Component({ + selector: 'document-property-dialog', + templateUrl: './document-property-dialog.component.html', + styleUrls: ['./document-property-dialog.component.css'] +}) +export class DocumentPropertyDialogComponent { + + static readonly NEW_MODE: string = 'Create Document Property'; + static readonly EDIT_MODE: string = 'Edit Document Property'; + public propertyTypes: string[] = Object.keys(PropertyValueTypeEnum) + protected readonly PropertyValueTypeEnum = PropertyValueTypeEnum; + formTitle: string; + current: DocumentPropertyRo; + propertyForm: UntypedFormGroup; + disabled: true; + private allPropertyNames: string[] = []; + + notInList(list: string[], exception: string) { + if (!list || !exception) { + return (c: AbstractControl): { [key: string]: any } => { + return null; + } + } + + return (c: AbstractControl): { [key: string]: any } => { + if (c.value && c.value !== exception && list.includes(c.value)) + return {'notInList': {valid: false}}; + return null; + } + } + + + constructor( + public dialog: MatDialog, + protected http: HttpClient, + private dialogRef: MatDialogRef<DocumentPropertyDialogComponent>, + private alertService: AlertMessageService, + @Inject(MAT_DIALOG_DATA) public data: any, + private fb: UntypedFormBuilder) { + + this.current = {...data.row}; + this.allPropertyNames = data.allPropertyNames; + + this.updateTitle(); + + this.propertyForm = fb.group({ + 'property': new UntypedFormControl({value: '', readonly: true}, [ + this.notInList(this.allPropertyNames, this.current.property)]), + 'desc': new UntypedFormControl({value: '', readonly: true}, null), + 'type': new UntypedFormControl({value: '', readonly: true}, null), + 'value': new UntypedFormControl({value: ''}), + 'valuePattern': new UntypedFormControl({value: ''}), + 'errorMessage': new UntypedFormControl({value: ''}), + }); + + this.propertyForm.controls['errorMessage'].setValue(''); + this.updateValueState(); + } + + get isNewItem(): boolean { + return this.current?.status === EntityStatus.NEW; + } + + get isReadOnly(): boolean { + return this.current?.readonly; + } + + private updateTitle(): void { + this.formTitle = this.isNewItem ? DocumentPropertyDialogComponent.NEW_MODE : DocumentPropertyDialogComponent.EDIT_MODE; + } + + /** + * Methods validates the property with server validator. If property value is ok + * it closes the dialog else writes the errorMessage. + */ + async submitForm() { + this.checkValidity(this.propertyForm) + this.dialogRef.close(this.getCurrent()); + } + + checkValidity(g: UntypedFormGroup) { + Object.keys(g.controls).forEach(key => { + g.get(key).markAsDirty(); + }); + Object.keys(g.controls).forEach(key => { + g.get(key).markAsTouched(); + }); + //!!! updateValueAndValidity - else some filed did no update current / on blur never happened + Object.keys(g.controls).forEach(key => { + g.get(key).updateValueAndValidity(); + }); + } + + /** + * Method casts string value to correct property type for dialog component used for editing. + * @param value + * @param propertyType + */ + public valueFromPropertyStringValue(value: string, propertyType: PropertyValueTypeEnum) { + if (propertyType === PropertyValueTypeEnum.BOOLEAN) { + return value === 'true'; + } else { + return value; + } + } + + public valueToPropertyStringValue(value: any, propertyType: PropertyValueTypeEnum) { + if (propertyType === PropertyValueTypeEnum.BOOLEAN) { + return value ? 'true' : 'false'; + } else { + return value; + } + } + + getInputType(propertyType: PropertyValueTypeEnum) { + console.log("Get input type for row " + PropertyValueTypeEnumUtil.getKeyName(this.current.type)) + switch (propertyType) { + case PropertyValueTypeEnum.STRING: + case PropertyValueTypeEnum.LIST_STRING: + case PropertyValueTypeEnum.MAP_STRING: + case PropertyValueTypeEnum.FILENAME: + case PropertyValueTypeEnum.PATH: + return 'text'; + case PropertyValueTypeEnum.INTEGER: + return 'text'; + case PropertyValueTypeEnum.BOOLEAN: + return 'checkbox'; + case PropertyValueTypeEnum.REGEXP: + return 'text'; + case PropertyValueTypeEnum.EMAIL: + return 'email'; + case PropertyValueTypeEnum.URL: + return 'url'; + default: + return 'text'; + } + } + + public getCurrent(): DocumentPropertyRo { + if (!this.propertyForm.dirty) { + return this.current; + } + if (this.current.status === EntityStatus.NEW) { + this.current.property = this.propertyForm.value['property']; + } else if (this.current.status === EntityStatus.PERSISTED) { + this.current.status = EntityStatus.UPDATED; + } + this.current.desc = this.propertyForm.value['desc']; + this.current.type = this.propertyForm.value['type']; + this.current.value = this.valueToPropertyStringValue(this.propertyForm.value['value'], this.current.type); + return this.current; + } + + closeDialog() { + // just close the dialog without any result + this.dialogRef.close(null); + } + + /** + * Method updates the state of the value field based on the system default checkbox. + */ + updateValueState(): void { + + if (!this.isNewItem || this.isReadOnly) { + this.propertyForm.controls['property'].disable(); + } else { + this.propertyForm.controls['property'].enable(); + this.propertyForm.markAsDirty(); + } + + if (this.isReadOnly) { + this.propertyForm.controls['value'].disable(); + this.propertyForm.controls['desc'].disable(); + this.propertyForm.controls['type'].disable(); + } else { + this.propertyForm.controls['value'].enable(); + this.propertyForm.controls['desc'].enable(); + this.propertyForm.controls['type'].enable(); + } + // update values + this.propertyForm.controls['property'].setValue(this.current.property); + this.propertyForm.controls['desc'].setValue(this.current.desc); + this.propertyForm.controls['type'].setValue(this.current.type); + this.propertyForm.controls['value'].setValue( + this.valueFromPropertyStringValue(this.current.value, this.current.type)); + } + + get isDirty(): boolean { + return this.propertyForm.dirty; + } + + propertyValueTypeDescription(name: string): string { + return PropertyValueTypeEnumUtil.getDescription(PropertyValueTypeEnum[name]); + } +} diff --git a/smp-angular/src/app/common/dialogs/property-details-dialog/property-details-dialog.component.ts b/smp-angular/src/app/common/dialogs/property-details-dialog/property-details-dialog.component.ts index c71953731331e8d61bf9f3aa32e57a56991fd240..5c64b025cd350695b1395ad019cddf8f0747ee87 100644 --- a/smp-angular/src/app/common/dialogs/property-details-dialog/property-details-dialog.component.ts +++ b/smp-angular/src/app/common/dialogs/property-details-dialog/property-details-dialog.component.ts @@ -24,7 +24,7 @@ import { import { PropertyValidationRo } from "../../../system-settings/admin-properties/property-validate-ro.model"; -import {PropertyTypeEnum} from "../../enums/property-type.enum"; +import {PropertySourceEnum} from "../../enums/property-source.enum"; import {firstValueFrom} from "rxjs"; @Component({ @@ -44,7 +44,7 @@ export class PropertyDetailsDialogComponent implements OnInit { propertyForm: UntypedFormGroup; disabled: true; showSpinner: boolean = false; - propertyType: PropertyTypeEnum = PropertyTypeEnum.SYSTEM; + propertyType: PropertySourceEnum = PropertySourceEnum.SYSTEM; constructor( @@ -58,7 +58,7 @@ export class PropertyDetailsDialogComponent implements OnInit { this.editMode = data.edit; this.propertyType = data.propertyType; - this.propertyType = !data.propertyType ? PropertyTypeEnum.SYSTEM : data.propertyType; + this.propertyType = !data.propertyType ? PropertySourceEnum.SYSTEM : data.propertyType; this.formTitle = (this.editMode ? PropertyDetailsDialogComponent.EDIT_MODE : PropertyDetailsDialogComponent.NEW_MODE) .replace('{TYPE}', this.capitalize(this.propertyType)); @@ -111,7 +111,7 @@ export class PropertyDetailsDialogComponent implements OnInit { let request: PropertyRo = this.getCurrent(); // if domain property we do not need to validate - if (this.propertyType == PropertyTypeEnum.DOMAIN) { + if (this.propertyType == PropertySourceEnum.DOMAIN) { this.propertyForm.controls['errorMessage'].setValue(""); // we can close the dialog this.closeDialog(); @@ -258,7 +258,7 @@ export class PropertyDetailsDialogComponent implements OnInit { } get isDomainProperty(): boolean { - return this.propertyType == PropertyTypeEnum.DOMAIN; + return this.propertyType == PropertySourceEnum.DOMAIN; } capitalize<T extends string>(str: T): string { diff --git a/smp-angular/src/app/common/enums/property-type.enum.ts b/smp-angular/src/app/common/enums/property-source.enum.ts similarity index 76% rename from smp-angular/src/app/common/enums/property-type.enum.ts rename to smp-angular/src/app/common/enums/property-source.enum.ts index 95bfa7a7c0d9ccc4b7eeb86a37980d34f43d6f62..6843539905d0c09a4f03793dcd915f62f66e2013 100644 --- a/smp-angular/src/app/common/enums/property-type.enum.ts +++ b/smp-angular/src/app/common/enums/property-source.enum.ts @@ -1,5 +1,5 @@ -export enum PropertyTypeEnum { +export enum PropertySourceEnum { /** * Resource, group of domain is marked as PUBLIC. */ diff --git a/smp-angular/src/app/common/enums/property-value-type.enum.ts b/smp-angular/src/app/common/enums/property-value-type.enum.ts new file mode 100644 index 0000000000000000000000000000000000000000..426c870da61877e2e2916ec83381b86f5e53031a --- /dev/null +++ b/smp-angular/src/app/common/enums/property-value-type.enum.ts @@ -0,0 +1,12 @@ +export enum PropertyValueTypeEnum { + STRING = 'STRING', + LIST_STRING = 'LIST_STRING', + MAP_STRING = 'MAP_STRING', + FILENAME = 'FILENAME', + PATH = 'PATH', + INTEGER = 'INTEGER', + BOOLEAN = 'BOOLEAN', + REGEXP = 'REGEXP', + EMAIL = 'EMAIL', + URL = 'URL', +} diff --git a/smp-angular/src/app/common/enums/utils/PropertyValueTypeEnumUtil.ts b/smp-angular/src/app/common/enums/utils/PropertyValueTypeEnumUtil.ts new file mode 100644 index 0000000000000000000000000000000000000000..56065cde7adebbd72496bb2f10ded129ff6aa201 --- /dev/null +++ b/smp-angular/src/app/common/enums/utils/PropertyValueTypeEnumUtil.ts @@ -0,0 +1,99 @@ +import {PropertyValueTypeEnum} from "../property-value-type.enum"; + +/** + * Utility class for PropertyValueTypeEnum to return description, regular expression + * and input type for each enum value. + * + * @since 5.1 + * @author Joze Rihtarsic + */ +export class PropertyValueTypeEnumUtil { + + + static getKeyNames(): Array<string> { + return Object.keys(PropertyValueTypeEnum).filter(k => typeof PropertyValueTypeEnum[k as any] === "number"); + } + + static getKeyName(enumItem: PropertyValueTypeEnum): string { + return PropertyValueTypeEnum[enumItem]; + } + + static getDescription(enumItem: PropertyValueTypeEnum): string { + console.log("Get description for row " + enumItem) + switch (enumItem) { + case PropertyValueTypeEnum.STRING: + return 'String'; + case PropertyValueTypeEnum.LIST_STRING: + return 'List of strings'; + case PropertyValueTypeEnum.MAP_STRING: + return 'Map of strings'; + case PropertyValueTypeEnum.FILENAME: + return 'Filename'; + case PropertyValueTypeEnum.PATH: + return 'Path'; + case PropertyValueTypeEnum.INTEGER: + return 'Integer'; + case PropertyValueTypeEnum.BOOLEAN: + return 'Boolean'; + case PropertyValueTypeEnum.REGEXP: + return 'Regular expression'; + case PropertyValueTypeEnum.EMAIL: + return 'Email'; + case PropertyValueTypeEnum.URL: + return 'URL'; + default: + return ''; + } + } + + static getInputType(propertyType: PropertyValueTypeEnum): string { + console.log("Get input type for row " + propertyType) + switch (propertyType) { + case PropertyValueTypeEnum.STRING: + case PropertyValueTypeEnum.LIST_STRING: + case PropertyValueTypeEnum.MAP_STRING: + case PropertyValueTypeEnum.FILENAME: + case PropertyValueTypeEnum.PATH: + case PropertyValueTypeEnum.REGEXP: + return 'text'; + case PropertyValueTypeEnum.INTEGER: + return 'number'; + case PropertyValueTypeEnum.BOOLEAN: + return 'checkbox'; + case PropertyValueTypeEnum.EMAIL: + return 'email'; + case PropertyValueTypeEnum.URL: + return 'url'; + default: + return 'text'; + } + } + + static getRegExp(enumItem: PropertyValueTypeEnum): RegExp { + console.log("Get input pattern for row " + enumItem) + switch (enumItem) { + case PropertyValueTypeEnum.STRING: + case PropertyValueTypeEnum.LIST_STRING: + case PropertyValueTypeEnum.MAP_STRING: + case PropertyValueTypeEnum.FILENAME: + return /.*/ + case PropertyValueTypeEnum.PATH: + return /^(.+)\/([^\/]+)$/; + case PropertyValueTypeEnum.INTEGER: + return /^-?\d+$/; + case PropertyValueTypeEnum.BOOLEAN: + return /^(true|false)$/i; + case PropertyValueTypeEnum.REGEXP: + // This regular expression checks if a string could be a valid RegExp + return /^\/(.*)\/([gimuy]*)$/; + case PropertyValueTypeEnum.EMAIL: + return /^[\w-]+(\.[\w-]+)*@([\w-]+\.)+[a-zA-Z]{2,7}$/; + case PropertyValueTypeEnum.URL: + return /^(http|https):\/\/[^ "]+$/; + default: + return null; + } + } + + +} diff --git a/smp-angular/src/app/common/model/document-property-ro.model.ts b/smp-angular/src/app/common/model/document-property-ro.model.ts new file mode 100644 index 0000000000000000000000000000000000000000..e07792de6a9493eb96a30999dd140c3a84ae7156 --- /dev/null +++ b/smp-angular/src/app/common/model/document-property-ro.model.ts @@ -0,0 +1,33 @@ +/*- + * #START_LICENSE# + * smp-webapp + * %% + * Copyright (C) 2017 - 2024 European Commission | eDelivery | DomiSMP + * %% + * Licensed under the EUPL, Version 1.2 or – as soon they will be approved by the European Commission - subsequent + * versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * + * [PROJECT_HOME]\license\eupl-1.2\license.txt or https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Unless required by applicable law or agreed to in writing, software distributed under the Licence is + * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and limitations under the Licence. + * #END_LICENSE# + */ +import {SearchTableEntity} from '../../common/search-table/search-table-entity.model'; +import {PropertyValueTypeEnum} from "../enums/property-value-type.enum"; + +/** + * Document property read only model. + * @author Joze Rihtarsic + * @since 5.1 + */ +export interface DocumentPropertyRo extends SearchTableEntity { + property: string; + value: string; + type?: PropertyValueTypeEnum; + desc: string; + readonly?: boolean; +} diff --git a/smp-angular/src/app/common/model/document-ro.model.ts b/smp-angular/src/app/common/model/document-ro.model.ts index 737e367c0cbfbe757a73b763487ab8f2311afeae..cf0a058179bbb01d17fe30b2a2c6864f25314888 100644 --- a/smp-angular/src/app/common/model/document-ro.model.ts +++ b/smp-angular/src/app/common/model/document-ro.model.ts @@ -1,4 +1,8 @@ -export interface DocumentRo { +import {DocumentPropertyRo} from "./document-property-ro.model"; +import {SearchTableEntity} from "../search-table/search-table-entity.model"; +import {EntityStatus} from "../enums/entity-status.enum"; + +export interface DocumentRo extends SearchTableEntity { mimeType?: string; name?: string; documentId?: string; @@ -7,5 +11,7 @@ export interface DocumentRo { payloadVersion?:number; payloadCreatedOn?: Date; payload?:string; + payloadStatus: EntityStatus; + properties?: DocumentPropertyRo[]; } diff --git a/smp-angular/src/app/common/panels/document-properties-panel/document-properties-panel.component.html b/smp-angular/src/app/common/panels/document-properties-panel/document-properties-panel.component.html new file mode 100644 index 0000000000000000000000000000000000000000..f1668a1f42a1104e9200ff53864ac609ec6fde8e --- /dev/null +++ b/smp-angular/src/app/common/panels/document-properties-panel/document-properties-panel.component.html @@ -0,0 +1,79 @@ +<div style="display: flex;flex-direction: row;height: 100%"> + <div *ngIf="showPropertyPanel" id="document-properties-panel"> + <div style="display:flex; flex-grow: 1; flex-direction: column"> + <mat-form-field style="width: 100%"> + <mat-label>Filter</mat-label> + <input matInput (keyup)="applyFilter($event)" #input> + </mat-form-field> + <div id="property-table-container"> + <table #DocumentPropertyTable mat-table class="mat-elevation-z1" + style="max-height: 100%;flex-grow: 1;" + id="document-property-table" + [dataSource]="propertyDataSource" matSort> + + <ng-container matColumnDef="property"> + <th mat-header-cell *matHeaderCellDef mat-sort-header>Property</th> + <td mat-cell *matCellDef="let row" + [matTooltip]="row.desc">{{ row.property }} + </td> + </ng-container> + <ng-container matColumnDef="value"> + <th mat-header-cell *matHeaderCellDef mat-sort-header>Value</th> + <td mat-cell *matCellDef="let row">{{ getTableRowValue(row) }}</td> + </ng-container> + <tr mat-header-row + *matHeaderRowDef="displayedColumns; sticky:true"></tr> + <tr mat-row + *matRowDef="let odd = odd; let row; columns: displayedColumns;" + (click)="propertySelected(row)" + (dblclick)="onEditSelectedProperty()" + [ngClass]="getRowClass(row, odd)" + ></tr> + <tr class="mat-row" *matNoDataRow> + <td class="mat-cell" colspan="2">No Document properties found</td> + </tr> + </table> + </div> + <mat-paginator [pageSizeOptions]="[5, 10, 25, 100]" showFirstLastButtons + [pageSize]="10" [length]="propertyDataSource.data?.length" + aria-label="Select page of properties"></mat-paginator> + + </div> + </div> + <!-- table Toolbar --> + <div style="width: 42px"> + <button mat-mini-fab aria-label="Expand/Collapse" + matTooltip="Expand/Collapse property panel" + (click)="onToggleButtonClicked()"> + <mat-icon *ngIf="showPropertyPanel">chevron_left</mat-icon> + <mat-icon *ngIf="!showPropertyPanel">chevron_right</mat-icon> + </button> + <tool-button-spacer [vertical]="false"></tool-button-spacer> + <button id="createButton" mat-mini-fab aria-label="Create" + matTooltip="Create new property" + (click)="onCreateProperty()"> + <mat-icon>add</mat-icon> + </button> + <button id="editButton" mat-mini-fab aria-label="Edit" + matTooltip="Edit property" + (click)="onEditSelectedProperty()" + [disabled]="editButtonDisabled"> + <mat-icon>edit</mat-icon> + </button> + <button id="deleteButton" mat-mini-fab aria-label="Delete/Remove" + matTooltip="Delete selected property" + (click)="onDeleteSelectedProperty()" + [disabled]="deleteButtonDisabled" + > + <mat-icon>remove</mat-icon> + </button> + <tool-button-spacer [vertical]="false"></tool-button-spacer> + <button id="resetButton" mat-mini-fab aria-label="Reset changes" + matTooltip="Reset changes" + (click)="onResetButtonClicked()" + color="primary" + [disabled]="!cancelButtonEnabled"> + <mat-icon>refresh</mat-icon> + </button> + </div> +</div> diff --git a/smp-angular/src/app/common/panels/document-properties-panel/document-properties-panel.component.scss b/smp-angular/src/app/common/panels/document-properties-panel/document-properties-panel.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..9f1ad60fe59831908890ef23303b065d3471e78d --- /dev/null +++ b/smp-angular/src/app/common/panels/document-properties-panel/document-properties-panel.component.scss @@ -0,0 +1,39 @@ +#document-properties-panel { + display: flex; + flex-direction: column; + flex-grow: 1; + padding: 0 1em; + min-width: 600px; +} + +#property-table-container { + position: relative; + top: 0; + bottom: 0; + left: 0; + right: 0; + verflow-y: scroll; +} + +#property-table-container table { + width: 100%; +} + + +.deleted { + text-decoration: line-through !important; + font-weight: bold; +} + +.table-row-new { + color: darkgreen !important; + font-weight: bold; +} + +.table-row-updated { + font-weight: bold; +} + +.table-row { + font-weight: normal; +} diff --git a/smp-angular/src/app/common/panels/document-properties-panel/document-properties-panel.component.ts b/smp-angular/src/app/common/panels/document-properties-panel/document-properties-panel.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..3e067286b9a3ad539e3ebf02ba679208dea2c416 --- /dev/null +++ b/smp-angular/src/app/common/panels/document-properties-panel/document-properties-panel.component.ts @@ -0,0 +1,318 @@ +/*- + * #START_LICENSE# + * smp-webapp + * %% + * Copyright (C) 2017 - 2024 European Commission | eDelivery | DomiSMP + * %% + * Licensed under the EUPL, Version 1.2 or – as soon they will be approved by the European Commission - subsequent + * versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * + * [PROJECT_HOME]\license\eupl-1.2\license.txt or https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Unless required by applicable law or agreed to in writing, software distributed under the Licence is + * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and limitations under the Licence. + * #END_LICENSE# + */ +import { + AfterViewInit, + Component, + forwardRef, + Input, + ViewChild, +} from '@angular/core'; +import { + BeforeLeaveGuard +} from "../../../window/sidenav/navigation-on-leave-guard"; +import {DomainPropertyRo} from "../../../common/model/domain-property-ro.model"; +import {MatTable, MatTableDataSource} from "@angular/material/table"; +import {EntityStatus} from "../../../common/enums/entity-status.enum"; +import { + ControlContainer, + ControlValueAccessor, + FormControl, + FormControlDirective, + NG_VALUE_ACCESSOR +} from "@angular/forms"; +import {DocumentPropertyRo} from "../../model/document-property-ro.model"; +import {MatSort} from "@angular/material/sort"; +import {MatDialog, MatDialogRef} from "@angular/material/dialog"; +import { + DocumentPropertyDialogComponent +} from "../../dialogs/document-property-dialog/document-property-dialog.component"; +import {PropertyValueTypeEnum} from "../../enums/property-value-type.enum"; +import {MatPaginator} from "@angular/material/paginator"; + +/** + * Component to display the properties of a document in a table. The properties can be edited and saved. + * @author Joze Rihtarsic + * @since 5.1 + */ +@Component({ + selector: 'document-properties-panel', + templateUrl: './document-properties-panel.component.html', + styleUrls: ['./document-properties-panel.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => DocumentPropertiesPanelComponent), + multi: true + } + ] +}) +export class DocumentPropertiesPanelComponent implements AfterViewInit, BeforeLeaveGuard, ControlValueAccessor { + private readonly NEW_PROPERTY_NAME_TEMPLATE: string = 'document.property.v'; + displayedColumns: string[] = ['property', 'value']; + private onChangeCallback: (_: any) => void = () => { + }; + selected?: DocumentPropertyRo = null; + dataChanged: boolean = false + showPropertyPanel: boolean = true; + initPropertyList: DocumentPropertyRo[] = []; + propertyDataSource: MatTableDataSource<DocumentPropertyRo> = new MatTableDataSource(); + + @ViewChild("DocumentPropertyTable") table: MatTable<DocumentPropertyRo>; + @ViewChild(MatPaginator) paginator: MatPaginator + @ViewChild(MatSort) sort: MatSort; + + constructor(public dialog: MatDialog, private controlContainer: ControlContainer) { + + } + + @ViewChild(FormControlDirective, {static: true}) + formControlDirective: FormControlDirective; + @Input() + formControl: FormControl; + + @Input() + formControlName: string; /* get hold of FormControl instance no matter formControl or formControlName is given. If formControlName is given, then this.controlContainer.control is the parent FormGroup (or FormArray) instance. */ + get control() { + return this.formControl || this.controlContainer.control.get(this.formControlName); + } + + + ngAfterViewInit() { + this.propertyDataSource.paginator = this.paginator; + this.propertyDataSource.sort = this.sort; + } + + applyFilter(event: Event) { + const filterValue: string = (event.target as HTMLInputElement).value; + this.propertyDataSource.filter = filterValue?.trim().toLowerCase(); + + if (this.propertyDataSource.paginator) { + this.propertyDataSource.paginator.firstPage(); + } + } + + get saveButtonEnabled(): boolean { + return this.dataChanged; + } + + get cancelButtonEnabled(): boolean { + return this.dataChanged; + } + + public onToggleButtonClicked(): void { + this.showPropertyPanel = !this.showPropertyPanel; + } + + /** + * Method created a new property and opens the dialog to edit it. + * After the dialog is closed with 'Save' button, the new property is added + * to the list of properties. + */ + public onCreateProperty(): void { + const newProperty: DocumentPropertyRo = { + property: this.uniquePropertyName, + value: "", + type: PropertyValueTypeEnum.STRING, + desc: "", + status: EntityStatus.NEW, + readonly: false + }; + this.editSelectedProperty(newProperty).afterClosed() + .subscribe((result): void => { + if (result) { + this.addNewProperty(result); + } + }); + } + + /** + * Method returns unique property name. The method checks all properties in the list and + * creates a new name with index if the name is already used. + */ + get uniquePropertyName(): string { + let propertyNames: string[] = this.allPropertyNames; + // create index and check "my.property.[index]" until we find unique name + // and format index with 3 digits + let index: number = 1; + let propertyName: string = this.NEW_PROPERTY_NAME_TEMPLATE + index.toString().padStart(3, '0'); + + while (propertyNames.includes(propertyName)) { + index++; + propertyName = this.NEW_PROPERTY_NAME_TEMPLATE + index.toString().padStart(3, '0'); + } + return propertyName + } + + private addNewProperty(newProperty: DocumentPropertyRo) { + this.propertyDataSource.data.push(newProperty); + + // to trigger refresh . render rows does not work on angular 16 + this.propertyDataSource.data = [...this.propertyDataSource.data]; + if (this.propertyDataSource.paginator) { + this.propertyDataSource.paginator.lastPage(); + } + this.selected = newProperty; + this.dataChanged = true; + this.onChangeCallback(this.propertyDataSource.data); + } + + public onEditSelectedProperty(): void { + if (!this.selected) { + return; + } + + this.editSelectedProperty(this.selected).afterClosed() + .subscribe((result): void => { + if (result) { + let indexToUpdate = this.propertyDataSource.data + .findIndex(item => item.property === result.property); + + let property: DocumentPropertyRo = this.equals(this.initPropertyList[indexToUpdate], result) ? + this.initPropertyList[indexToUpdate] : result; + + + this.propertyDataSource.data[indexToUpdate] = property; + // trigger reload + this.propertyDataSource.data = [...this.propertyDataSource.data]; + this.selected = property; + this.dataChanged = true; + this.onChangeCallback(this.propertyDataSource.data); + } + }); + } + + /** + * Method compares two DocumentPropertyRo objects and returns true if they are equal. The method + * validates property name, value, type and description. + * @param value1 + * @param value2 + */ + private equals(value1: DocumentPropertyRo, value2: DocumentPropertyRo): boolean { + return value1.property === value2.property + && value1.value === value2.value + && value1.desc === value2.desc + && value1.type === value2.type; + } + + public onDeleteSelectedProperty(): void { + if (!this.selected) { + return; + } + let properties = this.propertyDataSource.data; + + if (this.selected.status === EntityStatus.NEW) { + const index: number = properties.indexOf(this.selected); + if (index !== -1) { + properties.splice(index, 1); + } + } else { + this.selected.status = EntityStatus.REMOVED; + this.selected.deleted = true; + } + // to trigger refresh + this.propertyDataSource.data = [...properties]; + + this.onChangeCallback(this.propertyDataSource.data); + } + + + public onSaveButtonClicked() { + // submit list of properties to the backend + + } + + /* + * reset/reload properties from the server + */ + public onResetButtonClicked(): void { + this.control.setValue(this.initPropertyList); + this.control.markAsPristine(); + } + + /** + * return the value to be displayed in the table row. If the row is updated and not system default, + * then display the new value else display the old value + * @param row + */ + getTableRowValue(row: DomainPropertyRo) { + return row.systemDefault ? row.systemDefaultValue : row.value; + } + + isDirty(): boolean { + return this.dataChanged; + } + + propertySelected(property: DocumentPropertyRo) { + this.selected = property; + } + + get editButtonDisabled(): boolean { + return !this.selected; + } + + get deleteButtonDisabled(): boolean { + return !this.selected || this.selected.readonly; + } + + getRowClass(row, oddRow: boolean) { + return { + 'datatable-row-selected': row === this.selected, + 'table-row-new': (row.status === EntityStatus.NEW), + 'table-row-updated': (row.status === EntityStatus.UPDATED), + 'deleted': (row.status === EntityStatus.REMOVED), + 'datatable-row-odd': oddRow + }; + } + + registerOnChange(fn: any): void { + this.onChangeCallback = fn; + + } + + + registerOnTouched(fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + } + + /** + * Implementation of the ControlValueAccessor method to write value to the component. + * @param propertyList + */ + writeValue(propertyList: DocumentPropertyRo[]): void { + this.initPropertyList = propertyList; + this.propertyDataSource.data = !propertyList?.length ? [] : [...propertyList]; + this.dataChanged = false; + } + + public editSelectedProperty(row: DocumentPropertyRo): MatDialogRef<any> { + return this.dialog.open(DocumentPropertyDialogComponent, { + data: { + editMode: row.status, + row: row, + allPropertyNames: this.allPropertyNames + } + }); + } + + get allPropertyNames(): string[] { + return this.propertyDataSource.data.map(item => item.property); + } +} diff --git a/smp-angular/src/app/edit/edit-domain/edit-domain.component.ts b/smp-angular/src/app/edit/edit-domain/edit-domain.component.ts index 716901eb6914eb853e068be65c2dda343076329e..a6f65dc6942e8d0b8fe1f467cd600546993c01b2 100644 --- a/smp-angular/src/app/edit/edit-domain/edit-domain.component.ts +++ b/smp-angular/src/app/edit/edit-domain/edit-domain.component.ts @@ -12,7 +12,6 @@ import {MatTabGroup} from "@angular/material/tabs"; import {MemberTypeEnum} from "../../common/enums/member-type.enum"; import {firstValueFrom} from "rxjs"; - @Component({ templateUrl: './edit-domain.component.html', styleUrls: ['./edit-domain.component.css'] diff --git a/smp-angular/src/app/edit/edit-resources/resource-document-panel/resource-document-panel.component.html b/smp-angular/src/app/edit/edit-resources/resource-document-panel/resource-document-panel.component.html index 938b1184cf17a13a29997a365a4c2bbe13a7ca00..bef883a2d4bd931640c501a670838f0c26e476e9 100644 --- a/smp-angular/src/app/edit/edit-resources/resource-document-panel/resource-document-panel.component.html +++ b/smp-angular/src/app/edit/edit-resources/resource-document-panel/resource-document-panel.component.html @@ -1,7 +1,8 @@ <div id="resource-document-panel"> <mat-toolbar class="mat-elevation-z2" style="min-height: 50px !important;"> - <mat-toolbar-row class="smp-toolbar-row" style="justify-content: space-between;min-height: 50px !important;"> + <mat-toolbar-row class="smp-toolbar-row" + style="justify-content: space-between;min-height: 50px !important;"> <button id="validateResource_id" mat-raised-button color="primary" @@ -35,7 +36,8 @@ <div [formGroup]="documentForm" style="float: right; vertical-align:middle;display: flex;align-items: center;justify-content: center; gap:0.4em;padding-right: 10px"> <span style="font-size: 0.8em">Show version:</span> - <select matNativeControl style="width: 100px; border-bottom: grey solid 1px" + <select matNativeControl + style="width: 100px; border-bottom: grey solid 1px" placeholder="All document version" matTooltip="Select version to display." formControlName="payloadVersion" @@ -45,7 +47,7 @@ <option *ngFor="let version of getDocumentVersions" [value]="version" > - {{version}} + {{ version }} </option> </select> @@ -56,62 +58,52 @@ readonly> <mat-datepicker-toggle matSuffix [for]="payloadCreatedOnPicker" style="visibility: hidden"></mat-datepicker-toggle> - <ngx-mat-datetime-picker #payloadCreatedOnPicker [showSpinners]="true" [showSeconds]="false" + <ngx-mat-datetime-picker #payloadCreatedOnPicker [showSpinners]="true" + [showSeconds]="false" [hideTime]="false"></ngx-mat-datetime-picker> </div> </mat-toolbar-row> </mat-toolbar> - <div class="panel"> - <div style="display: flex;flex-direction: row;width: 100%"> - <smp-titled-label style="flex-grow: 1" title="Resource identifier:" - value="{{resource?.identifierValue}}"></smp-titled-label> - <smp-titled-label style="flex-grow: 1" title="Resource scheme:" - value="{{resource?.identifierScheme}}"></smp-titled-label> - </div> - <div style="display: flex;flex-direction: row;width: 100%"> - <smp-titled-label style="flex-grow: 1" title="Document name:" value="{{_document?.name}}"></smp-titled-label> - <smp-titled-label style="flex-grow: 1" title="Document mimeType:" - value="{{_document?.mimeType}}"></smp-titled-label> - <smp-titled-label style="flex-grow: 1" title="Current document version:" - value="{{_document?.currentResourceVersion}}"></smp-titled-label> - </div> - </div> + <div class="panel" [formGroup]="documentForm" + style="display: flex;flex-direction: row;flex-grow: 1; "> - <div [formGroup]="documentForm" style="width: 100%; display: flex; flex-direction: column; flex:1; gap:0.5em "> <div style="display:block; overflow: auto;flex: 2;align-self: stretch; flex-direction: column;border: ridge 3px #b0bec5" (click)="onEditPanelClick()" > - <smp-editor #smpDocumentEditor - formControlName="payload" - ngDefaultControl - ></smp-editor> + <smp-editor #smpDocumentEditor formControlName="payload" + ngDefaultControl></smp-editor> </div> - <mat-toolbar class="mat-elevation-z2" style="flex-grow: 0"> - <mat-toolbar-row class="smp-toolbar-row"> - <button id="back_id" mat-raised-button color="primary" - (click)="onBackButtonClicked()"> - <mat-icon>arrow_circle_left</mat-icon> - <span>Back</span> - </button> - <button id="cancel_id" mat-raised-button color="primary" - [disabled]="cancelButtonDisabled" - (click)="onDocumentResetButtonClicked()"> - <mat-icon>cancel</mat-icon> - <span>Cancel</span> - </button> - <button id="saveResource_id" mat-raised-button - color="primary" - matTooltip="Validate resource" - [disabled]="saveButtonDisabled" - (click)="onSaveButtonClicked()" - > - <mat-icon>save</mat-icon> - <span>Save</span> - </button> - </mat-toolbar-row> - </mat-toolbar> + <document-properties-panel style="min-height: 0; overflow: auto" + formControlName="properties" + ngDefaultControl></document-properties-panel> </div> + + <mat-toolbar class="mat-elevation-z2" + style="flex-grow: 0;"> + <mat-toolbar-row class="smp-toolbar-row"> + <button id="back_id" mat-raised-button color="primary" + (click)="onBackButtonClicked()"> + <mat-icon>arrow_circle_left</mat-icon> + <span>Back</span> + </button> + <button id="cancel_id" mat-raised-button color="primary" + [disabled]="cancelButtonDisabled" + (click)="onDocumentResetButtonClicked()"> + <mat-icon>cancel</mat-icon> + <span>Cancel</span> + </button> + <button id="saveResource_id" mat-raised-button + color="primary" + matTooltip="Validate resource" + [disabled]="saveButtonDisabled" + (click)="onSaveButtonClicked()" + > + <mat-icon>save</mat-icon> + <span>Save</span> + </button> + </mat-toolbar-row> + </mat-toolbar> </div> diff --git a/smp-angular/src/app/edit/edit-resources/resource-document-panel/resource-document-panel.component.ts b/smp-angular/src/app/edit/edit-resources/resource-document-panel/resource-document-panel.component.ts index 1b03dd752868ef015d278cf9736519bce601a6a8..f40e7e9e5debdd2307d01269e762b7f53bcd27b1 100644 --- a/smp-angular/src/app/edit/edit-resources/resource-document-panel/resource-document-panel.component.ts +++ b/smp-angular/src/app/edit/edit-resources/resource-document-panel/resource-document-panel.component.ts @@ -1,18 +1,33 @@ -import {AfterViewInit, Component, Input, ViewChild, ViewEncapsulation,} from '@angular/core'; +import {Component, Input, ViewChild, ViewEncapsulation,} from '@angular/core'; import {MatDialog, MatDialogRef} from "@angular/material/dialog"; -import {BeforeLeaveGuard} from "../../../window/sidenav/navigation-on-leave-guard"; +import { + BeforeLeaveGuard +} from "../../../window/sidenav/navigation-on-leave-guard"; import {GroupRo} from "../../../common/model/group-ro.model"; import {ResourceRo} from "../../../common/model/resource-ro.model"; -import {AlertMessageService} from "../../../common/alert-message/alert-message.service"; +import { + AlertMessageService +} from "../../../common/alert-message/alert-message.service"; import {DomainRo} from "../../../common/model/domain-ro.model"; -import {ResourceDefinitionRo} from "../../../system-settings/admin-extension/resource-definition-ro.model"; +import { + ResourceDefinitionRo +} from "../../../system-settings/admin-extension/resource-definition-ro.model"; import {EditResourceService} from "../edit-resource.service"; import {FormBuilder, FormControl, FormGroup} from "@angular/forms"; import {DocumentRo} from "../../../common/model/document-ro.model"; -import {NavigationService} from "../../../window/sidenav/navigation-model.service"; -import {DocumentWizardDialogComponent} from "../document-wizard-dialog/document-wizard-dialog.component"; -import {ConfirmationDialogComponent} from "../../../common/dialogs/confirmation-dialog/confirmation-dialog.component"; -import {SmpEditorComponent} from "../../../common/components/smp-editor/smp-editor.component"; +import { + NavigationService +} from "../../../window/sidenav/navigation-model.service"; +import { + DocumentWizardDialogComponent +} from "../document-wizard-dialog/document-wizard-dialog.component"; +import { + ConfirmationDialogComponent +} from "../../../common/dialogs/confirmation-dialog/confirmation-dialog.component"; +import { + SmpEditorComponent +} from "../../../common/components/smp-editor/smp-editor.component"; +import {EntityStatus} from "../../../common/enums/entity-status.enum"; @Component({ templateUrl: './resource-document-panel.component.html', @@ -52,6 +67,7 @@ export class ResourceDocumentPanelComponent implements BeforeLeaveGuard { 'payloadCreatedOn': new FormControl({value: null}), 'payloadVersion': new FormControl({value: null}), 'payload': new FormControl({value: null}), + 'properties': new FormControl({value: null}), }); this.resource = editResourceService.selectedResource @@ -94,7 +110,6 @@ export class ResourceDocumentPanelComponent implements BeforeLeaveGuard { this._document = value; this.documentForm.disable(); if (!!value) { - console.log("Document: " + value.payload) this.documentEditor.mimeType = value.mimeType; this.documentForm.controls['mimeType'].setValue(value.mimeType); this.documentForm.controls['name'].setValue(value.name); @@ -103,6 +118,7 @@ export class ResourceDocumentPanelComponent implements BeforeLeaveGuard { this.documentForm.controls['payloadCreatedOn'].setValue(value.payloadCreatedOn); this.documentForm.controls['payload'].setValue(value.payload); this.documentForm.controls['payload'].enable(); + this.documentForm.controls['properties'].setValue(value.properties); // the method documentVersionsExists already uses the current value to check if versions exists if (!this.documentVersionsExists) { this.documentForm.controls['payloadVersion'].disable(); @@ -116,13 +132,18 @@ export class ResourceDocumentPanelComponent implements BeforeLeaveGuard { this.documentForm.controls['payloadVersion'].setValue(""); this.documentForm.controls['payloadCreatedOn'].setValue(""); this.documentForm.controls['payload'].setValue(""); + this.documentForm.controls['properties'].setValue([]); } this.documentForm.markAsPristine(); } get document(): DocumentRo { let doc: DocumentRo = {...this._document}; - doc.payload = this.documentForm.controls['payload'].value; + if(this.documentForm.controls['payload'].dirty){ + doc.payload = this.documentForm.controls['payload'].value; + doc.payloadStatus = EntityStatus.UPDATED; + } + doc.properties = this.documentForm.controls['properties'].value; return doc; } @@ -156,6 +177,7 @@ export class ResourceDocumentPanelComponent implements BeforeLeaveGuard { onSaveButtonClicked(): void { + this.editResourceService.saveDocumentObservable(this._resource, this.document).subscribe((value: DocumentRo) => { if (value) { this.alertService.success("Document is saved with current version [" + value.currentResourceVersion + "].") @@ -265,9 +287,3 @@ export class ResourceDocumentPanelComponent implements BeforeLeaveGuard { return this._resource?.resourceTypeIdentifier === 'edelivery-oasis-smp-1.0-servicegroup'; } } - - - - - - diff --git a/smp-angular/src/app/edit/edit-resources/subresource-document-panel/subresource-document-panel.component.html b/smp-angular/src/app/edit/edit-resources/subresource-document-panel/subresource-document-panel.component.html index 3dcbe7efcc2a1e413c4001488ef23537c4f8885f..6a02eb7d2ff642e567db13de5a8d27ab24020fb5 100644 --- a/smp-angular/src/app/edit/edit-resources/subresource-document-panel/subresource-document-panel.component.html +++ b/smp-angular/src/app/edit/edit-resources/subresource-document-panel/subresource-document-panel.component.html @@ -58,62 +58,44 @@ </div> </mat-toolbar-row> </mat-toolbar> - <div class="panel"> + <div class="panel" [formGroup]="documentForm" + style="display: flex;flex-direction: row;flex-grow: 1; "> - <div style="display: flex;flex-direction: row;width: 100%"> - <smp-titled-label style="flex-grow: 1" title="Resource identifier:" - value="{{resource?.identifierValue}}"></smp-titled-label> - <smp-titled-label style="flex-grow: 1" title="Resource scheme:" - value="{{resource?.identifierScheme}}"></smp-titled-label> - </div> - <div style="display: flex;flex-direction: row;width: 100%"> - <smp-titled-label style="flex-grow: 1" title="Subresource identifier:" - value="{{subresource?.identifierValue}}"></smp-titled-label> - <smp-titled-label style="flex-grow: 1" title="Subresource scheme:" - value="{{subresource?.identifierScheme}}"></smp-titled-label> - </div> - <div style="display: flex;flex-direction: row;width: 100%"> - <smp-titled-label style="flex-grow: 1" title="Document name:" value="{{_document?.name}}"></smp-titled-label> - <smp-titled-label style="flex-grow: 1" title="Document mimeType:" - value="{{_document?.mimeType}}"></smp-titled-label> - <smp-titled-label style="flex-grow: 1" title="Current document version:" - value="{{_document?.currentResourceVersion}}"></smp-titled-label> - </div> - </div> - - <div [formGroup]="documentForm" style="width: 100%; display: flex; flex-direction: column; flex:1; gap:0.5em "> <div - style="display:block; overflow: auto;flex: 2;align-self: stretch; flex-direction: column;background-color: #FCFCFCBB; border: ridge 3px #b0bec5" + style="display:block; overflow: auto;flex: 2;align-self: stretch; flex-direction: column;border: ridge 3px #b0bec5" (click)="onEditPanelClick()" > - <smp-editor #smpDocumentEditor - formControlName="payload" - ngDefaultControl - ></smp-editor> + <smp-editor #smpDocumentEditor formControlName="payload" + ngDefaultControl></smp-editor> </div> - <mat-toolbar class="mat-elevation-z2" style="flex-grow: 0"> - <mat-toolbar-row class="smp-toolbar-row"> - <button id="back_id" mat-raised-button color="primary" - (click)="onBackButtonClicked()"> - <mat-icon>arrow_circle_left</mat-icon> - <span>Back</span> - </button> - <button id="cancel_id" mat-raised-button color="primary" - [disabled]="cancelButtonDisabled" - (click)="onDocumentResetButtonClicked()"> - <mat-icon>cancel</mat-icon> - <span>Cancel</span> - </button> - <button id="saveResource_id" mat-raised-button - color="primary" - matTooltip="Validate resource" - [disabled]="saveButtonDisabled" - (click)="onSaveButtonClicked()" - > - <mat-icon>save</mat-icon> - <span>Save</span> - </button> - </mat-toolbar-row> - </mat-toolbar> + <document-properties-panel style="min-height: 0; overflow: auto" + formControlName="properties" + ngDefaultControl></document-properties-panel> </div> + + <mat-toolbar class="mat-elevation-z2" + style="flex-grow: 0;"> + <mat-toolbar-row class="smp-toolbar-row"> + <button id="back_id" mat-raised-button color="primary" + (click)="onBackButtonClicked()"> + <mat-icon>arrow_circle_left</mat-icon> + <span>Back</span> + </button> + <button id="cancel_id" mat-raised-button color="primary" + [disabled]="cancelButtonDisabled" + (click)="onDocumentResetButtonClicked()"> + <mat-icon>cancel</mat-icon> + <span>Cancel</span> + </button> + <button id="saveResource_id" mat-raised-button + color="primary" + matTooltip="Validate resource" + [disabled]="saveButtonDisabled" + (click)="onSaveButtonClicked()" + > + <mat-icon>save</mat-icon> + <span>Save</span> + </button> + </mat-toolbar-row> + </mat-toolbar> </div> diff --git a/smp-angular/src/app/edit/edit-resources/subresource-document-panel/subresource-document-panel.component.ts b/smp-angular/src/app/edit/edit-resources/subresource-document-panel/subresource-document-panel.component.ts index 37990409b684fa6c1fe9a8c112f6d720ad2cf2fd..e063630daf2b90df08eb78fbf9a116bb0f93d2aa 100644 --- a/smp-angular/src/app/edit/edit-resources/subresource-document-panel/subresource-document-panel.component.ts +++ b/smp-angular/src/app/edit/edit-resources/subresource-document-panel/subresource-document-panel.component.ts @@ -19,6 +19,7 @@ import { } from "../subresource-document-wizard-dialog/subresource-wizard-edit-ro.model"; import {ConfirmationDialogComponent} from "../../../common/dialogs/confirmation-dialog/confirmation-dialog.component"; import {SmpEditorComponent} from "../../../common/components/smp-editor/smp-editor.component"; +import {EntityStatus} from "../../../common/enums/entity-status.enum"; @Component({ templateUrl: './subresource-document-panel.component.html', @@ -66,6 +67,7 @@ export class SubresourceDocumentPanelComponent implements AfterViewInit, BeforeL 'payloadVersion': new FormControl({value: null}), 'payloadCreatedOn': new FormControl({value: null}), 'payload': new FormControl({value: null}), + 'properties': new FormControl({value: null}), }); this.documentForm.controls['payload'].setValue("") @@ -133,7 +135,8 @@ export class SubresourceDocumentPanelComponent implements AfterViewInit, BeforeL @Input() set document(value: DocumentRo) { this._document = value; this.documentForm.disable(); - if (!!value) { + if (!!value){ + this.documentEditor.mimeType = value.mimeType; this.documentForm.controls['mimeType'].setValue(value.mimeType); this.documentForm.controls['name'].setValue(value.name); this.documentForm.controls['currentResourceVersion'].setValue(value.currentResourceVersion); @@ -141,6 +144,7 @@ export class SubresourceDocumentPanelComponent implements AfterViewInit, BeforeL this.documentForm.controls['payloadCreatedOn'].setValue(value.payloadCreatedOn); this.documentForm.controls['payload'].setValue(!value.payload ? "" : value.payload); this.documentForm.controls['payload'].enable(); + this.documentForm.controls['properties'].setValue(value.properties); if (!this.documentVersionsExists) { this.documentForm.controls['payloadVersion'].disable(); @@ -155,13 +159,18 @@ export class SubresourceDocumentPanelComponent implements AfterViewInit, BeforeL this.documentForm.controls['payloadVersion'].setValue(""); this.documentForm.controls['payloadCreatedOn'].setValue(""); this.documentForm.controls['payload'].setValue(""); + this.documentForm.controls['properties'].setValue([]); } this.documentForm.markAsPristine(); } get document(): DocumentRo { let doc: DocumentRo = {...this._document}; - doc.payload = this.documentForm.controls['payload'].value; + if(this.documentForm.controls['payload'].dirty){ + doc.payload = this.documentForm.controls['payload'].value; + doc.payloadStatus = EntityStatus.UPDATED; + } + doc.properties = this.documentForm.controls['properties'].value; return doc; } @@ -288,7 +297,6 @@ export class SubresourceDocumentPanelComponent implements AfterViewInit, BeforeL } onDocumentResetButtonClicked(): void { - this.dialog.open(ConfirmationDialogComponent, { data: { title: "Cancel changes", diff --git a/smp-angular/src/app/edit/edit-resources/subresource-panel/resource-dialog/subresource-dialog.component.css b/smp-angular/src/app/edit/edit-resources/subresource-panel/subresource-dialog/subresource-dialog.component.css similarity index 100% rename from smp-angular/src/app/edit/edit-resources/subresource-panel/resource-dialog/subresource-dialog.component.css rename to smp-angular/src/app/edit/edit-resources/subresource-panel/subresource-dialog/subresource-dialog.component.css diff --git a/smp-angular/src/app/edit/edit-resources/subresource-panel/resource-dialog/subresource-dialog.component.html b/smp-angular/src/app/edit/edit-resources/subresource-panel/subresource-dialog/subresource-dialog.component.html similarity index 100% rename from smp-angular/src/app/edit/edit-resources/subresource-panel/resource-dialog/subresource-dialog.component.html rename to smp-angular/src/app/edit/edit-resources/subresource-panel/subresource-dialog/subresource-dialog.component.html diff --git a/smp-angular/src/app/edit/edit-resources/subresource-panel/resource-dialog/subresource-dialog.component.ts b/smp-angular/src/app/edit/edit-resources/subresource-panel/subresource-dialog/subresource-dialog.component.ts similarity index 91% rename from smp-angular/src/app/edit/edit-resources/subresource-panel/resource-dialog/subresource-dialog.component.ts rename to smp-angular/src/app/edit/edit-resources/subresource-panel/subresource-dialog/subresource-dialog.component.ts index 75f730993171097a813d53e680daa8070d440b1f..a2430fab995528469161128cebf4bc6c1a5206bc 100644 --- a/smp-angular/src/app/edit/edit-resources/subresource-panel/resource-dialog/subresource-dialog.component.ts +++ b/smp-angular/src/app/edit/edit-resources/subresource-panel/subresource-dialog/subresource-dialog.component.ts @@ -1,15 +1,17 @@ import {Component, ElementRef, Inject, Input, ViewChild} from '@angular/core'; import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog'; import {FormBuilder, FormControl, FormGroup} from "@angular/forms"; -import {AlertMessageService} from "../../../../common/alert-message/alert-message.service"; -import {VisibilityEnum} from "../../../../common/enums/visibility.enum"; -import {GroupRo} from "../../../../common/model/group-ro.model"; +import { + AlertMessageService +} from "../../../../common/alert-message/alert-message.service"; import {ResourceRo} from "../../../../common/model/resource-ro.model"; import {DomainRo} from "../../../../common/model/domain-ro.model"; -import {ResourceDefinitionRo} from "../../../../system-settings/admin-extension/resource-definition-ro.model"; -import {EditGroupService} from "../../../edit-group/edit-group.service"; +import { + ResourceDefinitionRo +} from "../../../../system-settings/admin-extension/resource-definition-ro.model"; import {SubresourceRo} from "../../../../common/model/subresource-ro.model"; import {EditResourceService} from "../../edit-resource.service"; + @Component({ templateUrl: './subresource-dialog.component.html', styleUrls: ['./subresource-dialog.component.css'] diff --git a/smp-angular/src/app/edit/edit-resources/subresource-panel/subresource-panel.component.ts b/smp-angular/src/app/edit/edit-resources/subresource-panel/subresource-panel.component.ts index e230d34614de642526ba2e376eb8fb6422c14b76..0a3da6f7b74f5e2afd7f949063dec8fcfe4d3cc8 100644 --- a/smp-angular/src/app/edit/edit-resources/subresource-panel/subresource-panel.component.ts +++ b/smp-angular/src/app/edit/edit-resources/subresource-panel/subresource-panel.component.ts @@ -12,7 +12,7 @@ import {EditResourceService} from "../edit-resource.service"; import {SubresourceRo} from "../../../common/model/subresource-ro.model"; import {MatTableDataSource} from "@angular/material/table"; import {ConfirmationDialogComponent} from "../../../common/dialogs/confirmation-dialog/confirmation-dialog.component"; -import {SubresourceDialogComponent} from "./resource-dialog/subresource-dialog.component"; +import {SubresourceDialogComponent} from "./subresource-dialog/subresource-dialog.component"; import {SubresourceDefinitionRo} from "../../../system-settings/admin-extension/subresource-definition-ro.model"; import {NavigationNode, NavigationService} from "../../../window/sidenav/navigation-model.service"; @@ -22,7 +22,7 @@ import {NavigationNode, NavigationService} from "../../../window/sidenav/navigat templateUrl: './subresource-panel.component.html', styleUrls: ['./subresource-panel.component.scss'] }) -export class SubresourcePanelComponent implements AfterViewInit, OnInit, BeforeLeaveGuard { +export class SubresourcePanelComponent implements AfterViewInit, BeforeLeaveGuard { title: string = "Subresources"; @@ -44,9 +44,6 @@ export class SubresourcePanelComponent implements AfterViewInit, OnInit, BeforeL private dialog: MatDialog) { } - ngOnInit(): void { - } - ngAfterViewInit() { this.dataSource.paginator = this.paginator; diff --git a/smp-angular/src/app/system-settings/admin-domain/domain-properties-panel/domain-properties-panel.component.ts b/smp-angular/src/app/system-settings/admin-domain/domain-properties-panel/domain-properties-panel.component.ts index d9593e3dde887d80820a8124b2e848b61712dfa4..06b7b6799d95b34c034d6d5fdcbf23ff2921cdd9 100644 --- a/smp-angular/src/app/system-settings/admin-domain/domain-properties-panel/domain-properties-panel.component.ts +++ b/smp-angular/src/app/system-settings/admin-domain/domain-properties-panel/domain-properties-panel.component.ts @@ -17,7 +17,7 @@ import {MatTableDataSource} from "@angular/material/table"; import {Subscription} from "rxjs"; import {PropertyController} from "../../admin-properties/property-controller"; import {EntityStatus} from "../../../common/enums/entity-status.enum"; -import {PropertyTypeEnum} from "../../../common/enums/property-type.enum"; +import {PropertySourceEnum} from "../../../common/enums/property-source.enum"; import {EditDomainService} from "../../../edit/edit-domain/edit-domain.service"; @Component({ @@ -145,7 +145,7 @@ export class DomainPropertiesPanelComponent implements OnInit, OnDestroy, Before const dialogRef: MatDialogRef<any> = this.propertyController.edit({ data: { edit: this.selected?.status != EntityStatus.NEW, - propertyType: PropertyTypeEnum.DOMAIN, + propertyType: PropertySourceEnum.DOMAIN, row: this.selected } }) diff --git a/smp-angular/src/app/user-settings/user-alerts/user-alerts.component.ts b/smp-angular/src/app/user-settings/user-alerts/user-alerts.component.ts index bc296a6022806678b940c977c6d6ae2613278977..173f6d09d71739cfc203ceb528f74c5cbe67bbda 100644 --- a/smp-angular/src/app/user-settings/user-alerts/user-alerts.component.ts +++ b/smp-angular/src/app/user-settings/user-alerts/user-alerts.component.ts @@ -28,7 +28,6 @@ export class UserAlertsComponent implements OnInit, OnDestroy, BeforeLeaveGuard private securityEventService: SecurityEventService, public dialog: MatDialog) { - this.securityEventServiceSub = this.securityEventService.onLoginSuccessEvent().subscribe(() => { this.updateUserData(this.securityService.getCurrentUser()) } diff --git a/smp-angular/src/styles.css b/smp-angular/src/styles.css index 406c091b98bf47ff29561204a87f051ab74f5c34..48c456a492e3046dd930df20fb72baaef94a63ba 100644 --- a/smp-angular/src/styles.css +++ b/smp-angular/src/styles.css @@ -57,24 +57,6 @@ td , th { word-wrap: break-word !important; } -div:before, -table:before, -tr:before, -td:before, -form:before, -tbody:before, -mat-card:before, -mat-card-content:before, -.panel:before, -.selectionCriteria:before, -table.buttonsRow:before, -table.buttonsMoveRow, -mat-input-container:before, -ngx-datatable span:before { - content: " " !important; -} - - .ngx-datatable span { word-wrap: break-word; } @@ -282,26 +264,8 @@ mat-card { margin-top: 0 !important; } -/* --- INPUT ---*/ -.panel mat-select, .panel mat-datepicker { - padding-top: 10px; - padding-bottom: 10px; -} - .panel mat-select, .panel mat-form-field, .panel mat-datepicker { - margin-right: 2%; -} - -@media (max-width: 991px) { - .panel mat-form-field, .panel mat-select, .panel mat-datepicker { - width: 46%; - } -} - -@media (max-width: 767px) { - .panel mat-form-field, .panel mat-select, .panel mat-datepicker { - width: 100%; - } + margin-right: 5px; } .mat-select-menu-container, @@ -353,6 +317,13 @@ mat-card { border-top: 1px solid transparent; } +.mat-mdc-table .mdc-data-table__header-row { + height: 2.0em; +} +.mat-mdc-table .mdc-data-table__row { + height: 1.8em; +} + /*-------------------------------------------------- [alert-message] ----------------------------------------------------*/ diff --git a/smp-resource-extensions/oasis-smp-spi/src/main/java/eu/europa/ec/smp/spi/handler/OasisSMPResource10Handler.java b/smp-resource-extensions/oasis-smp-spi/src/main/java/eu/europa/ec/smp/spi/handler/OasisSMPResource10Handler.java index 9ec6efd1a87f959b91473509764b73c3b249530f..6f2b2dbff102b6fbf57bbf77183e567442e9e591 100644 --- a/smp-resource-extensions/oasis-smp-spi/src/main/java/eu/europa/ec/smp/spi/handler/OasisSMPResource10Handler.java +++ b/smp-resource-extensions/oasis-smp-spi/src/main/java/eu/europa/ec/smp/spi/handler/OasisSMPResource10Handler.java @@ -28,6 +28,7 @@ import eu.europa.ec.smp.spi.api.model.RequestData; import eu.europa.ec.smp.spi.api.model.ResourceIdentifier; import eu.europa.ec.smp.spi.api.model.ResponseData; import eu.europa.ec.smp.spi.def.OasisSMPSubresource10; +import eu.europa.ec.smp.spi.enums.TransientDocumentPropertyType; import eu.europa.ec.smp.spi.exceptions.ResourceException; import gen.eu.europa.ec.ddc.api.smp10.ParticipantIdentifierType; import gen.eu.europa.ec.ddc.api.smp10.ServiceGroup; @@ -52,7 +53,11 @@ import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; +import static eu.europa.ec.smp.spi.enums.TransientDocumentPropertyType.RESOURCE_IDENTIFIER_SCHEME; +import static eu.europa.ec.smp.spi.enums.TransientDocumentPropertyType.RESOURCE_IDENTIFIER_VALUE; import static eu.europa.ec.smp.spi.exceptions.ResourceException.ErrorCode.*; +import static org.apache.commons.lang3.StringUtils.equalsIgnoreCase; +import static org.apache.commons.lang3.StringUtils.trim; @Component public class OasisSMPResource10Handler extends AbstractOasisSMPHandler { @@ -78,8 +83,10 @@ public class OasisSMPResource10Handler extends AbstractOasisSMPHandler { ServiceGroup resource = new ServiceGroup(); resource.setParticipantIdentifier(new ParticipantIdentifierType()); - resource.getParticipantIdentifier().setValue(identifier.getValue()); - resource.getParticipantIdentifier().setScheme(identifier.getScheme()); + resource.getParticipantIdentifier().setValue(RESOURCE_IDENTIFIER_VALUE.getPropertyPlaceholder()); + if (identifier.getScheme()!= null) { + resource.getParticipantIdentifier().setScheme(RESOURCE_IDENTIFIER_SCHEME.getPropertyPlaceholder()); + } resource.setServiceMetadataReferenceCollection(new ServiceMetadataReferenceCollectionType()); try { @@ -186,8 +193,12 @@ public class OasisSMPResource10Handler extends AbstractOasisSMPHandler { } } else { LOG.info("Update ServiceGroup identifier before saving. Old: [{}], New: [{}]", orgResourceId, nrmResourceId); - orgResourceId.setValue(nrmResourceId.getValue()); - orgResourceId.setScheme(nrmResourceId.getScheme()); + if (!StringUtils.equalsIgnoreCase(orgResourceId.getValue(), RESOURCE_IDENTIFIER_VALUE.getPropertyPlaceholder())) { + orgResourceId.setValue(nrmResourceId.getValue()); + } + if (!StringUtils.equalsIgnoreCase(orgResourceId.getScheme(), RESOURCE_IDENTIFIER_SCHEME.getPropertyPlaceholder())) { + orgResourceId.setScheme(nrmResourceId.getScheme()); + } try { // need to save resource because of the update on the resource identifier values reader.serializeNative(resource, responseData.getOutputStream(), true); @@ -228,8 +239,20 @@ public class OasisSMPResource10Handler extends AbstractOasisSMPHandler { throw new ResourceException(INVALID_RESOURCE, "Error occurred while parsing Oasis SMP 1.0 ServiceGroup with error: " + ExceptionUtils.getRootCauseMessage(e), e); } final ParticipantIdentifierType participantId = resource.getParticipantIdentifier(); + String participantIdValue = participantId.getValue(); + String participantIdScheme = participantId.getScheme(); + if (equalsIgnoreCase(trim(participantIdValue), RESOURCE_IDENTIFIER_VALUE.getPropertyPlaceholder())) { + participantIdValue = identifier.getValue(); + } + + if (equalsIgnoreCase(trim(participantIdScheme), RESOURCE_IDENTIFIER_SCHEME.getPropertyPlaceholder())) { + participantIdScheme = identifier.getScheme(); + } + ResourceIdentifier xmlResourceIdentifier = smpIdentifierApi.normalizeResourceIdentifier(resourceData.getDomainCode(), - participantId.getValue(), participantId.getScheme()); + participantIdValue, participantIdScheme); + + if (!xmlResourceIdentifier.equals(identifier)) { // Business identifier must equal path diff --git a/smp-resource-extensions/oasis-smp-spi/src/main/java/eu/europa/ec/smp/spi/handler/OasisSMPResource20Handler.java b/smp-resource-extensions/oasis-smp-spi/src/main/java/eu/europa/ec/smp/spi/handler/OasisSMPResource20Handler.java index 1d668bb92ae026376f4bca21ef60e3a83f040554..3b8f789e039d7cd7c718d1cf8f1a40a82006f1ba 100644 --- a/smp-resource-extensions/oasis-smp-spi/src/main/java/eu/europa/ec/smp/spi/handler/OasisSMPResource20Handler.java +++ b/smp-resource-extensions/oasis-smp-spi/src/main/java/eu/europa/ec/smp/spi/handler/OasisSMPResource20Handler.java @@ -56,7 +56,11 @@ import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; +import static eu.europa.ec.smp.spi.enums.TransientDocumentPropertyType.RESOURCE_IDENTIFIER_SCHEME; +import static eu.europa.ec.smp.spi.enums.TransientDocumentPropertyType.RESOURCE_IDENTIFIER_VALUE; import static eu.europa.ec.smp.spi.exceptions.ResourceException.ErrorCode.*; +import static org.apache.commons.lang3.StringUtils.equalsIgnoreCase; +import static org.apache.commons.lang3.StringUtils.trim; @Component public class OasisSMPResource20Handler extends AbstractOasisSMPHandler { @@ -86,9 +90,10 @@ public class OasisSMPResource20Handler extends AbstractOasisSMPHandler { resource.setSMPVersionID(new SMPVersionID()); resource.getSMPVersionID().setValue("2.0"); resource.setParticipantID(new ParticipantID()); - resource.getParticipantID().setValue(identifier.getValue()); - resource.getParticipantID().setSchemeID(identifier.getScheme()); - + resource.getParticipantID().setValue(RESOURCE_IDENTIFIER_VALUE.getPropertyPlaceholder()); + if (identifier.getScheme() != null) { + resource.getParticipantID().setSchemeID(RESOURCE_IDENTIFIER_SCHEME.getPropertyPlaceholder()); + } try { reader.serializeNative(resource, responseData.getOutputStream(), true); } catch (TechnicalException e) { @@ -177,8 +182,13 @@ public class OasisSMPResource20Handler extends AbstractOasisSMPHandler { } else { LOG.info("Update Resource/ServiceGroup identifier before saving. Old: [{}], New: [{}]", orgResourceId, nrmResourceId); - orgResourceId.setValue(nrmResourceId.getValue()); - orgResourceId.setSchemeID(nrmResourceId.getScheme()); + if (!StringUtils.equalsIgnoreCase(orgResourceId.getValue(), RESOURCE_IDENTIFIER_VALUE.getPropertyPlaceholder())) { + orgResourceId.setValue(nrmResourceId.getValue()); + } + if (!StringUtils.equalsIgnoreCase(orgResourceId.getSchemeID(), RESOURCE_IDENTIFIER_SCHEME.getPropertyPlaceholder())) { + orgResourceId.setSchemeID(nrmResourceId.getScheme()); + } + try { // need to save resource because of the update on the resource identifier values reader.serializeNative(resource, responseData.getOutputStream(), true); @@ -220,9 +230,20 @@ public class OasisSMPResource20Handler extends AbstractOasisSMPHandler { throw new ResourceException(INVALID_RESOURCE, "Error occurred while reading the Oasis SMP 2.0 ServiceGroup with error: " + ExceptionUtils.getRootCauseMessage(e), e); } final ParticipantID participantId = resource.getParticipantID(); + String participantIdValue = participantId.getValue(); + String participantIdScheme = participantId.getSchemeID(); + if (equalsIgnoreCase(trim(participantIdValue), RESOURCE_IDENTIFIER_VALUE.getPropertyPlaceholder())) { + participantIdValue = identifier.getValue(); + } + + if (equalsIgnoreCase(trim(participantIdScheme), RESOURCE_IDENTIFIER_SCHEME.getPropertyPlaceholder())) { + participantIdScheme = identifier.getScheme(); + } + ResourceIdentifier xmlResourceIdentifier = smpIdentifierApi.normalizeResourceIdentifier( resourceData.getDomainCode(), - participantId.getValue(), participantId.getSchemeID()); + participantIdValue, participantIdScheme); + if (!xmlResourceIdentifier.equals(identifier)) { // Business identifier must equal path diff --git a/smp-resource-extensions/oasis-smp-spi/src/main/java/eu/europa/ec/smp/spi/handler/OasisSMPSubresource10Handler.java b/smp-resource-extensions/oasis-smp-spi/src/main/java/eu/europa/ec/smp/spi/handler/OasisSMPSubresource10Handler.java index 5ce2adbd960abcce362231bc60c3f51c44463156..4461e425f703a33e141bdb25e54739d46501779e 100644 --- a/smp-resource-extensions/oasis-smp-spi/src/main/java/eu/europa/ec/smp/spi/handler/OasisSMPSubresource10Handler.java +++ b/smp-resource-extensions/oasis-smp-spi/src/main/java/eu/europa/ec/smp/spi/handler/OasisSMPSubresource10Handler.java @@ -47,6 +47,7 @@ import java.io.InputStream; import java.util.Collections; import java.util.List; +import static eu.europa.ec.smp.spi.enums.TransientDocumentPropertyType.*; import static eu.europa.ec.smp.spi.exceptions.ResourceException.ErrorCode.*; @Component @@ -95,11 +96,16 @@ public class OasisSMPSubresource10Handler extends AbstractOasisSMPHandler { serviceInformationType.setProcessList(processListType); subresource.setServiceInformation(serviceInformationType); serviceInformationType.setParticipantIdentifier(new ParticipantIdentifierType()); - serviceInformationType.getParticipantIdentifier().setValue(identifier.getValue()); - serviceInformationType.getParticipantIdentifier().setScheme(identifier.getScheme()); + serviceInformationType.getParticipantIdentifier().setValue(RESOURCE_IDENTIFIER_VALUE.getPropertyPlaceholder()); + if (identifier.getScheme()!= null) { + serviceInformationType.getParticipantIdentifier().setScheme(RESOURCE_IDENTIFIER_SCHEME.getPropertyPlaceholder()); + } + serviceInformationType.setDocumentIdentifier(new DocumentIdentifier()); - serviceInformationType.getDocumentIdentifier().setValue(subresourceIdentifier.getValue()); - serviceInformationType.getDocumentIdentifier().setScheme(subresourceIdentifier.getScheme()); + serviceInformationType.getDocumentIdentifier().setValue(SUBRESOURCE_IDENTIFIER_VALUE.getPropertyPlaceholder()); + if (subresourceIdentifier.getScheme()!= null) { + serviceInformationType.getDocumentIdentifier().setScheme(SUBRESOURCE_IDENTIFIER_SCHEME.getPropertyPlaceholder()); + } try { reader.serializeNativeAny(subresource, responseData.getOutputStream(), true); diff --git a/smp-resource-extensions/oasis-smp-spi/src/main/java/eu/europa/ec/smp/spi/handler/OasisSMPSubresource20Handler.java b/smp-resource-extensions/oasis-smp-spi/src/main/java/eu/europa/ec/smp/spi/handler/OasisSMPSubresource20Handler.java index ab32b59d7622103682e6e97eecc89a4a2e36c272..663b5cdba44518470f4bbf6b7b039ca1cd8c068b 100644 --- a/smp-resource-extensions/oasis-smp-spi/src/main/java/eu/europa/ec/smp/spi/handler/OasisSMPSubresource20Handler.java +++ b/smp-resource-extensions/oasis-smp-spi/src/main/java/eu/europa/ec/smp/spi/handler/OasisSMPSubresource20Handler.java @@ -56,6 +56,7 @@ import java.time.OffsetDateTime; import java.util.Collections; import java.util.List; +import static eu.europa.ec.smp.spi.enums.TransientDocumentPropertyType.*; import static eu.europa.ec.smp.spi.exceptions.ResourceException.ErrorCode.*; @Component @@ -91,11 +92,17 @@ public class OasisSMPSubresource20Handler extends AbstractOasisSMPHandler { subresource.setSMPVersionID(new SMPVersionID()); subresource.getSMPVersionID().setValue("2.0"); subresource.setParticipantID(new ParticipantID()); - subresource.getParticipantID().setValue(identifier.getValue()); - subresource.getParticipantID().setSchemeID(identifier.getScheme()); + subresource.getParticipantID().setValue(RESOURCE_IDENTIFIER_VALUE.getPropertyPlaceholder()); + if (identifier.getScheme() != null) { + subresource.getParticipantID().setSchemeID(RESOURCE_IDENTIFIER_SCHEME.getPropertyPlaceholder()); + } + subresource.setServiceID(new ServiceID()); - subresource.getServiceID().setValue(subresourceIdentifier.getValue()); - subresource.getServiceID().setSchemeID(subresourceIdentifier.getScheme()); + subresource.getServiceID().setValue(SUBRESOURCE_IDENTIFIER_VALUE.getPropertyPlaceholder()); + if (subresourceIdentifier.getScheme() != null) { + subresource.getServiceID().setSchemeID(SUBRESOURCE_IDENTIFIER_SCHEME.getPropertyPlaceholder()); + } + ProcessMetadata processMetadata = new ProcessMetadata(); subresource.getProcessMetadatas().add(processMetadata); Process process = new Process(); diff --git a/smp-resource-extensions/oasis-smp-spi/src/main/java/eu/europa/ec/smp/spi/validation/Subresource10Validator.java b/smp-resource-extensions/oasis-smp-spi/src/main/java/eu/europa/ec/smp/spi/validation/Subresource10Validator.java index f845e99ea9999e3281413ac28f78d80f1838cdd0..d6499310f23d93f60f059a8bd594c00dcdfdabd6 100644 --- a/smp-resource-extensions/oasis-smp-spi/src/main/java/eu/europa/ec/smp/spi/validation/Subresource10Validator.java +++ b/smp-resource-extensions/oasis-smp-spi/src/main/java/eu/europa/ec/smp/spi/validation/Subresource10Validator.java @@ -32,8 +32,11 @@ import java.time.OffsetDateTime; import java.util.HashSet; import java.util.Set; +import static eu.europa.ec.smp.spi.enums.TransientDocumentPropertyType.*; import static eu.europa.ec.smp.spi.exceptions.ResourceException.ErrorCode.INVALID_PARAMETERS; import static eu.europa.ec.smp.spi.exceptions.ResourceException.ErrorCode.INVALID_RESOURCE; +import static org.apache.commons.lang3.StringUtils.equalsIgnoreCase; +import static org.apache.commons.lang3.StringUtils.trim; /** @@ -96,14 +99,36 @@ public class Subresource10Validator { final ParticipantIdentifierType participantId = serviceInformation.getParticipantIdentifier(); final DocumentIdentifier documentId = serviceInformation.getDocumentIdentifier(); + + String participantIdValue = participantId.getValue(); + String participantIdScheme = participantId.getScheme(); + String documentIdValue = documentId.getValue(); + String documentIdScheme = documentId.getScheme(); + + if (equalsIgnoreCase(trim(participantIdValue), RESOURCE_IDENTIFIER_VALUE.getPropertyPlaceholder())) { + participantIdValue = participantIdentifierFromUrl.getValue(); + } + + if (equalsIgnoreCase(trim(participantIdScheme), RESOURCE_IDENTIFIER_SCHEME.getPropertyPlaceholder())) { + participantIdScheme = participantIdentifierFromUrl.getScheme(); + } + + if (equalsIgnoreCase(trim(documentIdValue), SUBRESOURCE_IDENTIFIER_VALUE.getPropertyPlaceholder())) { + documentIdValue = documentIdentifierFromUrl.getValue(); + } + + if (equalsIgnoreCase(trim(documentIdScheme), SUBRESOURCE_IDENTIFIER_SCHEME.getPropertyPlaceholder())) { + documentIdScheme = documentIdentifierFromUrl.getScheme(); + } + ResourceIdentifier xmlResourceIdentifier = smpIdentifierApi.normalizeResourceIdentifier( domainCode, - participantId.getValue(), - participantId.getScheme()); + participantIdValue, + participantIdScheme); ResourceIdentifier xmlSubresourceIdentifier = smpIdentifierApi.normalizeSubresourceIdentifier( domainCode, - documentId.getValue(), - documentId.getScheme()); + documentIdValue, + documentIdScheme); ResourceIdentifier nrmResIdentifierFromUrl = smpIdentifierApi.normalizeResourceIdentifier( domainCode, diff --git a/smp-resource-extensions/oasis-smp-spi/src/main/java/eu/europa/ec/smp/spi/validation/Subresource20Validator.java b/smp-resource-extensions/oasis-smp-spi/src/main/java/eu/europa/ec/smp/spi/validation/Subresource20Validator.java index 00ff18e8fb8f1935e43dc723eed50fe445236c74..46e367c110ad9c1025f1319241e5c40ed804bce4 100644 --- a/smp-resource-extensions/oasis-smp-spi/src/main/java/eu/europa/ec/smp/spi/validation/Subresource20Validator.java +++ b/smp-resource-extensions/oasis-smp-spi/src/main/java/eu/europa/ec/smp/spi/validation/Subresource20Validator.java @@ -38,7 +38,11 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import static eu.europa.ec.smp.spi.enums.TransientDocumentPropertyType.*; +import static eu.europa.ec.smp.spi.enums.TransientDocumentPropertyType.SUBRESOURCE_IDENTIFIER_SCHEME; import static eu.europa.ec.smp.spi.exceptions.ResourceException.ErrorCode.INVALID_PARAMETERS; +import static org.apache.commons.lang3.StringUtils.equalsIgnoreCase; +import static org.apache.commons.lang3.StringUtils.trim; /** @@ -64,14 +68,38 @@ public class Subresource20Validator { final ParticipantID participantId = subresource.getParticipantID(); final ServiceID documentId = subresource.getServiceID(); + + String participantIdValue = participantId.getValue(); + String participantIdScheme = participantId.getSchemeID(); + String documentIdValue = documentId.getValue(); + String documentIdScheme = documentId.getSchemeID(); + + if (equalsIgnoreCase(trim(participantIdValue), RESOURCE_IDENTIFIER_VALUE.getPropertyPlaceholder())) { + participantIdValue = participantIdentifierFromUrl.getValue(); + } + + if (equalsIgnoreCase(trim(participantIdScheme), RESOURCE_IDENTIFIER_SCHEME.getPropertyPlaceholder())) { + participantIdScheme = participantIdentifierFromUrl.getScheme(); + } + + if (equalsIgnoreCase(trim(documentIdValue), SUBRESOURCE_IDENTIFIER_VALUE.getPropertyPlaceholder())) { + documentIdValue = documentIdentifierFromUrl.getValue(); + } + + if (equalsIgnoreCase(trim(documentIdScheme), SUBRESOURCE_IDENTIFIER_SCHEME.getPropertyPlaceholder())) { + documentIdScheme = documentIdentifierFromUrl.getScheme(); + } + ResourceIdentifier xmlResourceIdentifier = smpIdentifierApi.normalizeResourceIdentifier( domainCode, - participantId.getValue(), - participantId.getSchemeID()); + participantIdValue, + participantIdScheme); ResourceIdentifier xmlSubresourceIdentifier = smpIdentifierApi.normalizeSubresourceIdentifier( domainCode, - documentId.getValue(), - documentId.getSchemeID()); + documentIdValue, + documentIdScheme); + + if (!xmlResourceIdentifier.equals(participantIdentifierFromUrl)) { // Business identifier must equal path throw new ResourceException(INVALID_PARAMETERS, "Participant identifiers don't match between URL parameter [" + participantIdentifierFromUrl + "] and XML body: [" + xmlResourceIdentifier + "]"); @@ -84,7 +112,6 @@ public class Subresource20Validator { List<ProcessMetadata> processMetadata = subresource.getProcessMetadatas(); validateProcesses(processMetadata); - } diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/DocumentDao.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/DocumentDao.java index 860bde5ab9bcaddae2715f4909d074143c7e735c..b3f7542653983820b195b2e5177e77eab7bdd0c5 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/DocumentDao.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/DocumentDao.java @@ -63,6 +63,25 @@ public class DocumentDao extends BaseDao<DBDocument> { } } + /** + * Method returns the document for the (detached) subresource + * + * @param dbSubresource resource + * @return document for the resource or empty if not found + */ + public Optional<DBDocument> getDocumentForSubresource(DBSubresource dbSubresource) { + try { + // expected is only one domain, + TypedQuery<DBDocument> query = memEManager.createNamedQuery(QUERY_DOCUMENT_FOR_SUBRESOURCE, DBDocument.class); + query.setParameter(PARAM_SUBRESOURCE_ID, dbSubresource.getId()); + return Optional.of(query.getSingleResult()); + } catch (NonUniqueResultException e) { + throw new SMPRuntimeException(ErrorCode.RESOURCE_DOCUMENT_ERROR, dbSubresource.getIdentifierValue(), dbSubresource.getIdentifierScheme(), "Multiple documents"); + } catch (NoResultException e) { + return Optional.empty(); + } + } + public Optional<DBDocumentVersion> getCurrentDocumentVersionForResource(DBResource dbResource) { try { diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/DomainConfigurationDao.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/DomainConfigurationDao.java new file mode 100644 index 0000000000000000000000000000000000000000..39acb2d288f55b4bc0e0e733ba28a49a2aff062c --- /dev/null +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/DomainConfigurationDao.java @@ -0,0 +1,269 @@ +/*- + * #START_LICENSE# + * smp-webapp + * %% + * Copyright (C) 2017 - 2024 European Commission | eDelivery | DomiSMP + * %% + * Licensed under the EUPL, Version 1.2 or – as soon they will be approved by the European Commission - subsequent + * versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * + * [PROJECT_HOME]\license\eupl-1.2\license.txt or https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Unless required by applicable law or agreed to in writing, software distributed under the Licence is + * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and limitations under the Licence. + * #END_LICENSE# + */ +package eu.europa.ec.edelivery.smp.data.dao; + +import eu.europa.ec.edelivery.smp.config.SMPEnvironmentProperties; +import eu.europa.ec.edelivery.smp.config.enums.SMPDomainPropertyEnum; +import eu.europa.ec.edelivery.smp.config.enums.SMPEnvPropertyEnum; +import eu.europa.ec.edelivery.smp.data.model.DBDomain; +import eu.europa.ec.edelivery.smp.data.model.DBDomainConfiguration; +import eu.europa.ec.edelivery.smp.data.ui.DomainPropertyRO; +import eu.europa.ec.edelivery.smp.data.ui.PropertyRO; +import eu.europa.ec.edelivery.smp.data.ui.PropertyValidationRO; +import eu.europa.ec.edelivery.smp.data.ui.auth.SMPRole; +import eu.europa.ec.edelivery.smp.exceptions.SMPRuntimeException; +import eu.europa.ec.edelivery.smp.logging.SMPLogger; +import eu.europa.ec.edelivery.smp.logging.SMPLoggerFactory; +import eu.europa.ec.edelivery.smp.utils.PropertyUtils; +import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; + +import javax.persistence.TransactionRequiredException; +import javax.persistence.TypedQuery; +import java.io.File; +import java.nio.file.Paths; +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; + +import static eu.europa.ec.edelivery.smp.data.dao.QueryNames.PARAM_DOMAIN_ID; +import static eu.europa.ec.edelivery.smp.data.dao.QueryNames.QUERY_DOMAIN_CONFIGURATION_ALL; + + +/** + * Specific DAO for DomainConfiguration + * + * @author Joze Rihtarsic + * @since 5.1 + */ +@Repository +public class DomainConfigurationDao extends BaseDao<DBDomainConfiguration> { + + private static final SMPLogger LOG = SMPLoggerFactory.getLogger(DomainConfigurationDao.class); + + + /** + * Method returns all Domain properties for the given domain and role. If the property does not exist in the database, + * the default value is returned. If the role is not system admin, and + * property is system admin only, the property is not returned! + * + * @param domain - domain to get properties + * @param role - the properties are filtered based on the role. System admin gets all roles + * but other roles get only properties which are not system admin only. + * @return list of domain properties + */ + public List<DBDomainConfiguration> getDomainPropertiesForRole(DBDomain domain, SMPRole role) { + // get domain configuration from the database + List<DBDomainConfiguration> domainConfiguration = getDomainConfiguration(domain); + // convert list to map + Map<String, DBDomainConfiguration> dbList = domainConfiguration.stream() + .collect(Collectors.toMap(DBDomainConfiguration::getProperty, Function.identity())); + + // return only properties that are not system admin only + return Arrays.stream(SMPDomainPropertyEnum.values()) + .filter(domainPropertyRO -> role == SMPRole.SYSTEM_ADMIN || !domainPropertyRO.isSystemAdminOnly()) + .map(enumType -> { + if (dbList.containsKey(enumType.getProperty())) { + return dbList.get(enumType.getProperty()); + } + return createDBDomainConfiguration(enumType); + }).filter(Objects::nonNull) + .collect(Collectors.toList()); + } + + /** + * Method creates a Domain properties for the given SMPDomainPropertyEnum. The object is not + * persisted in the database. + */ + private DBDomainConfiguration createDBDomainConfiguration(SMPDomainPropertyEnum domainPropertyEnum) { + DBDomainConfiguration dbDomainConfiguration = new DBDomainConfiguration(); + dbDomainConfiguration.setProperty(domainPropertyEnum.getProperty()); + dbDomainConfiguration.setValue(domainPropertyEnum.getDefValue()); + dbDomainConfiguration.setDescription(domainPropertyEnum.getDesc()); + dbDomainConfiguration.setUseSystemDefault(true); + return dbDomainConfiguration; + } + + /** + * Method updates domain properties for the given domain and role. If the role is not system admin, and + * property is system admin only, the property is not updated! + * + * @param domain - domain to update properties + * @param domainProperties - list of domain properties + * @param role - role of the user + * @return list of updated domain properties + */ + @Transactional + public List<DBDomainConfiguration> updateDomainPropertiesForRole(DBDomain domain, + List<DomainPropertyRO> domainProperties, + SMPRole role) { + + // get current domain configuration + Map<String, DBDomainConfiguration> currentDomainConfiguration = getDomainConfiguration(domain) + .stream().collect(Collectors.toMap(DBDomainConfiguration::getProperty, Function.identity())); + Map<String, DomainPropertyRO> newDomainPropertyValues = + domainProperties.stream().collect(Collectors.toMap(DomainPropertyRO::getProperty, Function.identity())); + + List<DBDomainConfiguration> listOfDomainConfiguration = new ArrayList<>(); + + // database domain configuration property list must match SMPDomainPropertyEnum + for (SMPDomainPropertyEnum domainProp : SMPDomainPropertyEnum.values()) { + if (role != SMPRole.SYSTEM_ADMIN && domainProp.isSystemAdminOnly()) { + // skip system admin only properties + continue; + } + DBDomainConfiguration domainConfiguration = currentDomainConfiguration.get(domainProp.getProperty()); + DomainPropertyRO domainPropertyRO = newDomainPropertyValues.get(domainProp.getProperty()); + // if property already exists in the database, update value + DBDomainConfiguration updatedDomainProp = updateDomainProperty(domain, domainProp, domainConfiguration, domainPropertyRO); + listOfDomainConfiguration.add(updatedDomainProp); + // remove updated property from the map + currentDomainConfiguration.remove(domainProp.getProperty()); + LOG.debug("Updated domain property [{}]: [{}] for domain [{}]", + domainProp.getProperty(), updatedDomainProp, domain.getDomainCode()); + + } + // remove properties that are not in the new list + currentDomainConfiguration.values().forEach(domainConfiguration -> { + removeConfiguration(domainConfiguration); + LOG.debug("Removed domain property [{}]: [{}] for domain [{}]", + domainConfiguration.getProperty(), domainConfiguration.getValue(), + domain.getDomainCode()); + }); + + // + return listOfDomainConfiguration; + } + + + public PropertyValidationRO validateDomainProperty(PropertyRO propertyRO) { + LOG.info("Validate property: [{}]", propertyRO.getProperty()); + PropertyValidationRO propertyValidationRO = new PropertyValidationRO(); + propertyValidationRO.setProperty(propertyRO.getProperty()); + propertyValidationRO.setValue(propertyRO.getValue()); + + Optional<SMPDomainPropertyEnum> optPropertyEnum = SMPDomainPropertyEnum.getByProperty(propertyRO.getProperty()); + if (!optPropertyEnum.isPresent()) { + LOG.debug("Property: [{}] is not Domain SMP property!", propertyRO.getProperty()); + propertyValidationRO.setErrorMessage("Property [" + propertyRO.getProperty() + "] is not SMP property!"); + propertyValidationRO.setPropertyValid(false); + return propertyValidationRO; + } + + SMPDomainPropertyEnum propertyEnum = optPropertyEnum.get(); + return validateDomainPropertyValue(propertyEnum, propertyValidationRO); + } + + /** + * Method validates domain property value for given property enum. + * + * @param propertyEnum - property enum + * @param propertyValidationRO - property validation object with value. + * @return property validation object with validation result and error message if validation failed. + */ + private PropertyValidationRO validateDomainPropertyValue(SMPDomainPropertyEnum propertyEnum, + PropertyValidationRO propertyValidationRO) { + // try to parse value + try { + File confDir = Paths.get(SMPEnvironmentProperties.getInstance().getEnvPropertyValue(SMPEnvPropertyEnum.SECURITY_FOLDER)).toFile(); + PropertyUtils.parseProperty(propertyEnum.getPropertyEnum(), propertyValidationRO.getValue(), confDir); + } catch (SMPRuntimeException ex) { + propertyValidationRO.setErrorMessage(ex.getMessage()); + propertyValidationRO.setPropertyValid(false); + return propertyValidationRO; + } + + propertyValidationRO.setPropertyValid(true); + return propertyValidationRO; + } + + + /** + * Returns the domain properties for the given domain. + * + * @param domain - domain for which the properties are requested + * @return - list of domain properties + */ + public List<DBDomainConfiguration> getDomainConfiguration(DBDomain domain) { + TypedQuery<DBDomainConfiguration> query = memEManager.createNamedQuery(QUERY_DOMAIN_CONFIGURATION_ALL, + DBDomainConfiguration.class); + query.setParameter(PARAM_DOMAIN_ID, domain.getId()); + return query.getResultList(); + } + + /** + * Update domain property. If property does not exist in the database, it will be created. + * The method must be called in transactional context, else TransactionRequiredException + * will be thrown from JPA merge method. + * + * @param domain - domain to update. Value is used in case domain configuration does not exist in the database. + * @param domainProp - domain property to update + * @param domainConfiguration - current domain configuration or null if it does not exist in the database. + * The object must be attached to the persistence context. + * @param domainPropertyRO - new domain property value and system default flag + * @return new/updated domain configuration + */ + public DBDomainConfiguration updateDomainProperty(DBDomain domain, SMPDomainPropertyEnum domainProp, + DBDomainConfiguration domainConfiguration, DomainPropertyRO domainPropertyRO) { + if (domainConfiguration != null && domainPropertyRO == null) { + LOG.debug("Domain property [{}] is not set. Skip the update of property value.", + domainProp.getProperty()); + return domainConfiguration; + } + + if (domainConfiguration == null) { + domainConfiguration = new DBDomainConfiguration(); + domainConfiguration.setDomain(domain); + domainConfiguration.setProperty(domainProp.getProperty()); + } + + if (domainPropertyRO != null) { + domainConfiguration.setValue(domainPropertyRO.getValue()); + domainConfiguration.setUseSystemDefault(domainPropertyRO.isSystemDefault()); + } else { + LOG.debug("Domain property [{}] is not set and propperty does not exists in database. Create new system default value", + domainProp.getProperty()); + domainConfiguration.setValue(domainProp.getDefValue()); + domainConfiguration.setDescription(domainProp.getDesc()); + domainConfiguration.setUseSystemDefault(true); + } + mergeConfiguration(domainConfiguration); + return domainConfiguration; + } + + + /** + * The method Merge the state of the given domain configuration into the current + * persistence context. The method must be + * called in existing transaction, and it is used to manage domain properties. + * + * @param entity - domain configuration to be merged + * @return - jpa merged/managed domain configuration + * @throws TransactionRequiredException if there is no transaction when + * invoked on a container-managed entity manager of that is of type + * <code>PersistenceContextType.TRANSACTION</code> + */ + public DBDomainConfiguration mergeConfiguration(DBDomainConfiguration entity) { + return memEManager.merge(entity); + } + + public void removeConfiguration(DBDomainConfiguration entity) { + memEManager.remove(entity); + } +} diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/DomainDao.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/DomainDao.java index 293516817fa5c08dda52c4620e17c244beb1c40b..c312c1b63c6c149be2b8a2b294af4be7f23e7e82 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/DomainDao.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/DomainDao.java @@ -19,20 +19,17 @@ package eu.europa.ec.edelivery.smp.data.dao; -import eu.europa.ec.edelivery.smp.config.enums.SMPDomainPropertyEnum; import eu.europa.ec.edelivery.smp.data.enums.MembershipRoleType; import eu.europa.ec.edelivery.smp.data.model.DBDomain; -import eu.europa.ec.edelivery.smp.data.model.DBDomainConfiguration; -import eu.europa.ec.edelivery.smp.data.ui.DomainPropertyRO; import eu.europa.ec.edelivery.smp.exceptions.ErrorCode; import eu.europa.ec.edelivery.smp.exceptions.SMPRuntimeException; import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; import javax.persistence.NoResultException; import javax.persistence.NonUniqueResultException; -import javax.persistence.TransactionRequiredException; import javax.persistence.TypedQuery; import java.util.List; import java.util.Optional; @@ -48,7 +45,7 @@ import static eu.europa.ec.edelivery.smp.exceptions.ErrorCode.ILLEGAL_STATE_DOMA */ @Repository public class DomainDao extends BaseDao<DBDomain> { - + private static final Logger LOG = org.slf4j.LoggerFactory.getLogger(DomainDao.class); /** * Returns the only single record from smp_domain table. @@ -231,71 +228,4 @@ public class DomainDao extends BaseDao<DBDomain> { } return false; } - - /** - * Returns the domain properties for the given domain. - * - * @param domain - domain for which the properties are requested - * @return - list of domain properties - */ - public List<DBDomainConfiguration> getDomainConfiguration(DBDomain domain) { - TypedQuery<DBDomainConfiguration> query = memEManager.createNamedQuery(QUERY_DOMAIN_CONFIGURATION_ALL, - DBDomainConfiguration.class); - query.setParameter(PARAM_DOMAIN_ID, domain.getId()); - return query.getResultList(); - } - - /** - * Update domain property. If property does not exist in the database, it will be created. - * The method must be called in transactional context, else TransactionRequiredException - * will be thrown from JPA merge method. - * - * @param domain - domain to update. Value is used in case domain configuration does not exist in the database. - * @param domainProp - domain property to update - * @param domainConfiguration - current domain configuration or null if it does not exist in the database. - * The object must be attached to the persistence context. - * @param domainPropertyRO - new domain property value and system default flag - * @return new/updated domain configuration - */ - public DBDomainConfiguration updateDomainProperty(DBDomain domain, SMPDomainPropertyEnum domainProp, - DBDomainConfiguration domainConfiguration, DomainPropertyRO domainPropertyRO) { - if (domainConfiguration == null) { - domainConfiguration = new DBDomainConfiguration(); - domainConfiguration.setDomain(domain); - domainConfiguration.setProperty(domainProp.getProperty()); - // attach domain configuration to the persistence context - mergeConfiguration(domainConfiguration); - } - - if (domainPropertyRO != null) { - domainConfiguration.setValue(domainPropertyRO.getValue()); - domainConfiguration.setUseSystemDefault(domainPropertyRO.isSystemDefault()); - } else { - domainConfiguration.setValue(domainProp.getDefValue()); - domainConfiguration.setUseSystemDefault(true); - } - - return domainConfiguration; - } - - - /** - * The method Merge the state of the given domain configuration into the current - * persistence context. The method must be - * called in existing transaction, and it is used to manage domain properties. - * - * @param entity - domain configuration to be merged - * @return - jpa merged/managed domain configuration - * @throws TransactionRequiredException if there is no transaction when - * invoked on a container-managed entity manager of that is of type - * <code>PersistenceContextType.TRANSACTION</code> - */ - public DBDomainConfiguration mergeConfiguration(DBDomainConfiguration entity) { - return memEManager.merge(entity); - } - - public void removeConfiguration(DBDomainConfiguration entity) { - memEManager.remove(entity); - } - } diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/QueryNames.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/QueryNames.java index e8f516b34deb30d12d63a3f8df21cc5ce8ca7870..12ef52e08969945dd2283de8df03cada89e15714 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/QueryNames.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/QueryNames.java @@ -119,6 +119,7 @@ public class QueryNames { public static final String QUERY_RESOURCE_DEF_BY_IDENTIFIER_EXTENSION = "DBExtResourceDef.getByIdentifierExtension"; public static final String QUERY_DOCUMENT_FOR_RESOURCE = "DBDocument.getForResource"; + public static final String QUERY_DOCUMENT_FOR_SUBRESOURCE = "DBDocument.getForSubresource"; public static final String QUERY_DOCUMENT_VERSION_CURRENT_FOR_RESOURCE = "DBDocumentVersion.forCurrentForResource"; public static final String QUERY_DOCUMENT_VERSION_LIST_FOR_RESOURCE = "DBDocumentVersion.getAllForResource"; diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/DBDomainConfiguration.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/DBDomainConfiguration.java index a2b5c9c29b7ad9770692bc714459e32c226ada20..c4db75785831d43d069fd52b1a76164f8fda7063 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/DBDomainConfiguration.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/DBDomainConfiguration.java @@ -68,7 +68,7 @@ public class DBDomainConfiguration extends BaseEntity { @ColumnDescription(comment = "Property description") String description; - @Column(name = "USER_SYSTEM_DEFAULT", nullable = false) + @Column(name = "SYSTEM_DEFAULT", nullable = false) @ColumnDescription(comment = "Use system default value") boolean useSystemDefault = true; diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/doc/DBDocument.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/doc/DBDocument.java index dcf41cc1c5f2c509526bb0ea387c6b78356f3694..5e9d842eeae664501f16579ae9c1726cc233c1b7 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/doc/DBDocument.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/doc/DBDocument.java @@ -8,9 +8,9 @@ * versions of the EUPL (the "Licence"); * You may not use this work except in compliance with the Licence. * You may obtain a copy of the Licence at: - * + * * [PROJECT_HOME]\license\eupl-1.2\license.txt or https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the Licence is * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the Licence for the specific language governing permissions and limitations under the Licence. @@ -32,6 +32,7 @@ import java.util.List; import java.util.Objects; import static eu.europa.ec.edelivery.smp.data.dao.QueryNames.QUERY_DOCUMENT_FOR_RESOURCE; +import static eu.europa.ec.edelivery.smp.data.dao.QueryNames.QUERY_DOCUMENT_FOR_SUBRESOURCE; /** * Database optimization: load service metadata xml only when needed and @@ -46,7 +47,8 @@ import static eu.europa.ec.edelivery.smp.data.dao.QueryNames.QUERY_DOCUMENT_FOR_ @Table(name = "SMP_DOCUMENT") @org.hibernate.annotations.Table(appliesTo = "SMP_DOCUMENT", comment = "SMP document entity for resources and subresources") @NamedQueries({ - @NamedQuery(name = QUERY_DOCUMENT_FOR_RESOURCE, query = "SELECT d FROM DBResource r JOIN r.document d WHERE r.id =:resource_id") + @NamedQuery(name = QUERY_DOCUMENT_FOR_RESOURCE, query = "SELECT d FROM DBResource r JOIN r.document d WHERE r.id =:resource_id"), + @NamedQuery(name = QUERY_DOCUMENT_FOR_SUBRESOURCE, query = "SELECT d FROM DBSubresource sr JOIN sr.document d WHERE sr.id =:subresource_id") }) public class DBDocument extends BaseEntity { private static final SMPLogger LOG = SMPLoggerFactory.getLogger(DBDocument.class); @@ -63,7 +65,6 @@ public class DBDocument extends BaseEntity { cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY - ) List<DBDocumentVersion> documentVersions; @@ -77,6 +78,13 @@ public class DBDocument extends BaseEntity { @Column(name = "NAME") private String name; + @OneToMany( + mappedBy = "document", + cascade = CascadeType.ALL, + orphanRemoval = true, + fetch = FetchType.LAZY + ) + private List<DBDocumentProperty> documentProperties = new ArrayList<>(); @Override public Long getId() { @@ -90,17 +98,18 @@ public class DBDocument extends BaseEntity { /** * Returns document version ordered from the latest version to first version + * * @return document versions */ public List<DBDocumentVersion> getDocumentVersions() { - if (documentVersions ==null) { + if (documentVersions == null) { documentVersions = new ArrayList<>(); } return documentVersions; } - public DBDocumentVersion addNewDocumentVersion(DBDocumentVersion documentVersion){ - if (documentVersion.getId() !=null && getDocumentVersions().contains(documentVersion)) { + public DBDocumentVersion addNewDocumentVersion(DBDocumentVersion documentVersion) { + if (documentVersion.getId() != null && getDocumentVersions().contains(documentVersion)) { LOG.info("Document version [{}] already exists on document [{}]", documentVersion, this); return documentVersion; } @@ -111,19 +120,19 @@ public class DBDocument extends BaseEntity { return documentVersion; } - public void removeDocumentVersion(DBDocumentVersion documentVersion){ + public void removeDocumentVersion(DBDocumentVersion documentVersion) { boolean removed = getDocumentVersions().remove(documentVersion); - if (removed){ + if (removed) { documentVersion.setDocument(null); } } - protected int getNextVersionIndex(){ + protected int getNextVersionIndex() { List<DBDocumentVersion> list = getDocumentVersions(); return list.stream() .map(DBDocumentVersion::getVersion) - .reduce(0, (a, b) -> Integer.max(a, b)) + 1; + .reduce(0, Integer::max) + 1; } @@ -135,6 +144,29 @@ public class DBDocument extends BaseEntity { this.currentVersion = currentVersion; } + public List<DBDocumentProperty> getDocumentProperties() { + if (documentProperties == null) { + documentProperties = new ArrayList<>(); + } + return documentProperties; + } + + private void removeTransientProperties() { + getDocumentProperties().removeIf(DBDocumentProperty::isTransient); + } + + @Override + public void prePersist() { + super.prePersist(); + removeTransientProperties(); + } + + @Override + public void preUpdate() { + super.preUpdate(); + removeTransientProperties(); + } + public String getMimeType() { return mimeType; diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/doc/DBDocumentProperty.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/doc/DBDocumentProperty.java new file mode 100644 index 0000000000000000000000000000000000000000..7ba6f77a614825d72228e17c95ef1cb063c5f943 --- /dev/null +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/doc/DBDocumentProperty.java @@ -0,0 +1,181 @@ +/*- + * #START_LICENSE# + * smp-server-library + * %% + * Copyright (C) 2017 - 2024 European Commission | eDelivery | DomiSMP + * %% + * Licensed under the EUPL, Version 1.2 or – as soon they will be approved by the European Commission - subsequent + * versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * + * [PROJECT_HOME]\license\eupl-1.2\license.txt or https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Unless required by applicable law or agreed to in writing, software distributed under the Licence is + * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and limitations under the Licence. + * #END_LICENSE# + */ +package eu.europa.ec.edelivery.smp.data.model.doc; + +import eu.europa.ec.edelivery.smp.config.enums.SMPPropertyTypeEnum; +import eu.europa.ec.edelivery.smp.data.dao.utils.ColumnDescription; +import eu.europa.ec.edelivery.smp.data.model.BaseEntity; +import eu.europa.ec.edelivery.smp.data.model.CommonColumnsLengths; +import eu.europa.ec.smp.spi.enums.TransientDocumentPropertyType; +import org.apache.commons.lang3.StringUtils; +import org.hibernate.annotations.GenericGenerator; +import org.hibernate.envers.Audited; + +import javax.persistence.*; +import javax.validation.constraints.NotNull; +import java.time.OffsetDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Objects; +import java.util.StringJoiner; + +/** + * Document property entity + * + * @since 5.1 + * @author Joze Rihtarsic + */ + +@Entity +@Audited +@Table(name = "SMP_DOCUMENT_PROPERTY", + indexes = {@Index(name = "SMP_DOC_PROP_IDX", columnList = "FK_DOCUMENT_ID, PROPERTY_NAME", unique = true) + }) +public class DBDocumentProperty extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.AUTO, generator = "SMP_DOC_PROP_SEQ") + @GenericGenerator(name = "SMP_DOC_PROP_SEQ", strategy = "native") + @Column(name = "ID") + @ColumnDescription(comment = "Unique document property id") + Long id; + + @NotNull + @Column(name = "PROPERTY_NAME") + protected String property; + + @Column(name = "PROPERTY_VALUE", length = CommonColumnsLengths.MAX_MEDIUM_TEXT_LENGTH) + private String value; + + @Column(name = "DESCRIPTION", length = CommonColumnsLengths.MAX_FREE_TEXT_LENGTH) + @ColumnDescription(comment = "Property description") + String description; + + @Column(name = "PROPERTY_TYPE", length = CommonColumnsLengths.MAX_TEXT_LENGTH_64) + @Enumerated(EnumType.STRING) + private SMPPropertyTypeEnum type; + + @NotNull + @ManyToOne + @JoinColumn(name = "FK_DOCUMENT_ID") + private DBDocument document; + + public DBDocumentProperty() { + } + + public DBDocumentProperty(String property, String value, DBDocument document) { + this.property = property; + this.value = value; + this.document = document; + } + + public DBDocumentProperty(String property, OffsetDateTime value, DBDocument document) { + this.property = property; + this.document = document; + setDateTime(value); + } + + public void setId(Long id) { + this.id = id; + } + + @Override + public Long getId() { + return id; + } + + public String getProperty() { + return property; + } + + public void setProperty(String property) { + this.property = property; + } + + public DBDocument getDocument() { + return document; + } + + public void setDocument(DBDocument document) { + this.document = document; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public SMPPropertyTypeEnum getType() { + return type; + } + + public void setType(SMPPropertyTypeEnum type) { + this.type = type; + } + + @Transient + public void setDateTime(OffsetDateTime date) { + setValue(date == null ? null : date.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME)); + } + + @Transient + public OffsetDateTime getDateTime() { + return StringUtils.isBlank(value) ? null : OffsetDateTime.parse(getValue(), DateTimeFormatter.ISO_OFFSET_DATE_TIME); + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + if (!super.equals(o)) return false; + DBDocumentProperty that = (DBDocumentProperty) o; + return Objects.equals(id, that.id) && Objects.equals(property, that.property) && Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), id, property, value, document); + } + + public boolean isTransient() { + return TransientDocumentPropertyType.fromPropertyName(getProperty()) != null; + } + + @Override + public String toString() { + return new StringJoiner(", ", DBDocumentProperty.class.getSimpleName() + "[", "]") + .add("id=" + id) + .add("property='" + property + "'") + .add("value='" + value + "'") + .add("description='" + description + "'") + .add("type=" + type) + .add("document=" + document) + .toString(); + } +} diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/doc/DBDocumentVersion.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/doc/DBDocumentVersion.java index e03a7b58fdcf75b4e788f6da10bac76a15f04e8b..02cd71db827e029e6cf52c0a941ebd9f887ad4e4 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/doc/DBDocumentVersion.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/doc/DBDocumentVersion.java @@ -71,12 +71,10 @@ public class DBDocumentVersion extends BaseEntity { @ColumnDescription(comment = "Unique version document id") Long id; - @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "FK_DOCUMENT_ID") private DBDocument document; - @Column(name = "VERSION", nullable = false) private int version; diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/DocumentPropertyRO.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/DocumentPropertyRO.java new file mode 100644 index 0000000000000000000000000000000000000000..648a3142beb41173286ce8834e319e853ea07ad3 --- /dev/null +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/DocumentPropertyRO.java @@ -0,0 +1,75 @@ +package eu.europa.ec.edelivery.smp.data.ui; + + +import eu.europa.ec.edelivery.smp.config.enums.SMPPropertyTypeEnum; + +/** + * Document property contains values for updating the document variables. The + * properties are used with document templates such are ${my.property.name}. + * + * @author Joze Rihtarsic + * @since 5.1 + */ +public class DocumentPropertyRO extends BaseRO { + + private static final long serialVersionUID = 9008583888835630036L; + private String property; + private String value; + private String desc; + private SMPPropertyTypeEnum type = SMPPropertyTypeEnum.STRING; + // the property is readonly and can not be changed. Example of readonly + // property is resource identifier + private boolean readonly; + + public DocumentPropertyRO() { + } + + public DocumentPropertyRO(String property, String value, String desc, boolean readonly) { + this.property = property; + this.value = value; + this.desc = desc; + this.readonly = readonly; + } + + public String getProperty() { + return property; + } + + public void setProperty(String property) { + this.property = property; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + public SMPPropertyTypeEnum getType() { + return type; + } + + public void setType(SMPPropertyTypeEnum type) { + this.type = type; + } + + public boolean isReadonly() { + return readonly; + } + + public void setReadonly(boolean readonly) { + this.readonly = readonly; + } + + +} diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/DocumentRo.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/DocumentRO.java similarity index 74% rename from smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/DocumentRo.java rename to smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/DocumentRO.java index 8152c4183d43cca711ed22790ea305b319eb273f..d115312a5b676e50b51817f7f95cbcb9e2768e5e 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/DocumentRo.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/DocumentRO.java @@ -8,9 +8,9 @@ * versions of the EUPL (the "Licence"); * You may not use this work except in compliance with the Licence. * You may obtain a copy of the Licence at: - * + * * [PROJECT_HOME]\license\eupl-1.2\license.txt or https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the Licence is * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the Licence for the specific language governing permissions and limitations under the Licence. @@ -18,11 +18,14 @@ */ package eu.europa.ec.edelivery.smp.data.ui; +import eu.europa.ec.edelivery.smp.config.enums.SMPPropertyTypeEnum; +import eu.europa.ec.edelivery.smp.data.ui.enums.EntityROStatus; + import java.time.OffsetDateTime; import java.util.ArrayList; import java.util.List; -public class DocumentRo { +public class DocumentRO extends BaseRO { String documentId; String mimeType; @@ -31,8 +34,11 @@ public class DocumentRo { String name; Integer payloadVersion; String payload; + private int payloadStatus = EntityROStatus.PERSISTED.getStatusNumber(); OffsetDateTime payloadCreatedOn; + List<DocumentPropertyRO> properties = new ArrayList<>(); + public String getDocumentId() { return documentId; } @@ -88,6 +94,14 @@ public class DocumentRo { this.payload = payload; } + public int getPayloadStatus() { + return payloadStatus; + } + + public void setPayloadStatus(int payloadStatus) { + this.payloadStatus = payloadStatus; + } + public OffsetDateTime getPayloadCreatedOn() { return payloadCreatedOn; } @@ -95,4 +109,15 @@ public class DocumentRo { public void setPayloadCreatedOn(OffsetDateTime payloadCreatedOn) { this.payloadCreatedOn = payloadCreatedOn; } + + public List<DocumentPropertyRO> getProperties() { + return properties; + } + + public void addProperty(String key, String value, String description, SMPPropertyTypeEnum type, boolean readonly) { + DocumentPropertyRO propertyRO = new DocumentPropertyRO(key, value, description, readonly); + propertyRO.setType(type); + + this.properties.add(propertyRO); + } } diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/enums/EntityROStatus.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/enums/EntityROStatus.java index b8847430399cc14d59e89a4ddd3727b593276f82..25cccee05db2b1249916340f34be0bee79c06699 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/enums/EntityROStatus.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/enums/EntityROStatus.java @@ -19,6 +19,8 @@ package eu.europa.ec.edelivery.smp.data.ui.enums; +import java.util.Arrays; + /** * Enumeration of Resource Object status. * @author Joze Rihtarsic @@ -28,7 +30,7 @@ public enum EntityROStatus { PERSISTED(0), UPDATED(1), NEW(2), - REMOVE(3), + REMOVED(3), ERROR(4); int statusNumber; @@ -37,7 +39,18 @@ public enum EntityROStatus { this.statusNumber = statusNumber; } - public int getStatusNumber() { + public final int getStatusNumber() { return statusNumber; } + + /** + * Method returns EntityROStatus based on status number. If status number is not found, method returns null. + * @param statusNumber status number + * @return EntityROStatus or null + */ + public static EntityROStatus fromStatusNumber(int statusNumber) { + return Arrays.stream(EntityROStatus.values()) + .filter(entityROStatus -> entityROStatus.getStatusNumber() == statusNumber) + .findFirst().orElse(null); + } } diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/IdentifierFormatterService.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/IdentifierFormatterService.java index 1a430ce36ab74e37ccecd841c72aad798f2da6d1..01b27ec7a603981f568f2623395c165582027191 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/IdentifierFormatterService.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/IdentifierFormatterService.java @@ -5,6 +5,7 @@ import eu.europa.ec.dynamicdiscovery.model.identifiers.types.EBCorePartyIdFormat import eu.europa.ec.edelivery.smp.config.enums.SMPDomainPropertyEnum; import eu.europa.ec.edelivery.smp.config.enums.SMPPropertyEnum; import eu.europa.ec.edelivery.smp.config.enums.SMPPropertyTypeEnum; +import eu.europa.ec.edelivery.smp.data.dao.DomainConfigurationDao; import eu.europa.ec.edelivery.smp.data.dao.DomainDao; import eu.europa.ec.edelivery.smp.data.model.DBDomain; import eu.europa.ec.edelivery.smp.data.model.DBDomainConfiguration; @@ -33,10 +34,14 @@ public class IdentifierFormatterService { private static final Logger LOG = LoggerFactory.getLogger(IdentifierFormatterService.class); private final DomainDao domainDao; + private final DomainConfigurationDao domainConfigurationDao; private final ConfigurationService configurationService; - public IdentifierFormatterService(DomainDao domainDao, ConfigurationService configurationService) { + public IdentifierFormatterService(DomainDao domainDao, + DomainConfigurationDao domainConfigurationDao, + ConfigurationService configurationService) { this.domainDao = domainDao; + this.domainConfigurationDao = domainConfigurationDao; this.configurationService = configurationService; } @@ -53,7 +58,7 @@ public class IdentifierFormatterService { DBDomain domain = domainDao.getDomainByCode(domainCode) .orElseThrow(() -> new SMPRuntimeException(ErrorCode.DOMAIN_NOT_EXISTS, domainCode)); - List<DBDomainConfiguration> listDomainConf = domainDao.getDomainConfiguration(domain); + List<DBDomainConfiguration> listDomainConf = domainConfigurationDao.getDomainConfiguration(domain); IdentifierFormatter identifierFormatter = IdentifierFormatter.Builder .create() .addFormatterTypes(new EBCorePartyIdFormatterType()) @@ -82,7 +87,7 @@ public class IdentifierFormatterService { } DBDomain domain = domainDao.getDomainByCode(domainCode) .orElseThrow(() -> new SMPRuntimeException(ErrorCode.DOMAIN_NOT_EXISTS, domainCode)); - List<DBDomainConfiguration> listDomainConf = domainDao.getDomainConfiguration(domain); + List<DBDomainConfiguration> listDomainConf = domainConfigurationDao.getDomainConfiguration(domain); IdentifierFormatter identifierFormatter = IdentifierFormatter.Builder .create() .build(); diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/resource/AbstractResourceHandler.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/resource/AbstractResourceHandler.java index f30cd5dd7ac9e1ea015b79fff16b2433b97e2a71..fabb69722db7e1e63e1b7e0083871d8bffcc9827 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/resource/AbstractResourceHandler.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/resource/AbstractResourceHandler.java @@ -8,9 +8,9 @@ * versions of the EUPL (the "Licence"); * You may not use this work except in compliance with the Licence. * You may obtain a copy of the Licence at: - * + * * [PROJECT_HOME]\license\eupl-1.2\license.txt or https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the Licence is * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the Licence for the specific language governing permissions and limitations under the Licence. @@ -30,6 +30,7 @@ import eu.europa.ec.edelivery.smp.logging.SMPLoggerFactory; import eu.europa.ec.edelivery.smp.services.spi.SPIUtils; import eu.europa.ec.edelivery.smp.services.spi.data.SpiRequestData; import eu.europa.ec.edelivery.smp.servlet.ResourceResponse; +import eu.europa.ec.edelivery.smp.utils.StringNamedSubstitutor; import eu.europa.ec.smp.spi.api.model.RequestData; import eu.europa.ec.smp.spi.api.model.ResponseData; import eu.europa.ec.smp.spi.exceptions.ResourceException; @@ -39,8 +40,11 @@ import eu.europa.ec.smp.spi.resource.SubresourceDefinitionSpi; import org.apache.commons.lang3.StringUtils; import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.io.InputStream; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.stream.Collectors; @@ -109,10 +113,18 @@ public class AbstractResourceHandler { if (content == null || content.length == 0) { throw new SMPRuntimeException(ErrorCode.RESOURCE_DOCUMENT_MISSING, resource.getIdentifierValue(), resource.getIdentifierScheme()); } - ByteArrayInputStream inputStream = new ByteArrayInputStream(content); - return buildRequestDataForResource(domain, - resource, - inputStream); + // read and replace properties + Map<String, Object> docProp = resourceStorage.getResourceProperties(resource); + + try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { + StringNamedSubstitutor.resolve(new ByteArrayInputStream(content), docProp, baos); + ByteArrayInputStream inputStream = new ByteArrayInputStream(baos.toByteArray()); + return buildRequestDataForResource(domain, + resource, + inputStream); + } catch (IOException e) { + throw new SMPRuntimeException(ErrorCode.RESOURCE_DOCUMENT_MISSING, resource.getIdentifierValue(), resource.getIdentifierScheme()); + } } public RequestData buildRequestDataForResource(DBDomain domain, DBResource resource, InputStream inputStream) { @@ -129,10 +141,18 @@ public class AbstractResourceHandler { subresource.getIdentifierValue(), subresource.getIdentifierScheme(), resource.getIdentifierValue(), resource.getIdentifierScheme()); } - return new SpiRequestData(domain.getDomainCode(), - SPIUtils.toUrlIdentifier(resource), - SPIUtils.toUrlIdentifier(subresource), - new ByteArrayInputStream(content)); + + Map<String, Object> docProp = resourceStorage.getSubresourceProperties(resource, subresource); + + try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { + StringNamedSubstitutor.resolve(new ByteArrayInputStream(content), docProp, baos); + return new SpiRequestData(domain.getDomainCode(), + SPIUtils.toUrlIdentifier(resource), + SPIUtils.toUrlIdentifier(subresource), + new ByteArrayInputStream(baos.toByteArray())); + } catch (IOException e) { + throw new SMPRuntimeException(ErrorCode.RESOURCE_DOCUMENT_MISSING, resource.getIdentifierValue(), resource.getIdentifierScheme()); + } } public RequestData buildRequestDataForSubResource(DBDomain domain, DBResource resource, DBSubresource subresource, InputStream inputStream) { diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/resource/ResourceHandlerService.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/resource/ResourceHandlerService.java index bab20637e86f333953200a7c0e7f0642d4f65ada..50f35d88d0f5c6e244638631173c53eb0a6bf880 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/resource/ResourceHandlerService.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/resource/ResourceHandlerService.java @@ -120,7 +120,6 @@ public class ResourceHandlerService extends AbstractResourceHandler { LOG.debug("Handle the CREATE action for resource request [{}]", resourceRequest); // locate the resource handler - ResolvedData resolvedData = resourceRequest.getResolvedData(); DBResource resource = resolvedData.getResource(); DBDomain domain = resolvedData.getDomain(); diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/resource/ResourceStorage.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/resource/ResourceStorage.java index cb297d3a8a82be2d0926ffc85e77677d578177c0..9d8aba290f9a907848f55aa96a07169e9c7f6ff9 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/resource/ResourceStorage.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/resource/ResourceStorage.java @@ -8,9 +8,9 @@ * versions of the EUPL (the "Licence"); * You may not use this work except in compliance with the Licence. * You may obtain a copy of the Licence at: - * + * * [PROJECT_HOME]\license\eupl-1.2\license.txt or https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the Licence is * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the Licence for the specific language governing permissions and limitations under the Licence. @@ -28,11 +28,17 @@ import eu.europa.ec.edelivery.smp.data.model.doc.DBResource; import eu.europa.ec.edelivery.smp.data.model.doc.DBSubresource; import eu.europa.ec.edelivery.smp.logging.SMPLogger; import eu.europa.ec.edelivery.smp.logging.SMPLoggerFactory; +import eu.europa.ec.smp.spi.enums.TransientDocumentPropertyType; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; import java.util.Optional; +import static eu.europa.ec.smp.spi.enums.TransientDocumentPropertyType.*; + /** * The class handles the resource action as creating, updating and reading the resources. * @@ -54,9 +60,7 @@ public class ResourceStorage { public byte[] getDocumentContentForResource(DBResource dbResource) { LOG.debug("getDocumentContentForResource: [{}]", dbResource); - Optional<DBDocumentVersion> documentVersion = this.documentDao.getCurrentDocumentVersionForResource(dbResource); - return documentVersion.isPresent() ? documentVersion.get().getContent() : null; } @@ -68,6 +72,72 @@ public class ResourceStorage { } + @Transactional + public Map<String, Object> getResourceProperties(DBResource resource) { + + DBDocument document = documentDao.getDocumentForResource(resource).orElseGet(null); + if (document == null) { + LOG.debug("Document not found for resource [{}]", resource); + return Collections.emptyMap(); + } + + Map<String, Object> documentProperties = new HashMap<>(); + documentProperties.put(TransientDocumentPropertyType.RESOURCE_IDENTIFIER_VALUE.getPropertyName(), resource.getIdentifierValue()); + if (resource.getIdentifierScheme() != null) { + documentProperties.put(TransientDocumentPropertyType.RESOURCE_IDENTIFIER_SCHEME.getPropertyName(), resource.getIdentifierScheme()); + } + // add document properties + documentProperties.putAll(getDocumentProperties(document)); + return documentProperties; + } + + /** + * Method returns the 'transient' and custom document properties as map. If + * document is null, empty map is returned. + * + * @param document document + * @return document properties the key, value map + */ + private Map<String, Object> getDocumentProperties(DBDocument document) { + if (document == null) { + return Collections.emptyMap(); + } + Map<String, Object> documentProperties = new HashMap<>(); + documentProperties.put(DOCUMENT_NAME.getPropertyName(), document.getName()); + documentProperties.put(DOCUMENT_MIMETYPE.getPropertyName(), document.getMimeType()); + documentProperties.put(DOCUMENT_VERSION.getPropertyName(), document.getCurrentVersion()); + + document.getDocumentProperties().forEach(property -> { + documentProperties.put(property.getProperty(), property.getValue()); + }); + return documentProperties; + } + + @Transactional + public Map<String, Object> getSubresourceProperties(DBResource resource, DBSubresource subresource) { + + Map<String, Object> documentProperties = new HashMap<>(); + DBDocument document = documentDao.getDocumentForSubresource(subresource).orElseGet(null); + if (document == null) { + LOG.debug("Document not found for subresource [{}]", resource); + return Collections.emptyMap(); + } + // add identifiers + documentProperties.put(TransientDocumentPropertyType.RESOURCE_IDENTIFIER_VALUE.getPropertyName(), resource.getIdentifierValue()); + if (resource.getIdentifierScheme() != null) { + documentProperties.put(TransientDocumentPropertyType.RESOURCE_IDENTIFIER_SCHEME.getPropertyName(), resource.getIdentifierScheme()); + } + documentProperties.put(SUBRESOURCE_IDENTIFIER_VALUE.getPropertyName(), subresource.getIdentifierValue()); + if (subresource.getIdentifierScheme() != null) { + documentProperties.put(SUBRESOURCE_IDENTIFIER_SCHEME.getPropertyName(), subresource.getIdentifierScheme()); + } + + // add document properties + documentProperties.putAll(getDocumentProperties(document)); + return documentProperties; + } + + @Transactional public DBResource addDocumentVersionForResource(DBResource resource, DBDocumentVersion version) { LOG.debug("addDocumentVersionForResource: [{}]", resource); diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIDocumentService.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIDocumentService.java index efe5fb109fd401cc14db8d6d352187a855cebe80..fe70089bf84bc5c88b29143e2ec0e7f47ea43354 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIDocumentService.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIDocumentService.java @@ -8,9 +8,9 @@ * versions of the EUPL (the "Licence"); * You may not use this work except in compliance with the Licence. * You may obtain a copy of the Licence at: - * + * * [PROJECT_HOME]\license\eupl-1.2\license.txt or https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the Licence is * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the Licence for the specific language governing permissions and limitations under the Licence. @@ -18,16 +18,16 @@ */ package eu.europa.ec.edelivery.smp.services.ui; +import eu.europa.ec.edelivery.smp.config.enums.SMPPropertyTypeEnum; import eu.europa.ec.edelivery.smp.data.dao.DocumentDao; import eu.europa.ec.edelivery.smp.data.dao.ResourceDao; import eu.europa.ec.edelivery.smp.data.dao.SubresourceDao; import eu.europa.ec.edelivery.smp.data.model.DBDomainResourceDef; -import eu.europa.ec.edelivery.smp.data.model.doc.DBDocument; -import eu.europa.ec.edelivery.smp.data.model.doc.DBDocumentVersion; -import eu.europa.ec.edelivery.smp.data.model.doc.DBResource; -import eu.europa.ec.edelivery.smp.data.model.doc.DBSubresource; +import eu.europa.ec.edelivery.smp.data.model.doc.*; import eu.europa.ec.edelivery.smp.data.model.ext.DBSubresourceDef; -import eu.europa.ec.edelivery.smp.data.ui.DocumentRo; +import eu.europa.ec.edelivery.smp.data.ui.DocumentPropertyRO; +import eu.europa.ec.edelivery.smp.data.ui.DocumentRO; +import eu.europa.ec.edelivery.smp.data.ui.enums.EntityROStatus; import eu.europa.ec.edelivery.smp.exceptions.ErrorCode; import eu.europa.ec.edelivery.smp.exceptions.SMPRuntimeException; import eu.europa.ec.edelivery.smp.logging.SMPLogger; @@ -36,6 +36,7 @@ import eu.europa.ec.edelivery.smp.services.resource.ResourceHandlerService; import eu.europa.ec.edelivery.smp.services.spi.data.SpiResponseData; import eu.europa.ec.smp.spi.api.model.RequestData; import eu.europa.ec.smp.spi.api.model.ResponseData; +import eu.europa.ec.smp.spi.enums.TransientDocumentPropertyType; import eu.europa.ec.smp.spi.exceptions.ResourceException; import eu.europa.ec.smp.spi.resource.ResourceHandlerSpi; import org.apache.commons.lang3.exception.ExceptionUtils; @@ -44,10 +45,15 @@ import org.springframework.transaction.annotation.Transactional; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; +import java.util.ArrayList; import java.util.Collections; +import java.util.List; + +import static eu.europa.ec.smp.spi.enums.TransientDocumentPropertyType.*; @Service public class UIDocumentService { + private static final SMPLogger LOG = SMPLoggerFactory.getLogger(UIDocumentService.class); ResourceDao resourceDao; SubresourceDao subresourceDao; @@ -62,7 +68,7 @@ public class UIDocumentService { } @Transactional - public void validateDocumentForResource(Long resourceId, DocumentRo documentRo) { + public void validateDocumentForResource(Long resourceId, DocumentRO documentRo) { DBResource resource = resourceDao.find(resourceId); DBDomainResourceDef domainResourceDef = resource.getDomainResourceDef(); ResourceHandlerSpi resourceHandler = resourceHandlerService.getResourceHandler(domainResourceDef.getResourceDef()); @@ -75,7 +81,7 @@ public class UIDocumentService { } @Transactional - public void validateDocumentForSubresource(Long subresourceId, Long resourceId, DocumentRo documentRo) { + public void validateDocumentForSubresource(Long subresourceId, Long resourceId, DocumentRO documentRo) { DBSubresource entity = subresourceDao.find(subresourceId); DBResource parentEntity = resourceDao.find(resourceId); ResourceHandlerSpi resourceHandler = resourceHandlerService.getSubresourceHandler(entity.getSubresourceDef(), entity.getSubresourceDef().getResourceDef()); @@ -88,20 +94,20 @@ public class UIDocumentService { } @Transactional - public DocumentRo generateDocumentForResource(Long resourceId, DocumentRo documentRo) { - LOG.info("Generate document"); + public DocumentRO generateDocumentForResource(Long resourceId, DocumentRO documentRo) { + LOG.info("generate Document For Resource"); DBResource resource = resourceDao.find(resourceId); DBDomainResourceDef domainResourceDef = resource.getDomainResourceDef(); ResourceHandlerSpi resourceHandler = resourceHandlerService.getResourceHandler(domainResourceDef.getResourceDef()); RequestData data = resourceHandlerService.buildRequestDataForResource(domainResourceDef.getDomain(), resource, null); - return getDocumentRo(resourceHandler, data); + return generateDocumentWithHandler(resourceHandler, data); } @Transactional - public DocumentRo generateDocumentForSubresource(Long subresourceId, Long resourceId, DocumentRo documentRo) { - LOG.info("Generate document"); + public DocumentRO generateDocumentForSubresource(Long subresourceId, Long resourceId, DocumentRO documentRo) { + LOG.info("generate Document For Subresource"); DBResource parentEntity = resourceDao.find(resourceId); DBSubresource entity = subresourceDao.find(subresourceId); DBSubresourceDef subresourceDef = entity.getSubresourceDef(); @@ -110,10 +116,17 @@ public class UIDocumentService { RequestData data = resourceHandlerService.buildRequestDataForSubResource(parentEntity.getDomainResourceDef().getDomain(), parentEntity, entity, null); - return getDocumentRo(resourceHandler, data); + return generateDocumentWithHandler(resourceHandler, data); } - private DocumentRo getDocumentRo(ResourceHandlerSpi resourceHandler, RequestData data) { + /** + * Generate document with ResourceHandlerSpi. Method invokes the given handler to generate the document. + * + * @param resourceHandler handler to generate document + * @param data request data + * @return DocumentRo with payload + */ + private DocumentRO generateDocumentWithHandler(ResourceHandlerSpi resourceHandler, RequestData data) { ByteArrayOutputStream bos = new ByteArrayOutputStream(); ResponseData responseData = new SpiResponseData(bos); try { @@ -122,33 +135,153 @@ public class UIDocumentService { throw new SMPRuntimeException(ErrorCode.INVALID_REQUEST, "StoreResourceValidation", ExceptionUtils.getRootCauseMessage(e)); } String genDoc = bos.toString(); - LOG.info("Generate document [{}]", genDoc); - DocumentRo result = new DocumentRo(); + DocumentRO result = new DocumentRO(); result.setPayload(genDoc); return result; } @Transactional - public DocumentRo saveDocumentForResource(Long resourceId, DocumentRo documentRo) { + public DocumentRO saveDocumentForResource(Long resourceId, DocumentRO documentRo) { - DBResource resource = resourceDao.find(resourceId); + final DBResource resource = resourceDao.find(resourceId); + final DBDocument document = resource.getDocument(); + + boolean isPayloadChanged = documentRo.getPayloadStatus() != EntityROStatus.PERSISTED.getStatusNumber(); + if (isPayloadChanged) { + LOG.debug("Store resource payload for resource [{}]", resource.getIdentifierValue()); + storeResourcePayload(resource, document, documentRo); + } + + if (isDocumentPropertiesChanged(documentRo)) { + // persist non-transient properties + documentRo.getProperties().stream().filter(p -> + TransientDocumentPropertyType.fromPropertyName(p.getProperty()) == null) + .forEach(p -> persistDocumentProperty(p, document)); + } + + return convertWithVersion(document, document.getCurrentVersion(), getInitialProperties(resource)); + } + + + /** + * Method persists the document property. If property has status DELETED removes the property from database + * if property is UPDATED, the existing property entity is updated and if property is new then new property is + * created. + * + * @param documentPropertyRO Document Property RO to persist + * @DBDocument db document to which the property belongs + */ + private void persistDocumentProperty(DocumentPropertyRO documentPropertyRO, DBDocument dbDocument) { + + EntityROStatus status = EntityROStatus.fromStatusNumber(documentPropertyRO.getStatus()); + status = status == null ? EntityROStatus.PERSISTED : status; + + + DBDocumentProperty dbDocumentProperty = dbDocument.getDocumentProperties().stream() + .filter(p -> p.getProperty().equals(documentPropertyRO.getProperty())) + .findFirst().orElse(null); + + switch (status) { + case REMOVED: + if (dbDocumentProperty != null) { + dbDocument.getDocumentProperties().remove(dbDocumentProperty); + } else { + LOG.warn("Document property [{}] not found for document [{}]", documentPropertyRO.getProperty(), dbDocument.getId()); + } + break; + case UPDATED: + if (dbDocumentProperty != null) { + dbDocumentProperty.setDescription(documentPropertyRO.getDesc()); + dbDocumentProperty.setValue(documentPropertyRO.getValue()); + } else { + LOG.warn("Document property [{}] not found for document [{}]. property is added", documentPropertyRO.getProperty(), dbDocument.getId()); + addDocumentProperty(documentPropertyRO, dbDocument); + } + break; + case NEW: + if (dbDocumentProperty == null) { + addDocumentProperty(documentPropertyRO, dbDocument); + } else { + LOG.warn("Document property [{}] already exists for document [{}]. property is updated", + documentPropertyRO.getProperty(), dbDocument.getId()); + dbDocumentProperty.setDescription(documentPropertyRO.getDesc()); + dbDocumentProperty.setValue(documentPropertyRO.getValue()); + } + break; + default: + LOG.warn("Document property [{}] status [{}] indicates no change!", + documentPropertyRO.getProperty(), status); + } + } + + /** + * Method adds new Document Property to the DBDocument property list + * + * @param documentPropertyRO Document Property RO to persist + * @param dbDocument db document to which the property belongs + */ + private void addDocumentProperty(DocumentPropertyRO documentPropertyRO, DBDocument dbDocument) { + DBDocumentProperty dbDocumentProperty = new DBDocumentProperty(); + dbDocumentProperty.setDocument(dbDocument); + dbDocumentProperty.setProperty(documentPropertyRO.getProperty()); + dbDocumentProperty.setValue(documentPropertyRO.getValue()); + dbDocumentProperty.setDescription(documentPropertyRO.getDesc()); + dbDocumentProperty.setType(documentPropertyRO.getType()); + + dbDocument.getDocumentProperties().add(dbDocumentProperty); + } + + /** + * Method validates if any "non-transient" of the document properties are changed. If the document does not have + * any properties or all exiting properties return status PERSISTED it returns false otherwise true. + * + * @param documentRo document to validate + * @retun true if any of the properties changed otherwise false + */ + private boolean isDocumentPropertiesChanged(DocumentRO documentRo) { + return documentRo != null && documentRo.getProperties() != null && documentRo.getProperties().stream() + .filter(p -> TransientDocumentPropertyType.fromPropertyName(p.getProperty()) == null) + .anyMatch(p -> p.getStatus() != EntityROStatus.PERSISTED.getStatusNumber()); + } + + /** + * Method stores the payload for the given resource as a new payload version. + * The method invokes the ResourceHandlerSpi to update/validate the payload before storing it to database. + * + * @param resource resource to store the payload + * @param document the resource database document entity + * @param documentRo document RO the with new payload + */ + private void storeResourcePayload(DBResource resource, DBDocument document, DocumentRO documentRo) { DBDomainResourceDef domainResourceDef = resource.getDomainResourceDef(); - ResourceHandlerSpi resourceHandler = resourceHandlerService.getResourceHandler(domainResourceDef.getResourceDef()); - RequestData data = resourceHandlerService.buildRequestDataForResource(domainResourceDef.getDomain(), resource, new ByteArrayInputStream(documentRo.getPayload().getBytes())); - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - ResponseData responseData = new SpiResponseData(bos); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + // invoke the resource handler for the document type + ResourceHandlerSpi resourceHandler = resourceHandlerService.getResourceHandler( + domainResourceDef.getResourceDef()); + RequestData data = resourceHandlerService.buildRequestDataForResource(domainResourceDef.getDomain(), + resource, new ByteArrayInputStream(documentRo.getPayload().getBytes())); + ResponseData responseData = new SpiResponseData(baos); try { resourceHandler.storeResource(data, responseData); } catch (ResourceException e) { throw new SMPRuntimeException(ErrorCode.INVALID_REQUEST, "StoreResourceValidation", ExceptionUtils.getRootCauseMessage(e)); } + // create new version to document + int version = document.getDocumentVersions().stream().mapToInt(DBDocumentVersion::getVersion) + .max().orElse(0); - DBDocument document = resource.getDocument(); - return createNewVersionAndConvert(document, bos); + DBDocumentVersion documentVersion = new DBDocumentVersion(); + documentVersion.setVersion(version + 1); + documentVersion.setDocument(document); + documentVersion.setContent(baos.toByteArray()); + // to get the current persist time + documentVersion.prePersist(); + document.getDocumentVersions().add(documentVersion); + document.setCurrentVersion(documentVersion.getVersion()); } @Transactional - public DocumentRo saveSubresourceDocumentForResource(Long subresource, Long resourceId, DocumentRo documentRo) { + public DocumentRO saveSubresourceDocumentForResource(Long subresource, Long resourceId, DocumentRO documentRo) { DBResource parentResource = resourceDao.find(resourceId); DBSubresource entity = subresourceDao.find(subresource); @@ -166,7 +299,7 @@ public class UIDocumentService { } DBDocument document = entity.getDocument(); - return createNewVersionAndConvert(document, bos); + return createNewVersionAndConvert(document, bos, getInitialProperties(entity)); } /** @@ -178,19 +311,40 @@ public class UIDocumentService { * @return DocumentRo with payload and version */ @Transactional - public DocumentRo getDocumentForResource(Long resourceId, int version) { + public DocumentRO getDocumentForResource(Long resourceId, int version) { DBResource resource = resourceDao.find(resourceId); DBDocument document = resource.getDocument(); - return convertWithVersion(document, version); + + + return convertWithVersion(document, version, getInitialProperties(resource)); } @Transactional - public DocumentRo getDocumentForSubResource(Long subresourceId, Long resourceId, int version) { + public DocumentRO getDocumentForSubResource(Long subresourceId, Long resourceId, int version) { DBSubresource subresource = subresourceDao.find(subresourceId); DBDocument document = subresource.getDocument(); - return convertWithVersion(document, version); + return convertWithVersion(document, version, getInitialProperties(subresource)); + } + + private List<DocumentPropertyRO> getInitialProperties(DBResource resource) { + List<DocumentPropertyRO> propertyROS = new ArrayList<>(); + propertyROS.add(new DocumentPropertyRO(RESOURCE_IDENTIFIER_VALUE.getPropertyName(), + resource.getIdentifierValue(), RESOURCE_IDENTIFIER_VALUE.getPropertyDescription(), true)); + propertyROS.add(new DocumentPropertyRO(RESOURCE_IDENTIFIER_SCHEME.getPropertyName(), + resource.getIdentifierScheme(), RESOURCE_IDENTIFIER_SCHEME.getPropertyDescription(), true)); + return propertyROS; } + private List<DocumentPropertyRO> getInitialProperties(DBSubresource subresource) { + List<DocumentPropertyRO> propertyROS = getInitialProperties(subresource.getResource()); + propertyROS.add(new DocumentPropertyRO(SUBRESOURCE_IDENTIFIER_VALUE.getPropertyName(), + subresource.getIdentifierValue(), SUBRESOURCE_IDENTIFIER_VALUE.getPropertyDescription(), true)); + propertyROS.add(new DocumentPropertyRO(SUBRESOURCE_IDENTIFIER_SCHEME.getPropertyName(), + subresource.getIdentifierScheme(), SUBRESOURCE_IDENTIFIER_SCHEME.getPropertyDescription(), true)); + return propertyROS; + } + + /** * return Create new Document version and convert DBDocument to DocumentRo * @@ -198,7 +352,7 @@ public class UIDocumentService { * @param baos to write document content * @return DocumentRo */ - private DocumentRo createNewVersionAndConvert(DBDocument document, ByteArrayOutputStream baos) { + private DocumentRO createNewVersionAndConvert(DBDocument document, ByteArrayOutputStream baos, List<DocumentPropertyRO> initialProperties) { // get max version int version = document.getDocumentVersions().stream().mapToInt(DBDocumentVersion::getVersion) @@ -213,10 +367,10 @@ public class UIDocumentService { document.getDocumentVersions().add(documentVersion); document.setCurrentVersion(documentVersion.getVersion()); - return convert(document, documentVersion); + return convert(document, documentVersion, initialProperties); } - public DocumentRo convertWithVersion(DBDocument document, int version) { + public DocumentRO convertWithVersion(DBDocument document, int version, List<DocumentPropertyRO> initialProperties) { DBDocumentVersion currentVersion = null; DBDocumentVersion documentVersion = null; for (DBDocumentVersion dv : document.getDocumentVersions()) { @@ -231,11 +385,18 @@ public class UIDocumentService { if (documentVersion == null && !document.getDocumentVersions().isEmpty()) { documentVersion = document.getDocumentVersions().get(document.getDocumentVersions().size() - 1); } - return convert(document, documentVersion); + return convert(document, documentVersion, initialProperties); } - public DocumentRo convert(DBDocument document, DBDocumentVersion version) { - DocumentRo documentRo = new DocumentRo(); + public DocumentRO convert(DBDocument document, DBDocumentVersion version, List<DocumentPropertyRO> initialProperties) { + DocumentRO documentRo = new DocumentRO(); + documentRo.addProperty(DOCUMENT_NAME.getPropertyName(), + document.getName(), "Document Name", SMPPropertyTypeEnum.STRING, true); + documentRo.addProperty(DOCUMENT_MIMETYPE.getPropertyName(), + document.getMimeType(), "Document Mimetype", SMPPropertyTypeEnum.STRING, true); + + documentRo.getProperties().addAll(initialProperties); + // set list of versions document.getDocumentVersions().forEach(dv -> documentRo.getAllVersions().add(dv.getVersion())); @@ -243,11 +404,22 @@ public class UIDocumentService { documentRo.setMimeType(document.getMimeType()); documentRo.setName(document.getName()); documentRo.setCurrentResourceVersion(document.getCurrentVersion()); + document.getDocumentProperties().stream() + .forEach(p -> { + documentRo.addProperty(p.getProperty(), + p.getValue(), + p.getDescription(), + p.getType(), false); + LOG.info("Document property [{}] added to document [{}]", p); + }); + + if (version != null) { documentRo.setPayloadCreatedOn(version.getCreatedOn()); documentRo.setPayloadVersion(version.getVersion()); documentRo.setPayload(new String(version.getContent())); } + return documentRo; } } diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIDomainAdminService.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIDomainAdminService.java index 5b515363ceaff481ab4f63391d79d1399aac91b1..cbe6ffc578069cd1e58ffcf2bb295fb9ef08607f 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIDomainAdminService.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIDomainAdminService.java @@ -18,11 +18,9 @@ */ package eu.europa.ec.edelivery.smp.services.ui; -import eu.europa.ec.edelivery.smp.config.enums.SMPDomainPropertyEnum; import eu.europa.ec.edelivery.smp.data.dao.*; import eu.europa.ec.edelivery.smp.data.enums.VisibilityType; import eu.europa.ec.edelivery.smp.data.model.DBDomain; -import eu.europa.ec.edelivery.smp.data.model.DBDomainConfiguration; import eu.europa.ec.edelivery.smp.data.model.DBDomainResourceDef; import eu.europa.ec.edelivery.smp.data.model.DBGroup; import eu.europa.ec.edelivery.smp.data.model.ext.DBResourceDef; @@ -31,6 +29,7 @@ import eu.europa.ec.edelivery.smp.data.model.user.DBGroupMember; import eu.europa.ec.edelivery.smp.data.ui.DomainPropertyRO; import eu.europa.ec.edelivery.smp.data.ui.DomainRO; import eu.europa.ec.edelivery.smp.data.ui.ServiceResult; +import eu.europa.ec.edelivery.smp.data.ui.auth.SMPRole; import eu.europa.ec.edelivery.smp.data.ui.enums.EntityROStatus; import eu.europa.ec.edelivery.smp.exceptions.BadRequestException; import eu.europa.ec.edelivery.smp.exceptions.ErrorBusinessCode; @@ -45,7 +44,6 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.*; -import java.util.function.Function; import java.util.stream.Collectors; /** @@ -61,6 +59,7 @@ public class UIDomainAdminService extends UIServiceBase<DBDomain, DomainRO> { private static final SMPLogger LOG = SMPLoggerFactory.getLogger(UIDomainAdminService.class); private final DomainDao domainDao; + private final DomainConfigurationDao domainConfigurationDao; private final DomainMemberDao domainMemberDao; private final ResourceDao resourceDao; private final ResourceDefDao resourceDefDao; @@ -72,6 +71,7 @@ public class UIDomainAdminService extends UIServiceBase<DBDomain, DomainRO> { public UIDomainAdminService(ConversionService conversionService, DomainDao domainDao, + DomainConfigurationDao domainConfigurationDao, DomainMemberDao domainMemberDao, ResourceDao resourceDao, ResourceDefDao resourceDefDao, @@ -81,6 +81,7 @@ public class UIDomainAdminService extends UIServiceBase<DBDomain, DomainRO> { SMLIntegrationService smlIntegrationService) { this.conversionService = conversionService; this.domainDao = domainDao; + this.domainConfigurationDao = domainConfigurationDao; this.resourceDao = resourceDao; this.resourceDefDao = resourceDefDao; this.domainResourceDefDao = domainResourceDefDao; @@ -240,18 +241,9 @@ public class UIDomainAdminService extends UIServiceBase<DBDomain, DomainRO> { if (domain == null) { throw new BadRequestException(ErrorBusinessCode.NOT_FOUND, "Domain does not exist in database!"); } - List<DBDomainConfiguration> domainConfiguration = domainDao.getDomainConfiguration(domain); - - Map<String, DomainPropertyRO> dbList = domainConfiguration.stream() - .map(dc -> conversionService.convert(dc, DomainPropertyRO.class)) - .collect(Collectors.toMap(DomainPropertyRO::getProperty, dp -> dp)); - - return Arrays.stream(SMPDomainPropertyEnum.values()).map(enumType -> { - if (dbList.containsKey(enumType.getProperty())) { - return dbList.get(enumType.getProperty()); - } - return conversionService.convert(enumType, DomainPropertyRO.class); - }).filter(Objects::nonNull).collect(Collectors.toList()); + return domainConfigurationDao.getDomainPropertiesForRole(domain, SMPRole.SYSTEM_ADMIN).stream() + .map(domainConfiguration -> conversionService.convert(domainConfiguration, DomainPropertyRO.class)) + .collect(Collectors.toList()); } @Transactional @@ -260,38 +252,9 @@ public class UIDomainAdminService extends UIServiceBase<DBDomain, DomainRO> { if (domain == null) { throw new BadRequestException(ErrorBusinessCode.NOT_FOUND, "Domain does not exist in database!"); } - // get current domain configuration - Map<String, DBDomainConfiguration> currentDomainConfiguration = domainDao.getDomainConfiguration(domain) - .stream().collect(Collectors.toMap(DBDomainConfiguration::getProperty, Function.identity())); - Map<String, DomainPropertyRO> newDomainPropertyValues = - domainProperties.stream().collect(Collectors.toMap(DomainPropertyRO::getProperty, dp -> dp)); - - List<DBDomainConfiguration> listOfDomainConfiguration = new ArrayList<>(); - - // database domain configuration property list must match SMPDomainPropertyEnum - for (SMPDomainPropertyEnum domainProp : SMPDomainPropertyEnum.values()) { - DBDomainConfiguration domainConfiguration = currentDomainConfiguration.get(domainProp.getProperty()); - DomainPropertyRO domainPropertyRO = newDomainPropertyValues.get(domainProp.getProperty()); - // if property already exists in the database, update value - DBDomainConfiguration updatedDomainProp = domainDao.updateDomainProperty(domain, domainProp, - domainConfiguration, domainPropertyRO); - listOfDomainConfiguration.add(updatedDomainProp); - // remove updated property from the map - currentDomainConfiguration.remove(domainProp.getProperty()); - LOG.debug("Updated domain property [{}]: [{}] for domain [{}]", - domainProp.getProperty(), updatedDomainProp, domain.getDomainCode()); - - } - // remove properties that are not in the new list - currentDomainConfiguration.values().forEach(domainConfiguration -> { - domainDao.removeConfiguration(domainConfiguration); - LOG.debug("Removed domain property [{}]: [{}] for domain [{}]", - domainConfiguration.getProperty(), domainConfiguration.getValue(), - domain.getDomainCode()); - }); - - // up - return listOfDomainConfiguration.stream().map(dc -> conversionService.convert(dc, DomainPropertyRO.class)) + return domainConfigurationDao.updateDomainPropertiesForRole(domain, domainProperties, SMPRole.SYSTEM_ADMIN) + .stream() + .map(domainConfiguration -> conversionService.convert(domainConfiguration, DomainPropertyRO.class)) .collect(Collectors.toList()); } @@ -339,7 +302,7 @@ public class UIDomainAdminService extends UIServiceBase<DBDomain, DomainRO> { // finally remove the domain domainDao.remove(domain); DomainRO domainRO = conversionService.convert(domain, DomainRO.class); - domainRO.setStatus(EntityROStatus.REMOVE.getStatusNumber()); + domainRO.setStatus(EntityROStatus.REMOVED.getStatusNumber()); return domainRO; } @@ -350,6 +313,4 @@ public class UIDomainAdminService extends UIServiceBase<DBDomain, DomainRO> { } groupDao.remove(group); } - - } diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIDomainEditService.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIDomainEditService.java index c33165fb5e48a51ab6da9fcd4d87d1c5738ee4c6..d2bfbec09cf3adefdc3e4b45512029ed0409f41e 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIDomainEditService.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIDomainEditService.java @@ -8,9 +8,9 @@ * versions of the EUPL (the "Licence"); * You may not use this work except in compliance with the Licence. * You may obtain a copy of the Licence at: - * + * * [PROJECT_HOME]\license\eupl-1.2\license.txt or https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the Licence is * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the Licence for the specific language governing permissions and limitations under the Licence. @@ -18,34 +18,26 @@ */ package eu.europa.ec.edelivery.smp.services.ui; -import eu.europa.ec.edelivery.smp.config.SMPEnvironmentProperties; -import eu.europa.ec.edelivery.smp.config.enums.SMPDomainPropertyEnum; -import eu.europa.ec.edelivery.smp.config.enums.SMPEnvPropertyEnum; -import eu.europa.ec.edelivery.smp.data.dao.BaseDao; -import eu.europa.ec.edelivery.smp.data.dao.DomainDao; -import eu.europa.ec.edelivery.smp.data.dao.DomainMemberDao; -import eu.europa.ec.edelivery.smp.data.dao.UserDao; +import eu.europa.ec.edelivery.smp.data.dao.*; import eu.europa.ec.edelivery.smp.data.enums.MembershipRoleType; import eu.europa.ec.edelivery.smp.data.model.DBDomain; -import eu.europa.ec.edelivery.smp.data.model.DBDomainConfiguration; import eu.europa.ec.edelivery.smp.data.model.DBDomainResourceDef; import eu.europa.ec.edelivery.smp.data.model.user.DBDomainMember; import eu.europa.ec.edelivery.smp.data.model.user.DBUser; import eu.europa.ec.edelivery.smp.data.ui.*; +import eu.europa.ec.edelivery.smp.data.ui.auth.SMPRole; import eu.europa.ec.edelivery.smp.exceptions.BadRequestException; import eu.europa.ec.edelivery.smp.exceptions.ErrorBusinessCode; import eu.europa.ec.edelivery.smp.exceptions.ErrorCode; import eu.europa.ec.edelivery.smp.exceptions.SMPRuntimeException; import eu.europa.ec.edelivery.smp.logging.SMPLogger; import eu.europa.ec.edelivery.smp.logging.SMPLoggerFactory; -import eu.europa.ec.edelivery.smp.utils.PropertyUtils; import org.springframework.core.convert.ConversionService; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.io.File; -import java.nio.file.Paths; -import java.util.*; +import java.util.List; +import java.util.Objects; import java.util.stream.Collectors; /** @@ -60,14 +52,17 @@ public class UIDomainEditService extends UIServiceBase<DBDomain, DomainPublicRO> private static final SMPLogger LOG = SMPLoggerFactory.getLogger(UIDomainEditService.class); private final DomainDao domainDao; - + private final DomainConfigurationDao domainConfigurationDao; private final DomainMemberDao domainMemberDao; private final UserDao userDao; private final ConversionService conversionService; - public UIDomainEditService(DomainDao domainDao, DomainMemberDao domainMemberDao, ConversionService conversionService, UserDao userDao) { + public UIDomainEditService(DomainDao domainDao, + DomainConfigurationDao domainConfigurationDao, + DomainMemberDao domainMemberDao, ConversionService conversionService, UserDao userDao) { this.domainDao = domainDao; + this.domainConfigurationDao = domainConfigurationDao; this.domainMemberDao = domainMemberDao; this.conversionService = conversionService; this.userDao = userDao; @@ -81,11 +76,11 @@ public class UIDomainEditService extends UIServiceBase<DBDomain, DomainPublicRO> /** * Method returns Domain resource object list for page. * - * @param page - page number - * @param pageSize - page size + * @param page - page number + * @param pageSize - page size * @param sortField - sort field * @param sortOrder - sort order - * @param filter - filter + * @param filter - filter * @return ServiceResult<DomainPublicRO> - list of domain resource objects */ @Override @@ -187,6 +182,7 @@ public class UIDomainEditService extends UIServiceBase<DBDomain, DomainPublicRO> /** * Method returns all Domain properties which are not tagged as system admin only! + * * @param domainId - domain to get properties * @return list of domain properties */ @@ -195,111 +191,30 @@ public class UIDomainEditService extends UIServiceBase<DBDomain, DomainPublicRO> if (domain == null) { throw new BadRequestException(ErrorBusinessCode.NOT_FOUND, "Domain does not exist in database!"); } - List<DBDomainConfiguration> domainConfiguration = domainDao.getDomainConfiguration(domain); - - Map<String, DomainPropertyRO> dbList = domainConfiguration.stream() - .map(dc -> conversionService.convert(dc, DomainPropertyRO.class)) - .collect(Collectors.toMap(DomainPropertyRO::getProperty, dp -> dp)); - // return only properties that are not system admin only - return Arrays.stream(SMPDomainPropertyEnum.values()) - .filter(SMPDomainPropertyEnum::isNotSystemAdminOnly) - .map(enumType -> { - if (dbList.containsKey(enumType.getProperty())) { - return dbList.get(enumType.getProperty()); - } - return conversionService.convert(enumType, DomainPropertyRO.class); - }).filter(Objects::nonNull).collect(Collectors.toList()); + return domainConfigurationDao.getDomainPropertiesForRole(domain, SMPRole.USER).stream() + .map(property -> conversionService.convert(property, DomainPropertyRO.class)) + .collect(Collectors.toList()); } /** * Method updates domain properties which are not system admin only. - * @param domainId - domain to update properties + * + * @param domainId - domain to update properties * @param domainProperties - list of domain properties * @return list of updated domain properties */ @Transactional - public List<DomainPropertyRO> updateDomainEditProperties(Long domainId, List<DomainPropertyRO> domainProperties) { + public List<DomainPropertyRO> updateDomainEditProperties(Long domainId, List<DomainPropertyRO> domainProperties) { DBDomain domain = domainDao.find(domainId); if (domain == null) { throw new BadRequestException(ErrorBusinessCode.NOT_FOUND, "Domain does not exist in database!"); } - // get current domain configuration - Map<String, DBDomainConfiguration> currentDomainConfiguration = domainDao.getDomainConfiguration(domain) - .stream().collect(Collectors.toMap(DBDomainConfiguration::getProperty, dp -> dp)); - Map<String, DomainPropertyRO> newDomainPropertyValues = - domainProperties.stream().collect(Collectors.toMap(DomainPropertyRO::getProperty, dp -> dp)); - - List<DBDomainConfiguration> listOfDomainConfiguration = new ArrayList<>(); - - // database domain configuration property list must match SMPDomainPropertyEnum - for (SMPDomainPropertyEnum domainProp: SMPDomainPropertyEnum.values()) { - if (domainProp.isSystemAdminOnly()) { - // skip system admin only properties - continue; - } - DBDomainConfiguration domainConfiguration = currentDomainConfiguration.get(domainProp.getProperty()); - DomainPropertyRO domainPropertyRO = newDomainPropertyValues.get(domainProp.getProperty()); - // if property already exists in the database, update value - DBDomainConfiguration updatedDomainProp = domainDao.updateDomainProperty(domain, domainProp, domainConfiguration, domainPropertyRO); - listOfDomainConfiguration.add(updatedDomainProp); - // remove updated property from the map - currentDomainConfiguration.remove(domainProp.getProperty()); - LOG.debug("Updated domain property [{}]: [{}] for domain [{}]", - domainProp.getProperty(), updatedDomainProp, domain.getDomainCode()); - - } - // remove properties that are not in the new list - currentDomainConfiguration.values().forEach(domainConfiguration -> { - domainDao.removeConfiguration(domainConfiguration); - LOG.debug("Removed domain property [{}]: [{}] for domain [{}]", - domainConfiguration.getProperty(), domainConfiguration.getValue(), - domain.getDomainCode()); - }); - - // up - return listOfDomainConfiguration.stream().map(dc -> conversionService.convert(dc, DomainPropertyRO.class)) + return domainConfigurationDao.updateDomainPropertiesForRole(domain, domainProperties, SMPRole.USER).stream() + .map(property -> conversionService.convert(property, DomainPropertyRO.class)) .collect(Collectors.toList()); } - public PropertyValidationRO validateDomainProperty(PropertyRO propertyRO) { - LOG.info("Validate property: [{}]", propertyRO.getProperty()); - PropertyValidationRO propertyValidationRO = new PropertyValidationRO(); - propertyValidationRO.setProperty(propertyRO.getProperty()); - propertyValidationRO.setValue(propertyRO.getValue()); - - Optional<SMPDomainPropertyEnum> optPropertyEnum = SMPDomainPropertyEnum.getByProperty(propertyRO.getProperty()); - if (!optPropertyEnum.isPresent()) { - LOG.debug("Property: [{}] is not Domain SMP property!", propertyRO.getProperty()); - propertyValidationRO.setErrorMessage("Property [" + propertyRO.getProperty() + "] is not SMP property!"); - propertyValidationRO.setPropertyValid(false); - return propertyValidationRO; - } - - SMPDomainPropertyEnum propertyEnum = optPropertyEnum.get(); - return validateDomainPropertyValue(propertyEnum, propertyValidationRO); - } - - /** - * Method validates domain property value for given property enum. - * @param propertyEnum - property enum - * @param propertyValidationRO - property validation object with value. - * @return property validation object with validation result and error message if validation failed. - */ - private PropertyValidationRO validateDomainPropertyValue(SMPDomainPropertyEnum propertyEnum, - PropertyValidationRO propertyValidationRO) { - // try to parse value - try { - File confDir = Paths.get(SMPEnvironmentProperties.getInstance().getEnvPropertyValue(SMPEnvPropertyEnum.SECURITY_FOLDER)).toFile(); - PropertyUtils.parseProperty(propertyEnum.getPropertyEnum(), propertyValidationRO.getValue(), confDir); - } catch (SMPRuntimeException ex) { - propertyValidationRO.setErrorMessage(ex.getMessage()); - propertyValidationRO.setPropertyValid(false); - return propertyValidationRO; - } - - propertyValidationRO.setPropertyValid(true); - return propertyValidationRO; + return domainConfigurationDao.validateDomainProperty(propertyRO); } - } diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIUserService.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIUserService.java index 829d4643346ba6dbcec76cc5313386890c803a9b..d1531230fb4ef668b34741817f371db5e7c03adf 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIUserService.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIUserService.java @@ -462,7 +462,7 @@ public class UIUserService extends UIServiceBase<DBUser, UserRO> { validateCredentials(credential, userId, credentialType, credentialTargetType); credentialDao.remove(credential); CredentialRO credentialRO = conversionService.convert(credential, CredentialRO.class); - credentialRO.setStatus(EntityROStatus.REMOVE.getStatusNumber()); + credentialRO.setStatus(EntityROStatus.REMOVED.getStatusNumber()); return credentialRO; } diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/utils/StringNamedSubstitutor.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/utils/StringNamedSubstitutor.java index 4ec54d11810fda951e2688fba32b28d2a0350834..f2b2c4bdf1ba86c66d95e11bb3a8235ce5b5ede8 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/utils/StringNamedSubstitutor.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/utils/StringNamedSubstitutor.java @@ -51,34 +51,52 @@ public class StringNamedSubstitutor { public static String resolve(InputStream templateIS, Map<String, Object> config) throws IOException { Map<String, Object> lowerCaseMap = config.entrySet().stream() .collect(Collectors.toMap(e -> StringUtils.lowerCase(e.getKey()), Map.Entry::getValue)); - StringBuilder builder = new StringBuilder(); + try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) { + resolve(templateIS, lowerCaseMap, byteArrayOutputStream); + return byteArrayOutputStream.toString(); + } + } + + /** + * Substitute named variables in the string with key value pairs from the map. + * The variables are in the form of ${name} and are case-insensitive and can contain only letters, digits, _ and . + * + * @param templateIS the InputStream to resolve + * @param config the config to use + * @param outputStream the output stream to write the resolved string + * @throws IOException if an I/O error occurs + */ + public static void resolve(InputStream templateIS, Map<String, Object> config, OutputStream outputStream) throws IOException { + Map<String, Object> lowerCaseMap = config.entrySet().stream() + .collect(Collectors.toMap(e -> StringUtils.lowerCase(e.getKey()), Map.Entry::getValue)); + + try (BufferedReader template = new BufferedReader(new InputStreamReader(templateIS))) { + int read; + while ((read = template.read()) != -1) { + if (read != START_NAME.charAt(0) || !isStartSequence(template)) { + outputStream.write((char) read); + continue; + } - BufferedReader template = new BufferedReader(new InputStreamReader(templateIS)); - int read; - while ((read = template.read()) != -1) { - if (read == START_NAME.charAt(0) && isStartSequence(template)) { - //skip (START_NAME.length() - 1L) which is 1L template.skip(1L); String name = readName(template, END_NAME); if (name == null) { - builder.append(START_NAME); + outputStream.write(START_NAME.getBytes()); } else { String key = StringUtils.lowerCase(name); Object objValue = lowerCaseMap.get(key); - String value = objValue!=null? String.valueOf(lowerCaseMap.get(key)): null; + String value = objValue != null ? String.valueOf(lowerCaseMap.get(key)) : null; if (value != null) { - builder.append(value); + outputStream.write(value.getBytes()); } else { - builder.append(START_NAME).append(name).append(END_NAME); + outputStream.write(START_NAME.getBytes()); + outputStream.write(name.getBytes()); + outputStream.write(END_NAME); } } - } else { - builder.append((char) read); } } - - return builder.toString(); } public static boolean isStartSequence(BufferedReader reader) throws IOException { diff --git a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/data/dao/AbstractBaseDao.java b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/data/dao/AbstractBaseDao.java index cb41d0554ec7f56d71ada3b3d5d8cc456e99f462..11fa89df125f84547145884184e2526dfe4ed6c0 100644 --- a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/data/dao/AbstractBaseDao.java +++ b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/data/dao/AbstractBaseDao.java @@ -43,6 +43,7 @@ import static eu.europa.ec.edelivery.smp.config.enums.SMPEnvPropertyEnum.*; ResourceDao.class, SubresourceDao.class, DomainDao.class, + DomainConfigurationDao.class, UserDao.class, CredentialDao.class, ConfigurationDao.class} diff --git a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/data/dao/DocumentDaoTest.java b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/data/dao/DocumentDaoTest.java index d821adb94c9e8cc510907e920cc6ba909b41bc04..66e0beceafb33dbd77aff3deb9b49dcf2039ef14 100644 --- a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/data/dao/DocumentDaoTest.java +++ b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/data/dao/DocumentDaoTest.java @@ -19,11 +19,13 @@ package eu.europa.ec.edelivery.smp.data.dao; import eu.europa.ec.edelivery.smp.data.model.doc.DBDocument; +import eu.europa.ec.edelivery.smp.data.model.doc.DBDocumentProperty; import eu.europa.ec.edelivery.smp.data.model.doc.DBDocumentVersion; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import javax.transaction.Transactional; import java.util.List; import java.util.Optional; @@ -98,4 +100,24 @@ class DocumentDaoTest extends AbstractBaseDao { assertEquals(2, result.get().getVersion()); assertEquals(testUtilsDao.getDocumentD1G1RD1_S1().getDocumentVersions().get(1), result.get()); } + + @Test + @Transactional + void testPersistDocumentProperty() { + // given + DBDocument document = testUtilsDao.createDocument(2, "value1", "schema1"); + DBDocumentProperty property1 = new DBDocumentProperty("property1", "value1", document); + DBDocumentProperty property2 = new DBDocumentProperty("property2", "value1", document); + document.getDocumentProperties().add(property1); + document.getDocumentProperties().add(property2); + // when + testInstance.persistFlushDetach(document); + // then + DBDocument result = testInstance.find(document.getId()); + assertNotNull(result.getId()); + // different object instances + assertNotSame(document, result); + assertEquals(2, result.getDocumentProperties().size()); + assertEquals(property1, result.getDocumentProperties().get(0)); + } } diff --git a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/ui/UIDocumentServiceTest.java b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/ui/UIDocumentServiceTest.java index 052ece3bc77bd5b15dbe1b9fab3d75a5f51e29ec..89425ad0d67b2410c195da88d00ac9f56d17b273 100644 --- a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/ui/UIDocumentServiceTest.java +++ b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/ui/UIDocumentServiceTest.java @@ -21,15 +21,19 @@ package eu.europa.ec.edelivery.smp.services.ui; import eu.europa.ec.edelivery.smp.config.ConversionTestConfig; import eu.europa.ec.edelivery.smp.data.model.doc.DBResource; import eu.europa.ec.edelivery.smp.data.model.doc.DBSubresource; -import eu.europa.ec.edelivery.smp.data.ui.DocumentRo; +import eu.europa.ec.edelivery.smp.data.ui.DocumentPropertyRO; +import eu.europa.ec.edelivery.smp.data.ui.DocumentRO; import eu.europa.ec.edelivery.smp.exceptions.SMPRuntimeException; import eu.europa.ec.edelivery.smp.services.AbstractServiceIntegrationTest; import eu.europa.ec.edelivery.smp.services.resource.ResourceHandlerService; +import eu.europa.ec.edelivery.smp.utils.StringNamedSubstitutor; import eu.europa.ec.smp.spi.def.OasisSMPResource10; import eu.europa.ec.smp.spi.def.OasisSMPSubresource10; +import eu.europa.ec.smp.spi.enums.TransientDocumentPropertyType; import eu.europa.ec.smp.spi.handler.OasisSMPResource10Handler; import eu.europa.ec.smp.spi.handler.OasisSMPSubresource10Handler; import eu.europa.ec.smp.spi.validation.Subresource10Validator; +import org.assertj.core.api.Assertions; import org.hamcrest.CoreMatchers; import org.hamcrest.MatcherAssert; import org.junit.jupiter.api.BeforeEach; @@ -37,6 +41,9 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; +import java.util.Map; +import java.util.stream.Collectors; + import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -48,6 +55,9 @@ public class UIDocumentServiceTest extends AbstractServiceIntegrationTest { @Autowired protected UIDocumentService testInstance; + @Autowired + ResourceHandlerService resourceHandlerService; + @BeforeEach public void prepareDatabase() { // setup initial data! @@ -58,7 +68,7 @@ public class UIDocumentServiceTest extends AbstractServiceIntegrationTest { @Test void testGenerateDocumentForResource() { - DocumentRo result = testInstance.generateDocumentForResource(testUtilsDao.getResourceD1G1RD1().getId(), null); + DocumentRO result = testInstance.generateDocumentForResource(testUtilsDao.getResourceD1G1RD1().getId(), null); assertNotNull(result); assertNotNull(result.getPayload()); } @@ -67,7 +77,7 @@ public class UIDocumentServiceTest extends AbstractServiceIntegrationTest { void testGenerateDocumentForSubResource() { DBSubresource subresource = testUtilsDao.getSubresourceD1G1RD1_S1(); - DocumentRo result = testInstance.generateDocumentForSubresource(subresource.getId(), + DocumentRO result = testInstance.generateDocumentForSubresource(subresource.getId(), subresource.getResource().getId(), null); assertNotNull(result); @@ -77,7 +87,7 @@ public class UIDocumentServiceTest extends AbstractServiceIntegrationTest { @Test void testValidateForResource() { DBResource resource = testUtilsDao.getResourceD1G1RD1(); - DocumentRo testDoc = testInstance.generateDocumentForResource(resource.getId(), null); + DocumentRO testDoc = testInstance.generateDocumentForResource(resource.getId(), null); assertNotNull(testDoc.getPayload()); // must not throw exception testInstance.validateDocumentForResource(resource.getId(), testDoc); @@ -86,11 +96,11 @@ public class UIDocumentServiceTest extends AbstractServiceIntegrationTest { @Test void testValidateForResourceError() { DBResource resource = testUtilsDao.getResourceD1G1RD1(); - DocumentRo testDoc = new DocumentRo(); + DocumentRO testDoc = new DocumentRO(); testDoc.setPayload("test"); SMPRuntimeException result = assertThrows(SMPRuntimeException.class, () -> - testInstance.validateDocumentForResource(resource.getId(), testDoc)); + testInstance.validateDocumentForResource(resource.getId(), testDoc)); MatcherAssert.assertThat(result.getMessage(), CoreMatchers.containsString("Invalid request [ResourceValidation]")); } @@ -99,7 +109,7 @@ public class UIDocumentServiceTest extends AbstractServiceIntegrationTest { @Test void testValidateForSubresource() { DBSubresource subresource = testUtilsDao.getSubresourceD1G1RD1_S1(); - DocumentRo testDoc = testInstance.generateDocumentForSubresource(subresource.getId(), + DocumentRO testDoc = testInstance.generateDocumentForSubresource(subresource.getId(), subresource.getResource().getId(), null); @@ -111,11 +121,11 @@ public class UIDocumentServiceTest extends AbstractServiceIntegrationTest { @Test void testValidateForSubresourceError() { DBSubresource subresource = testUtilsDao.getSubresourceD1G1RD1_S1(); - DocumentRo testDoc = new DocumentRo(); + DocumentRO testDoc = new DocumentRO(); testDoc.setPayload("test"); SMPRuntimeException result = assertThrows(SMPRuntimeException.class, () -> - testInstance.validateDocumentForSubresource(subresource.getId(), subresource.getResource().getId(), testDoc)); + testInstance.validateDocumentForSubresource(subresource.getId(), subresource.getResource().getId(), testDoc)); MatcherAssert.assertThat(result.getMessage(), CoreMatchers.containsString("Invalid request [ResourceValidation]")); } @@ -123,38 +133,69 @@ public class UIDocumentServiceTest extends AbstractServiceIntegrationTest { @Test void testGetDocumentForResource() { DBResource resource = testUtilsDao.getResourceD1G1RD1(); - DocumentRo testDoc = testInstance.getDocumentForResource(resource.getId(), 1); + DocumentRO testDoc = testInstance.getDocumentForResource(resource.getId(), 1); assertNotNull(testDoc.getPayload()); } @Test void testGetDocumentForSubResource() { DBSubresource subresource = testUtilsDao.getSubresourceD1G1RD1_S1(); - DocumentRo testDoc = testInstance.getDocumentForSubResource(subresource.getId(), subresource.getResource().getId(), 1); + DocumentRO testDoc = testInstance.getDocumentForSubResource(subresource.getId(), subresource.getResource().getId(), 1); assertNotNull(testDoc.getPayload()); } @Test void testSaveDocumentForResource() { DBResource resource = testUtilsDao.getResourceD1G1RD1(); - DocumentRo testDoc = testInstance.generateDocumentForResource(resource.getId(), null); + DocumentRO testDoc = testInstance.generateDocumentForResource(resource.getId(), null); assertNotNull(testDoc.getPayload()); //when - DocumentRo result = testInstance.saveDocumentForResource(resource.getId(), testDoc); + DocumentRO result = testInstance.saveDocumentForResource(resource.getId(), testDoc); // then assertNotNull(result); } @Test - void testSaveSubresourceDocumentForResource() { + void testSaveDocumentForSubresource() { DBSubresource subresource = testUtilsDao.getSubresourceD1G1RD1_S1(); - DocumentRo testDoc = testInstance.generateDocumentForSubresource(subresource.getId(), + DocumentRO testDoc = testInstance.generateDocumentForSubresource(subresource.getId(), subresource.getResource().getId(), null); assertNotNull(testDoc.getPayload()); + //when - DocumentRo result = testInstance.saveSubresourceDocumentForResource(subresource.getId(), subresource.getResource().getId(), testDoc); + DocumentRO result = testInstance.saveSubresourceDocumentForResource(subresource.getId(), subresource.getResource().getId(), testDoc); // then assertNotNull(result); } + + @Test + void testTransientResolutionForSubresourceDocument() { + DBSubresource subresource = testUtilsDao.getSubresourceD1G1RD1_S1(); + DocumentRO testDoc = testInstance.generateDocumentForSubresource(subresource.getId(), + subresource.getResource().getId(), + null); + assertNotNull(testDoc.getPayload()); + // extension used by this test is SMP example extension which generates document with placeholders + Assertions.assertThat(testDoc.getPayload()).contains(TransientDocumentPropertyType.RESOURCE_IDENTIFIER_VALUE.getPropertyPlaceholder()); + Assertions.assertThat(testDoc.getPayload()).contains(TransientDocumentPropertyType.RESOURCE_IDENTIFIER_SCHEME.getPropertyPlaceholder()); + Assertions.assertThat(testDoc.getPayload()).contains(TransientDocumentPropertyType.SUBRESOURCE_IDENTIFIER_VALUE.getPropertyPlaceholder()); + Assertions.assertThat(testDoc.getPayload()).contains(TransientDocumentPropertyType.SUBRESOURCE_IDENTIFIER_SCHEME.getPropertyPlaceholder()); + + //when + DocumentRO result = testInstance.saveSubresourceDocumentForResource(subresource.getId(), subresource.getResource().getId(), testDoc); + + Map<String, Object> mapProperties = result.getProperties().stream().collect(Collectors.toMap(DocumentPropertyRO::getProperty, DocumentPropertyRO::getValue)); + String resolved = StringNamedSubstitutor.resolve(result.getPayload(), mapProperties); + // then + Assertions.assertThat(resolved).doesNotContain(TransientDocumentPropertyType.RESOURCE_IDENTIFIER_SCHEME.getPropertyPlaceholder()); + Assertions.assertThat(resolved).doesNotContain(TransientDocumentPropertyType.RESOURCE_IDENTIFIER_VALUE.getPropertyPlaceholder()); + Assertions.assertThat(resolved).doesNotContain(TransientDocumentPropertyType.SUBRESOURCE_IDENTIFIER_VALUE.getPropertyPlaceholder()); + Assertions.assertThat(resolved).doesNotContain(TransientDocumentPropertyType.SUBRESOURCE_IDENTIFIER_SCHEME.getPropertyPlaceholder()); + + Assertions.assertThat(resolved).contains(subresource.getIdentifierValue()); + Assertions.assertThat(resolved).contains(subresource.getIdentifierScheme()); + Assertions.assertThat(resolved).contains(subresource.getResource().getIdentifierValue()); + Assertions.assertThat(resolved).contains(subresource.getResource().getIdentifierScheme()); + } } diff --git a/smp-server-library/src/test/resources/cleanup-database.sql b/smp-server-library/src/test/resources/cleanup-database.sql index 604df6ae9c5863159e7821ccf8bdde37b0aa7f4b..847d25bd63fd55ac7d4b466f84a4861211b80bb2 100755 --- a/smp-server-library/src/test/resources/cleanup-database.sql +++ b/smp-server-library/src/test/resources/cleanup-database.sql @@ -18,6 +18,8 @@ DELETE FROM SMP_SUBRESOURCE; DELETE FROM SMP_SUBRESOURCE_AUD; DELETE FROM SMP_RESOURCE; DELETE FROM SMP_RESOURCE_AUD; +DELETE FROM SMP_DOCUMENT_PROPERTY; +DELETE FROM SMP_DOCUMENT_PROPERTY_AUD; DELETE FROM SMP_DOCUMENT_VERSION; DELETE FROM SMP_DOCUMENT_VERSION_AUD; DELETE FROM SMP_DOCUMENT; diff --git a/smp-spi/src/main/java/eu/europa/ec/smp/spi/ExtensionInfo.java b/smp-spi/src/main/java/eu/europa/ec/smp/spi/ExtensionInfo.java index e5944d82808336f072dfb328c24c3b2b39d941ee..b224501a9fb65447494a618e97098a1de80edaf0 100644 --- a/smp-spi/src/main/java/eu/europa/ec/smp/spi/ExtensionInfo.java +++ b/smp-spi/src/main/java/eu/europa/ec/smp/spi/ExtensionInfo.java @@ -40,5 +40,4 @@ public interface ExtensionInfo { List<ResourceDefinitionSpi> resourceTypes(); List<PayloadValidatorSpi> payloadValidators(); - } diff --git a/smp-spi/src/main/java/eu/europa/ec/smp/spi/enums/TransientDocumentPropertyType.java b/smp-spi/src/main/java/eu/europa/ec/smp/spi/enums/TransientDocumentPropertyType.java new file mode 100644 index 0000000000000000000000000000000000000000..6516501ac2657c4517b67ff9d04185627333e0d4 --- /dev/null +++ b/smp-spi/src/main/java/eu/europa/ec/smp/spi/enums/TransientDocumentPropertyType.java @@ -0,0 +1,52 @@ +package eu.europa.ec.smp.spi.enums; + +import org.apache.commons.lang3.StringUtils; + +import static org.apache.commons.lang3.StringUtils.equalsIgnoreCase; +import static org.apache.commons.lang3.StringUtils.trim; + +/** + * Enum for transient document properties + * + * @author Joze Rihtarsic + * @since 5.1 + */ +public enum TransientDocumentPropertyType { + RESOURCE_IDENTIFIER_VALUE("resource.identifier.value", "Resource Identifier Value"), + RESOURCE_IDENTIFIER_SCHEME("resource.identifier.scheme", "Resource Identifier Scheme"), + SUBRESOURCE_IDENTIFIER_VALUE("subresource.identifier.value", "Subresource Identifier Value"), + SUBRESOURCE_IDENTIFIER_SCHEME("subresource.identifier.scheme", "Subresource Identifier Scheme"), + DOCUMENT_NAME("document.name", "Document Name"), + DOCUMENT_MIMETYPE("document.mimetype", "Document Mimetype"), + DOCUMENT_VERSION("document.version", "Document Version"), + ; + + String propertyName; + String propertyDescription; + + TransientDocumentPropertyType(String propertyName, String propertyDescription) { + this.propertyName = propertyName; + this.propertyDescription = propertyDescription; + } + + public String getPropertyName() { + return propertyName; + } + + public String getPropertyPlaceholder() { + return "${" + propertyName + "}"; + } + + public String getPropertyDescription() { + return propertyDescription; + } + + public static TransientDocumentPropertyType fromPropertyName(String propertyName) { + for (TransientDocumentPropertyType transientDocumentPropertyType : values()) { + if (equalsIgnoreCase(transientDocumentPropertyType.propertyName, trim(propertyName))) { + return transientDocumentPropertyType; + } + } + return null; + } +} diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/edit/DocumentEditController.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/edit/DocumentEditController.java index a156d171fd54cfe08db5918115878f6b96ca6bd5..11d326265f1606c8d61e471d9fa6043f35363465 100644 --- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/edit/DocumentEditController.java +++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/edit/DocumentEditController.java @@ -8,9 +8,9 @@ * versions of the EUPL (the "Licence"); * You may not use this work except in compliance with the Licence. * You may obtain a copy of the Licence at: - * + * * [PROJECT_HOME]\license\eupl-1.2\license.txt or https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the Licence is * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the Licence for the specific language governing permissions and limitations under the Licence. @@ -19,7 +19,7 @@ package eu.europa.ec.edelivery.smp.ui.edit; -import eu.europa.ec.edelivery.smp.data.ui.DocumentRo; +import eu.europa.ec.edelivery.smp.data.ui.DocumentRO; import eu.europa.ec.edelivery.smp.logging.SMPLogger; import eu.europa.ec.edelivery.smp.logging.SMPLoggerFactory; import eu.europa.ec.edelivery.smp.services.ui.UIDocumentService; @@ -54,7 +54,7 @@ public class DocumentEditController { @GetMapping(path = SUB_CONTEXT_PATH_EDIT_DOCUMENT_GET, produces = MimeTypeUtils.APPLICATION_JSON_VALUE) @PreAuthorize("@smpAuthorizationService.isCurrentlyLoggedIn(#userEncId) " + "and @smpAuthorizationService.isResourceAdministrator(#resourceEncId)") - public DocumentRo getDocumentForResource(@PathVariable(PATH_PARAM_ENC_USER_ID) String userEncId, + public DocumentRO getDocumentForResource(@PathVariable(PATH_PARAM_ENC_USER_ID) String userEncId, @PathVariable(PATH_PARAM_ENC_RESOURCE_ID) String resourceEncId, @RequestParam(value = PARAM_NAME_VERSION, defaultValue = "-1") int version) { logAdminAccess("getDocumentForResource"); @@ -65,7 +65,7 @@ public class DocumentEditController { @GetMapping(path = SUB_CONTEXT_PATH_EDIT_DOCUMENT_GET_SUBRESOURCE, produces = MimeTypeUtils.APPLICATION_JSON_VALUE) @PreAuthorize("@smpAuthorizationService.isCurrentlyLoggedIn(#userEncId) " + "and @smpAuthorizationService.isResourceAdministrator(#resourceEncId)") - public DocumentRo getDocumentForSubResource(@PathVariable(PATH_PARAM_ENC_USER_ID) String userEncId, + public DocumentRO getDocumentForSubResource(@PathVariable(PATH_PARAM_ENC_USER_ID) String userEncId, @PathVariable(PATH_PARAM_ENC_RESOURCE_ID) String resourceEncId, @PathVariable(PATH_PARAM_ENC_SUBRESOURCE_ID) String subresourceEncId, @RequestParam(value = PARAM_NAME_VERSION, defaultValue = "-1") int version) { @@ -80,7 +80,7 @@ public class DocumentEditController { "and @smpAuthorizationService.isResourceAdministrator(#resourceEncId)") public void validateDocument(@PathVariable(PATH_PARAM_ENC_USER_ID) String userEncId, @PathVariable(PATH_PARAM_ENC_RESOURCE_ID) String resourceEncId, - @RequestBody DocumentRo document) { + @RequestBody DocumentRO document) { logAdminAccess("validateDocumentForResource"); Long resourceId = SessionSecurityUtils.decryptEntityId(resourceEncId); uiDocumentService.validateDocumentForResource(resourceId, document); @@ -92,7 +92,7 @@ public class DocumentEditController { public void validateSubresourceDocument(@PathVariable(PATH_PARAM_ENC_USER_ID) String userEncId, @PathVariable(PATH_PARAM_ENC_RESOURCE_ID) String resourceEncId, @PathVariable(PATH_PARAM_ENC_SUBRESOURCE_ID) String subresourceEncId, - @RequestBody DocumentRo document) { + @RequestBody DocumentRO document) { logAdminAccess("validateDocumentForResource"); Long resourceId = SessionSecurityUtils.decryptEntityId(resourceEncId); Long subresourceId = SessionSecurityUtils.decryptEntityId(subresourceEncId); @@ -103,9 +103,9 @@ public class DocumentEditController { @PostMapping(path = SUB_CONTEXT_PATH_EDIT_DOCUMENT_GENERATE, consumes = MimeTypeUtils.APPLICATION_JSON_VALUE) @PreAuthorize("@smpAuthorizationService.isCurrentlyLoggedIn(#userEncId) " + "and @smpAuthorizationService.isResourceAdministrator(#resourceEncId)") - public DocumentRo generateDocument(@PathVariable(PATH_PARAM_ENC_USER_ID) String userEncId, + public DocumentRO generateDocument(@PathVariable(PATH_PARAM_ENC_USER_ID) String userEncId, @PathVariable(PATH_PARAM_ENC_RESOURCE_ID) String resourceEncId, - @RequestBody(required = false) DocumentRo document) { + @RequestBody(required = false) DocumentRO document) { logAdminAccess("generateDocument"); Long resourceId = SessionSecurityUtils.decryptEntityId(resourceEncId); return uiDocumentService.generateDocumentForResource(resourceId, document); @@ -114,10 +114,10 @@ public class DocumentEditController { @PostMapping(path = SUB_CONTEXT_PATH_EDIT_DOCUMENT_SUBRESOURCE_GENERATE, consumes = MimeTypeUtils.APPLICATION_JSON_VALUE) @PreAuthorize("@smpAuthorizationService.isCurrentlyLoggedIn(#userEncId) " + "and @smpAuthorizationService.isResourceAdministrator(#resourceEncId)") - public DocumentRo generateSubresourceDocument(@PathVariable(PATH_PARAM_ENC_USER_ID) String userEncId, + public DocumentRO generateSubresourceDocument(@PathVariable(PATH_PARAM_ENC_USER_ID) String userEncId, @PathVariable(PATH_PARAM_ENC_RESOURCE_ID) String resourceEncId, @PathVariable(PATH_PARAM_ENC_SUBRESOURCE_ID) String subresourceEncId, - @RequestBody(required = false) DocumentRo document) { + @RequestBody(required = false) DocumentRO document) { logAdminAccess("generateDocument"); Long resourceId = SessionSecurityUtils.decryptEntityId(resourceEncId); Long subresourceId = SessionSecurityUtils.decryptEntityId(subresourceEncId); @@ -129,9 +129,9 @@ public class DocumentEditController { produces = MimeTypeUtils.APPLICATION_JSON_VALUE) @PreAuthorize("@smpAuthorizationService.isCurrentlyLoggedIn(#userEncId) " + "and @smpAuthorizationService.isResourceAdministrator(#resourceEncId)") - public DocumentRo saveDocument(@PathVariable(PATH_PARAM_ENC_USER_ID) String userEncId, - @PathVariable(PATH_PARAM_ENC_RESOURCE_ID) String resourceEncId, - @RequestBody DocumentRo document) { + public DocumentRO saveDocumentForResource(@PathVariable(PATH_PARAM_ENC_USER_ID) String userEncId, + @PathVariable(PATH_PARAM_ENC_RESOURCE_ID) String resourceEncId, + @RequestBody DocumentRO document) { logAdminAccess("validateDocumentForResource"); Long resourceId = SessionSecurityUtils.decryptEntityId(resourceEncId); return uiDocumentService.saveDocumentForResource(resourceId, document); @@ -142,10 +142,10 @@ public class DocumentEditController { produces = MimeTypeUtils.APPLICATION_JSON_VALUE) @PreAuthorize("@smpAuthorizationService.isCurrentlyLoggedIn(#userEncId) " + "and @smpAuthorizationService.isResourceAdministrator(#resourceEncId)") - public DocumentRo saveSubresourceDocument(@PathVariable(PATH_PARAM_ENC_USER_ID) String userEncId, + public DocumentRO saveSubresourceDocument(@PathVariable(PATH_PARAM_ENC_USER_ID) String userEncId, @PathVariable(PATH_PARAM_ENC_RESOURCE_ID) String resourceEncId, @PathVariable(PATH_PARAM_ENC_SUBRESOURCE_ID) String subresourceEncId, - @RequestBody DocumentRo document) { + @RequestBody DocumentRO document) { logAdminAccess("validateDocumentForResource"); Long resourceId = SessionSecurityUtils.decryptEntityId(resourceEncId); Long subresourceId = SessionSecurityUtils.decryptEntityId(subresourceEncId); diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/internal/KeystoreAdminController.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/internal/KeystoreAdminController.java index 3b8c7a0613d79139c1ebb227f39c4db172b41352..4f1cb8581091cbe18f3f93fefe459e40fb38a450 100644 --- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/internal/KeystoreAdminController.java +++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/internal/KeystoreAdminController.java @@ -130,11 +130,11 @@ public class KeystoreAdminController { if (x509Certificate == null) { String msg = "Certificate Key not removed because alias [" + alias + "] does not exist in keystore!"; LOG.error(msg); - response = creatEmptyResponse(alias, EntityROStatus.REMOVE, msg); + response = creatEmptyResponse(alias, EntityROStatus.REMOVED, msg); } else { response = uiKeystoreService.convertToRo(x509Certificate); response.setAlias(alias); - response.setStatus(EntityROStatus.REMOVE.getStatusNumber()); + response.setStatus(EntityROStatus.REMOVED.getStatusNumber()); } } catch (KeyStoreException | CertificateException | NoSuchAlgorithmException | IOException e) { String msg = e.getClass().getName() + " occurred while reading the keystore: " + e.getMessage(); diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/internal/TruststoreAdminController.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/internal/TruststoreAdminController.java index 88cfc2ad9af636dc8be99ee431496705770ce545..eede16b0ae84705a9af886e46a3482798f6b8090 100644 --- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/internal/TruststoreAdminController.java +++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/internal/TruststoreAdminController.java @@ -120,11 +120,11 @@ public class TruststoreAdminController { if (x509Certificate == null) { String msg = "Certificate not removed because alias [" + alias + "] does not exist in truststore!"; LOG.error(msg); - response = creatEmptyResponse(alias, EntityROStatus.REMOVE, msg); + response = creatEmptyResponse(alias, EntityROStatus.REMOVED, msg); } else { response = uiTruststoreService.convertToRo(x509Certificate); response.setAlias(alias); - response.setStatus(EntityROStatus.REMOVE.getStatusNumber()); + response.setStatus(EntityROStatus.REMOVED.getStatusNumber()); } } catch (KeyStoreException | CertificateException | NoSuchAlgorithmException | IOException e) { String msg = e.getClass().getName() + " occurred while reading the truststore: " + e.getMessage(); diff --git a/smp-webapp/src/main/smp-setup/database-scripts/mysql5innodb-drop.ddl b/smp-webapp/src/main/smp-setup/database-scripts/mysql5innodb-drop.ddl index a2e7b515c84c9ecce823170f3850d15e54a806dc..f33ba8d00756e7f4b4216169ebb739ca0b41400c 100644 --- a/smp-webapp/src/main/smp-setup/database-scripts/mysql5innodb-drop.ddl +++ b/smp-webapp/src/main/smp-setup/database-scripts/mysql5innodb-drop.ddl @@ -39,6 +39,14 @@ drop foreign key FKh9epnme26i271eixtvrpqejvi; + alter table SMP_DOCUMENT_PROPERTY + drop + foreign key FKfag3795e9mrvfvesd00yis9yh; + + alter table SMP_DOCUMENT_PROPERTY_AUD + drop + foreign key FK81057kcrugb1cfm0io5vkxtin; + alter table SMP_DOCUMENT_VERSION drop foreign key FKalsuoqx4csyp9mygvng911do; @@ -195,6 +203,10 @@ drop table if exists SMP_DOCUMENT_AUD; + drop table if exists SMP_DOCUMENT_PROPERTY; + + drop table if exists SMP_DOCUMENT_PROPERTY_AUD; + drop table if exists SMP_DOCUMENT_VERSION; drop table if exists SMP_DOCUMENT_VERSION_AUD; diff --git a/smp-webapp/src/main/smp-setup/database-scripts/mysql5innodb.ddl b/smp-webapp/src/main/smp-setup/database-scripts/mysql5innodb.ddl index bacf57b422f2816958da403b7c6dcdf5b8cb2190..421be51f4488369b97b2d55b72acc73cddd08320 100644 --- a/smp-webapp/src/main/smp-setup/database-scripts/mysql5innodb.ddl +++ b/smp-webapp/src/main/smp-setup/database-scripts/mysql5innodb.ddl @@ -179,6 +179,32 @@ primary key (ID, REV) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; + create table SMP_DOCUMENT_PROPERTY ( + ID bigint not null auto_increment comment 'Unique document property id', + CREATED_ON datetime not null, + LAST_UPDATED_ON datetime not null, + DESCRIPTION varchar(4000) CHARACTER SET utf8 COLLATE utf8_bin comment 'Property description', + PROPERTY_NAME varchar(255) CHARACTER SET utf8 COLLATE utf8_bin, + PROPERTY_TYPE varchar(64) CHARACTER SET utf8 COLLATE utf8_bin, + PROPERTY_VALUE varchar(1024) CHARACTER SET utf8 COLLATE utf8_bin, + FK_DOCUMENT_ID bigint, + primary key (ID) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8; + + create table SMP_DOCUMENT_PROPERTY_AUD ( + ID bigint not null, + REV bigint not null, + REVTYPE tinyint, + CREATED_ON datetime, + LAST_UPDATED_ON datetime, + DESCRIPTION varchar(4000) CHARACTER SET utf8 COLLATE utf8_bin, + PROPERTY_NAME varchar(255) CHARACTER SET utf8 COLLATE utf8_bin, + PROPERTY_TYPE varchar(64) CHARACTER SET utf8 COLLATE utf8_bin, + PROPERTY_VALUE varchar(1024) CHARACTER SET utf8 COLLATE utf8_bin, + FK_DOCUMENT_ID bigint, + primary key (ID, REV) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8; + create table SMP_DOCUMENT_VERSION ( ID bigint not null auto_increment comment 'Unique version document id', CREATED_ON datetime not null, @@ -245,7 +271,7 @@ LAST_UPDATED_ON datetime not null, DESCRIPTION varchar(4000) CHARACTER SET utf8 COLLATE utf8_bin comment 'Property description', PROPERTY_NAME varchar(512) CHARACTER SET utf8 COLLATE utf8_bin not null comment 'Property name/key', - USER_SYSTEM_DEFAULT bit not null comment 'Use system default value', + SYSTEM_DEFAULT bit not null comment 'Use system default value', PROPERTY_VALUE varchar(4000) CHARACTER SET utf8 COLLATE utf8_bin comment 'Property value', FK_DOMAIN_ID bigint not null, primary key (ID) @@ -259,7 +285,7 @@ LAST_UPDATED_ON datetime, DESCRIPTION varchar(4000) CHARACTER SET utf8 COLLATE utf8_bin, PROPERTY_NAME varchar(512) CHARACTER SET utf8 COLLATE utf8_bin, - USER_SYSTEM_DEFAULT bit, + SYSTEM_DEFAULT bit, PROPERTY_VALUE varchar(4000) CHARACTER SET utf8 COLLATE utf8_bin, FK_DOMAIN_ID bigint, primary key (ID, REV) @@ -560,6 +586,9 @@ alter table SMP_CREDENTIAL add constraint SMP_CRD_USER_NAME_TYPE_IDX unique (CREDENTIAL_NAME, CREDENTIAL_TYPE, CREDENTIAL_TARGET); + + alter table SMP_DOCUMENT_PROPERTY + add constraint SMP_DOC_PROP_IDX unique (FK_DOCUMENT_ID, PROPERTY_NAME); create index SMP_DOCVER_DOCUMENT_IDX on SMP_DOCUMENT_VERSION (FK_DOCUMENT_ID); alter table SMP_DOCUMENT_VERSION @@ -665,6 +694,16 @@ create index SMP_SMD_DOC_SCH_IDX on SMP_SUBRESOURCE (IDENTIFIER_SCHEME); foreign key (REV) references SMP_REV_INFO (id); + alter table SMP_DOCUMENT_PROPERTY + add constraint FKfag3795e9mrvfvesd00yis9yh + foreign key (FK_DOCUMENT_ID) + references SMP_DOCUMENT (ID); + + alter table SMP_DOCUMENT_PROPERTY_AUD + add constraint FK81057kcrugb1cfm0io5vkxtin + foreign key (REV) + references SMP_REV_INFO (id); + alter table SMP_DOCUMENT_VERSION add constraint FKalsuoqx4csyp9mygvng911do foreign key (FK_DOCUMENT_ID) diff --git a/smp-webapp/src/main/smp-setup/database-scripts/oracle10g-drop.ddl b/smp-webapp/src/main/smp-setup/database-scripts/oracle10g-drop.ddl index 4ce6d0474574c4a584a69bfd3e4273f03ad163d1..45b4fba095365730d7182e3040ceea70e09f062f 100644 --- a/smp-webapp/src/main/smp-setup/database-scripts/oracle10g-drop.ddl +++ b/smp-webapp/src/main/smp-setup/database-scripts/oracle10g-drop.ddl @@ -27,6 +27,10 @@ drop table SMP_DOCUMENT_AUD cascade constraints; + drop table SMP_DOCUMENT_PROPERTY cascade constraints; + + drop table SMP_DOCUMENT_PROPERTY_AUD cascade constraints; + drop table SMP_DOCUMENT_VERSION cascade constraints; drop table SMP_DOCUMENT_VERSION_AUD cascade constraints; @@ -91,6 +95,8 @@ drop sequence SMP_CREDENTIAL_SEQ; + drop sequence SMP_DOC_PROP_SEQ; + drop sequence SMP_DOCUMENT_SEQ; drop sequence SMP_DOCUMENT_VERSION_SEQ; diff --git a/smp-webapp/src/main/smp-setup/database-scripts/oracle10g.ddl b/smp-webapp/src/main/smp-setup/database-scripts/oracle10g.ddl index 3c861a3e1bade8f88937626e5c6b3d7fc745424b..c3460e7dd0c793b4acd46f763e89fca37b602e59 100644 --- a/smp-webapp/src/main/smp-setup/database-scripts/oracle10g.ddl +++ b/smp-webapp/src/main/smp-setup/database-scripts/oracle10g.ddl @@ -5,6 +5,7 @@ create sequence SMP_ALERT_PROP_SEQ start with 1 increment by 1; create sequence SMP_ALERT_SEQ start with 1 increment by 1; create sequence SMP_CREDENTIAL_SEQ start with 1 increment by 1; +create sequence SMP_DOC_PROP_SEQ start with 1 increment by 1; create sequence SMP_DOCUMENT_SEQ start with 1 increment by 1; create sequence SMP_DOCUMENT_VERSION_SEQ start with 1 increment by 1; create sequence SMP_DOMAIN_CONF_SEQ start with 1 increment by 1; @@ -303,6 +304,38 @@ create sequence SMP_USER_SEQ start with 1 increment by 1; primary key (ID, REV) ); + create table SMP_DOCUMENT_PROPERTY ( + ID number(19,0) not null, + CREATED_ON timestamp not null, + LAST_UPDATED_ON timestamp not null, + DESCRIPTION varchar2(4000 char), + PROPERTY_NAME varchar2(255 char), + PROPERTY_TYPE varchar2(64 char), + PROPERTY_VALUE varchar2(1024 char), + FK_DOCUMENT_ID number(19,0), + primary key (ID) + ); + + comment on column SMP_DOCUMENT_PROPERTY.ID is + 'Unique document property id'; + + comment on column SMP_DOCUMENT_PROPERTY.DESCRIPTION is + 'Property description'; + + create table SMP_DOCUMENT_PROPERTY_AUD ( + ID number(19,0) not null, + REV number(19,0) not null, + REVTYPE number(3,0), + CREATED_ON timestamp, + LAST_UPDATED_ON timestamp, + DESCRIPTION varchar2(4000 char), + PROPERTY_NAME varchar2(255 char), + PROPERTY_TYPE varchar2(64 char), + PROPERTY_VALUE varchar2(1024 char), + FK_DOCUMENT_ID number(19,0), + primary key (ID, REV) + ); + create table SMP_DOCUMENT_VERSION ( ID number(19,0) not null, CREATED_ON timestamp not null, @@ -417,7 +450,7 @@ create sequence SMP_USER_SEQ start with 1 increment by 1; LAST_UPDATED_ON timestamp not null, DESCRIPTION varchar2(4000 char), PROPERTY_NAME varchar2(512 char) not null, - USER_SYSTEM_DEFAULT number(1,0) not null, + SYSTEM_DEFAULT number(1,0) not null, PROPERTY_VALUE varchar2(4000 char), FK_DOMAIN_ID number(19,0) not null, primary key (ID) @@ -435,7 +468,7 @@ create sequence SMP_USER_SEQ start with 1 increment by 1; comment on column SMP_DOMAIN_CONFIGURATION.PROPERTY_NAME is 'Property name/key'; - comment on column SMP_DOMAIN_CONFIGURATION.USER_SYSTEM_DEFAULT is + comment on column SMP_DOMAIN_CONFIGURATION.SYSTEM_DEFAULT is 'Use system default value'; comment on column SMP_DOMAIN_CONFIGURATION.PROPERTY_VALUE is @@ -449,7 +482,7 @@ create sequence SMP_USER_SEQ start with 1 increment by 1; LAST_UPDATED_ON timestamp, DESCRIPTION varchar2(4000 char), PROPERTY_NAME varchar2(512 char), - USER_SYSTEM_DEFAULT number(1,0), + SYSTEM_DEFAULT number(1,0), PROPERTY_VALUE varchar2(4000 char), FK_DOMAIN_ID number(19,0), primary key (ID, REV) @@ -825,6 +858,9 @@ create sequence SMP_USER_SEQ start with 1 increment by 1; alter table SMP_CREDENTIAL add constraint SMP_CRD_USER_NAME_TYPE_IDX unique (CREDENTIAL_NAME, CREDENTIAL_TYPE, CREDENTIAL_TARGET); + + alter table SMP_DOCUMENT_PROPERTY + add constraint SMP_DOC_PROP_IDX unique (FK_DOCUMENT_ID, PROPERTY_NAME); create index SMP_DOCVER_DOCUMENT_IDX on SMP_DOCUMENT_VERSION (FK_DOCUMENT_ID); alter table SMP_DOCUMENT_VERSION @@ -930,6 +966,16 @@ create index SMP_SMD_DOC_SCH_IDX on SMP_SUBRESOURCE (IDENTIFIER_SCHEME); foreign key (REV) references SMP_REV_INFO; + alter table SMP_DOCUMENT_PROPERTY + add constraint FKfag3795e9mrvfvesd00yis9yh + foreign key (FK_DOCUMENT_ID) + references SMP_DOCUMENT; + + alter table SMP_DOCUMENT_PROPERTY_AUD + add constraint FK81057kcrugb1cfm0io5vkxtin + foreign key (REV) + references SMP_REV_INFO; + alter table SMP_DOCUMENT_VERSION add constraint FKalsuoqx4csyp9mygvng911do foreign key (FK_DOCUMENT_ID) diff --git a/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/ui/edit/DocumentEditControllerIT.java b/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/ui/edit/DocumentEditControllerIT.java index 13e9509ddbee2bd0da8809096de69973c46b27dc..0b610f028ee3ef4937786164e1c18b52366c493a 100644 --- a/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/ui/edit/DocumentEditControllerIT.java +++ b/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/ui/edit/DocumentEditControllerIT.java @@ -77,7 +77,7 @@ class DocumentEditControllerIT extends AbstractControllerTest { ) .andExpect(status().isOk()).andReturn(); // then - DocumentRo documentRo = getObjectFromResponse(result, DocumentRo.class); + DocumentRO documentRo = getObjectFromResponse(result, DocumentRO.class); assertNotNull(documentRo); assertTrue(documentRo.getAllVersions().isEmpty()); // was just created without document } @@ -106,7 +106,7 @@ class DocumentEditControllerIT extends AbstractControllerTest { ) .andExpect(status().isOk()).andReturn(); // then - DocumentRo documentRo = getObjectFromResponse(result, DocumentRo.class); + DocumentRO documentRo = getObjectFromResponse(result, DocumentRO.class); assertNotNull(documentRo); assertFalse(documentRo.getAllVersions().isEmpty()); // was just created without document assertNotNull(documentRo.getPayload()); @@ -129,7 +129,7 @@ class DocumentEditControllerIT extends AbstractControllerTest { ResourceRO resourceRO = resources.get(0); // document to validate - DocumentRo documentRo = new DocumentRo(); + DocumentRO documentRo = new DocumentRO(); documentRo.setPayload(TestROUtils.createSMP10ServiceGroupPayload(resourceRO.getIdentifierValue(), resourceRO.getIdentifierScheme())); // when @@ -160,7 +160,7 @@ class DocumentEditControllerIT extends AbstractControllerTest { ResourceRO resourceRO = resources.get(0); // document to validate - DocumentRo documentRo = new DocumentRo(); + DocumentRO documentRo = new DocumentRO(); documentRo.setPayload("invalid payload"); // when @@ -203,7 +203,7 @@ class DocumentEditControllerIT extends AbstractControllerTest { ) .andExpect(status().isOk()).andReturn(); - DocumentRo result = getObjectFromResponse(response, DocumentRo.class); + DocumentRO result = getObjectFromResponse(response, DocumentRO.class); assertNotNull(result); assertNotNull(result.getPayload()); } diff --git a/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/ui/internal/DomainAdminControllerIT.java b/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/ui/internal/DomainAdminControllerIT.java index 072eb64bb9db0a78a071e694bcde72dd8c45c741..3beb9dc0dfe196baadf3da60670cc5a7f643f2c1 100644 --- a/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/ui/internal/DomainAdminControllerIT.java +++ b/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/ui/internal/DomainAdminControllerIT.java @@ -168,7 +168,7 @@ class DomainAdminControllerIT extends AbstractControllerTest { // assertNotNull(resultObject); assertEquals(domainCode, resultObject.getDomainCode()); - assertEquals(EntityROStatus.REMOVE.getStatusNumber(), resultObject.getStatus()); + assertEquals(EntityROStatus.REMOVED.getStatusNumber(), resultObject.getStatus()); } @Test diff --git a/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/ui/internal/TruststoreAdminControllerTest.java b/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/ui/internal/TruststoreAdminControllerTest.java index 8f987f790eceb9c2e494e60fa36e74c5e9c32da8..d5dee4b7a6cb5c61bf9e4be7be97033eec5271d9 100644 --- a/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/ui/internal/TruststoreAdminControllerTest.java +++ b/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/ui/internal/TruststoreAdminControllerTest.java @@ -172,7 +172,7 @@ class TruststoreAdminControllerTest extends AbstractControllerTest { CertificateRO res = getObjectFromResponse(result, CertificateRO.class); assertNotNull(res); - assertEquals(EntityROStatus.REMOVE.getStatusNumber(), res.getStatus()); + assertEquals(EntityROStatus.REMOVED.getStatusNumber(), res.getStatus()); assertEquals(countStart - 1, uiTruststoreService.getCertificateROEntriesList().size()); } } diff --git a/smp-webapp/src/test/resources/cleanup-database.sql b/smp-webapp/src/test/resources/cleanup-database.sql index 604df6ae9c5863159e7821ccf8bdde37b0aa7f4b..847d25bd63fd55ac7d4b466f84a4861211b80bb2 100755 --- a/smp-webapp/src/test/resources/cleanup-database.sql +++ b/smp-webapp/src/test/resources/cleanup-database.sql @@ -18,6 +18,8 @@ DELETE FROM SMP_SUBRESOURCE; DELETE FROM SMP_SUBRESOURCE_AUD; DELETE FROM SMP_RESOURCE; DELETE FROM SMP_RESOURCE_AUD; +DELETE FROM SMP_DOCUMENT_PROPERTY; +DELETE FROM SMP_DOCUMENT_PROPERTY_AUD; DELETE FROM SMP_DOCUMENT_VERSION; DELETE FROM SMP_DOCUMENT_VERSION_AUD; DELETE FROM SMP_DOCUMENT; diff --git a/smp-webapp/src/test/resources/input/ServiceMetadata.xml b/smp-webapp/src/test/resources/input/ServiceMetadata.xml index 38624ee0a949e8f8c3a5c33ab3edb862f7f68ffe..e815bd68d59352ec2727dc8412b38623a2a82525 100644 --- a/smp-webapp/src/test/resources/input/ServiceMetadata.xml +++ b/smp-webapp/src/test/resources/input/ServiceMetadata.xml @@ -1,23 +1,4 @@ <?xml version="1.0" encoding="UTF-8" standalone="no"?> -<!-- - #START_LICENSE# - smp-webapp - %% - Copyright (C) 2017 - 2024 European Commission | eDelivery | DomiSMP - %% - Licensed under the EUPL, Version 1.2 or – as soon they will be approved by the European Commission - subsequent - versions of the EUPL (the "Licence"); - You may not use this work except in compliance with the Licence. - You may obtain a copy of the Licence at: - - [PROJECT_HOME]\license\eupl-1.2\license.txt or https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 - - Unless required by applicable law or agreed to in writing, software distributed under the Licence is - distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the Licence for the specific language governing permissions and limitations under the Licence. - #END_LICENSE# - --> - <ServiceMetadata xmlns="http://docs.oasis-open.org/bdxr/ns/SMP/2016/05"> <ServiceInformation> <ParticipantIdentifier scheme="ehealth-actorid-qns">urn:australia:ncpb</ParticipantIdentifier> diff --git a/smp-webapp/src/test/resources/input/ServiceMetadata_linarized.xml b/smp-webapp/src/test/resources/input/ServiceMetadata_linarized.xml index c693976d0b3ed879c5d398bfd34c9123baa11cf5..9c5c59a833655683277f9fa83d47eec8ed8b1dbb 100644 --- a/smp-webapp/src/test/resources/input/ServiceMetadata_linarized.xml +++ b/smp-webapp/src/test/resources/input/ServiceMetadata_linarized.xml @@ -1,20 +1,2 @@ <?xml version="1.0" encoding="UTF-8" standalone="no"?> -<!-- - #START_LICENSE# - smp-webapp - %% - Copyright (C) 2017 - 2024 European Commission | eDelivery | DomiSMP - %% - Licensed under the EUPL, Version 1.2 or – as soon they will be approved by the European Commission - subsequent - versions of the EUPL (the "Licence"); - You may not use this work except in compliance with the Licence. - You may obtain a copy of the Licence at: - - [PROJECT_HOME]\license\eupl-1.2\license.txt or https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 - - Unless required by applicable law or agreed to in writing, software distributed under the Licence is - distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the Licence for the specific language governing permissions and limitations under the Licence. - #END_LICENSE# - --> <ServiceMetadata xmlns="http://docs.oasis-open.org/bdxr/ns/SMP/2016/05"><ServiceInformation><ParticipantIdentifier scheme="ehealth-actorid-qns">urn:brazil:ncpb</ParticipantIdentifier><DocumentIdentifier scheme="ehealth-resid-qns">urn::epsos##services:extended:epsos::107</DocumentIdentifier><ProcessList><Process><ProcessIdentifier scheme="ehealth-procid-qns">urn:epsosPatientService::List</ProcessIdentifier><ServiceEndpointList><Endpoint transportProfile="urn:ihe:iti:2013:xcpd"><EndpointURI>http://poland.pl/ncp/patient/list</EndpointURI><RequireBusinessLevelSignature>false</RequireBusinessLevelSignature><MinimumAuthenticationLevel>urn:epSOS:loa:1</MinimumAuthenticationLevel><ServiceActivationDate>2016-06-06T11:06:02.000+02:00</ServiceActivationDate><ServiceExpirationDate>2026-06-06T11:06:02+02:00</ServiceExpirationDate><Certificate>MIID7jCCA1egAwIBAgICA+YwDQYJKoZIhvcNAQENBQAwOjELMAkGA1UEBhMCRlIxEzARBgNVBAoMCklIRSBFdXJvcGUxFjAUBgNVBAMMDUlIRSBFdXJvcGUgQ0EwHhcNMTYwNjAxMTQzNTUzWhcNMjYwNjAxMTQzNTUzWjCBgzELMAkGA1UEBhMCUFQxDDAKBgNVBAoMA01vSDENMAsGA1UECwwEU1BNUzENMAsGA1UEKgwESm9hbzEOMAwGA1UEBRMFQ3VuaGExHTAbBgNVBAMMFHFhZXBzb3MubWluLXNhdWRlLnB0MRkwFwYDVQQMDBBTZXJ2aWNlIFByb3ZpZGVyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1eN4qPSSRZqjVFG9TlcPlxf2WiSimQK9L1nf9Z/s0ezeGQjCukDeDq/Wzqd9fpHhaMMq+XSSOtyEtIr5K/As4kFrViONUUkG12J6UllSWogp0NYFwA4wIqKSFiTnQS5/nRTs05oONCCGILCyJNNeO53JzPlaq3/QbPLssuSAr6XucPE8wBBGM8b/TsB2G/zjG8yuSTgGbhaZekq/Vnf9ftj1fr/vJDDAQgH6Yvzd88Z0DACJPHfW1p4F/OWLI386Bq7g/bo1DUPAyEwlf+CkLgJWRKki3yJlOCIZ9enMA5O7rfeG3rXdgYGmWS7tNEgKXxgC+heiYvi7ZWd7M+/SUwIDAQABo4IBMzCCAS8wPgYDVR0fBDcwNTAzoDGgL4YtaHR0cHM6Ly9nYXplbGxlLmloZS5uZXQvcGtpL2NybC82NDMvY2FjcmwuY3JsMDwGCWCGSAGG+EIBBAQvFi1odHRwczovL2dhemVsbGUuaWhlLm5ldC9wa2kvY3JsLzY0My9jYWNybC5jcmwwPAYJYIZIAYb4QgEDBC8WLWh0dHBzOi8vZ2F6ZWxsZS5paGUubmV0L3BraS9jcmwvNjQzL2NhY3JsLmNybDAfBgNVHSMEGDAWgBTsMw4TyCJeouFrr0N7el3Sd3MdfjAdBgNVHQ4EFgQU1GQ/K1ykIwWFgiONzWJLQzufF/8wDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCBSAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDQYJKoZIhvcNAQENBQADgYEAZ7t1Qkr9wz3q6+WcF6p/YX7Jr0CzVe7w58FvJFk2AsHeYkSlOyO5hxNpQbs1L1v6JrcqziNFrh2QKGT2v6iPdWtdCT8HBLjmuvVWxxnfzYjdQ0J+kdKMAEV6EtWU78OqL60CCtUZKXE/NKJUq7TTUCFP2fwiARy/t1dTD2NZo8c=</Certificate><ServiceDescription>This is the epSOS Patient Service List for the Polish NCP</ServiceDescription><TechnicalContactUrl>http://poland.pl/contact</TechnicalContactUrl><TechnicalInformationUrl>http://poland.pl/contact</TechnicalInformationUrl></Endpoint></ServiceEndpointList></Process></ProcessList><Extension></Extension></ServiceInformation></ServiceMetadata>