diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..dfe07704 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/README.md b/README.md index 15cfcc91..4f33f2b8 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# OpenTOSCA UI +# OpenTOSCA UI 2.0 [![Build Status](https://travis-ci.org/OpenTOSCA/ui.svg?branch=master)](https://travis-ci.org/OpenTOSCA/ui) [![License](https://img.shields.io/badge/License-EPL%201.0-red.svg)](https://opensource.org/licenses/EPL-1.0) diff --git a/package-lock.json b/package-lock.json index f87046af..e98d5812 100644 --- a/package-lock.json +++ b/package-lock.json @@ -102,6 +102,55 @@ "uri-js": "^4.2.2" } }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true, + "optional": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "optional": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "node-sass": { + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.11.0.tgz", + "integrity": "sha512-bHUdHTphgQJZaF1LASx0kAviPH7sGlcyNhWade4eVIpFp6tsn7SV8xNMTbsQFpEV9VXpnwTTnNYlfsZXgGgmkA==", + "dev": true, + "optional": true, + "requires": { + "async-foreach": "^0.1.3", + "chalk": "^1.1.1", + "cross-spawn": "^3.0.0", + "gaze": "^1.0.0", + "get-stdin": "^4.0.1", + "glob": "^7.0.3", + "in-publish": "^2.0.0", + "lodash.assign": "^4.2.0", + "lodash.clonedeep": "^4.3.2", + "lodash.mergewith": "^4.6.0", + "meow": "^3.7.0", + "mkdirp": "^0.5.1", + "nan": "^2.10.0", + "node-gyp": "^3.8.0", + "npmlog": "^4.0.0", + "request": "^2.88.0", + "sass-graph": "^2.2.4", + "stdout-stream": "^1.4.0", + "true-case-path": "^1.0.2" + } + }, "opn": { "version": "5.4.0", "resolved": "https://registry.npmjs.org/opn/-/opn-5.4.0.tgz", @@ -134,6 +183,13 @@ "dev": true } } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true, + "optional": true } } }, @@ -1396,9 +1452,9 @@ "dev": true }, "@types/node": { - "version": "10.12.18", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.18.tgz", - "integrity": "sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ==", + "version": "10.17.44", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.44.tgz", + "integrity": "sha512-vHPAyBX1ffLcy4fQHmDyIUMUb42gHZjPHU66nhvbMzAWJqHnySGZ6STwN3rwrnSd1FHB0DI/RWgGELgKSYRDmw==", "dev": true }, "@types/q": { @@ -1665,8 +1721,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "dev": true, - "optional": true + "dev": true }, "accepts": { "version": "1.3.5", @@ -1819,7 +1874,6 @@ "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", "dev": true, - "optional": true, "requires": { "delegates": "^1.0.0", "readable-stream": "^2.0.6" @@ -1856,8 +1910,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", - "dev": true, - "optional": true + "dev": true }, "array-flatten": { "version": "2.1.2", @@ -1988,8 +2041,7 @@ "version": "0.1.3", "resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz", "integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=", - "dev": true, - "optional": true + "dev": true }, "async-limiter": { "version": "1.0.0", @@ -2302,7 +2354,6 @@ "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", "dev": true, - "optional": true, "requires": { "inherits": "~2.0.0" } @@ -2638,15 +2689,13 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", - "dev": true, - "optional": true + "dev": true }, "camelcase-keys": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", "dev": true, - "optional": true, "requires": { "camelcase": "^2.0.0", "map-obj": "^1.0.0" @@ -3058,8 +3107,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "dev": true, - "optional": true + "dev": true }, "constants-browserify": { "version": "1.0.0", @@ -3213,7 +3261,6 @@ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-3.0.1.tgz", "integrity": "sha1-ElYDfsufDF9549bvE14wdwGEuYI=", "dev": true, - "optional": true, "requires": { "lru-cache": "^4.0.1", "which": "^1.2.9" @@ -3275,7 +3322,6 @@ "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", "dev": true, - "optional": true, "requires": { "array-find-index": "^1.0.1" } @@ -3467,8 +3513,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", - "dev": true, - "optional": true + "dev": true }, "depd": { "version": "1.1.2", @@ -3644,6 +3689,12 @@ "minimalistic-crypto-utils": "^1.0.0" } }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, "emojis-list": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", @@ -4427,11 +4478,10 @@ } }, "fstream": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", - "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=", + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", + "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", "dev": true, - "optional": true, "requires": { "graceful-fs": "^4.1.2", "inherits": "~2.0.0", @@ -4449,7 +4499,6 @@ "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", "dev": true, - "optional": true, "requires": { "aproba": "^1.0.3", "console-control-strings": "^1.0.0", @@ -4466,7 +4515,6 @@ "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz", "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==", "dev": true, - "optional": true, "requires": { "globule": "^1.0.0" } @@ -4487,8 +4535,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", - "dev": true, - "optional": true + "dev": true }, "get-stream": { "version": "3.0.0", @@ -4567,11 +4614,10 @@ } }, "globule": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/globule/-/globule-1.2.1.tgz", - "integrity": "sha512-g7QtgWF4uYSL5/dn71WxubOrS7JVGCnFPEnoeChJmBnyR9Mw8nGoEwOgJL/RC2Te0WhbsEUCejfH8SZNJ+adYQ==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.2.tgz", + "integrity": "sha512-7IDTQTIu2xzXkT+6mlluidnWo+BypnbSoEVVQCGfzqnl5Ik8d3e1d4wycb8Rj9tWW+Z39uPWsdlquqiqPCd/pA==", "dev": true, - "optional": true, "requires": { "glob": "~7.1.1", "lodash": "~4.17.10", @@ -4648,8 +4694,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", - "dev": true, - "optional": true + "dev": true }, "has-value": { "version": "1.0.0", @@ -5008,18 +5053,16 @@ "dev": true }, "in-publish": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.0.tgz", - "integrity": "sha1-4g/146KvwmkDILbcVSaCqcf631E=", - "dev": true, - "optional": true + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.1.tgz", + "integrity": "sha512-oDM0kUSNFC31ShNxHKUyfZKy8ZeXZBWMjMdZHKLOk13uvT27VTL/QzRGfRUcevJhpkZAvlhPYuXkF7eNWrtyxQ==", + "dev": true }, "indent-string": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", "dev": true, - "optional": true, "requires": { "repeating": "^2.0.0" } @@ -5383,8 +5426,7 @@ "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", - "dev": true, - "optional": true + "dev": true }, "is-windows": { "version": "1.0.2", @@ -5692,11 +5734,10 @@ "dev": true }, "js-base64": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.1.tgz", - "integrity": "sha512-M7kLczedRMYX4L8Mdh4MzyAMM9O5osx+4FcOQuTvr3A9F2D9S5JXheN0ewNbrvK2UatkTRhL5ejGmGSjNMiZuw==", - "dev": true, - "optional": true + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.6.4.tgz", + "integrity": "sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==", + "dev": true }, "js-tokens": { "version": "4.0.0", @@ -6019,7 +6060,6 @@ "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", "dev": true, - "optional": true, "requires": { "graceful-fs": "^4.1.2", "parse-json": "^2.2.0", @@ -6032,8 +6072,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true, - "optional": true + "dev": true } } }, @@ -6095,9 +6134,9 @@ "dev": true }, "lodash.mergewith": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.1.tgz", - "integrity": "sha512-eWw5r+PYICtEBgrBE5hhlT6aAa75f411bgDz/ZL2KZqYV03USvucsxcHUIlGTDTECs1eunpI7HOV7U+WLDvNdQ==", + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", + "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", "dev": true, "optional": true }, @@ -6156,7 +6195,6 @@ "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", "dev": true, - "optional": true, "requires": { "currently-unhandled": "^0.4.1", "signal-exit": "^3.0.0" @@ -6314,8 +6352,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", - "dev": true, - "optional": true + "dev": true }, "map-visit": { "version": "1.0.0", @@ -6377,7 +6414,6 @@ "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", "dev": true, - "optional": true, "requires": { "camelcase-keys": "^2.0.0", "decamelize": "^1.1.2", @@ -6392,11 +6428,10 @@ }, "dependencies": { "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true, - "optional": true + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true } } }, @@ -6719,7 +6754,6 @@ "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz", "integrity": "sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==", "dev": true, - "optional": true, "requires": { "fstream": "^1.0.0", "glob": "^7.0.3", @@ -6740,7 +6774,6 @@ "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", "dev": true, - "optional": true, "requires": { "abbrev": "1" } @@ -6749,18 +6782,16 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", - "dev": true, - "optional": true + "dev": true }, "tar": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", - "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.2.tgz", + "integrity": "sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA==", "dev": true, - "optional": true, "requires": { "block-stream": "*", - "fstream": "^1.0.2", + "fstream": "^1.0.12", "inherits": "2" } } @@ -6834,11 +6865,10 @@ } }, "node-sass": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.11.0.tgz", - "integrity": "sha512-bHUdHTphgQJZaF1LASx0kAviPH7sGlcyNhWade4eVIpFp6tsn7SV8xNMTbsQFpEV9VXpnwTTnNYlfsZXgGgmkA==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.14.1.tgz", + "integrity": "sha512-sjCuOlvGyCJS40R8BscF5vhVlQjNN069NtQ1gSxyK1u9iqvn6tf7O1R4GNowVZfiZUCRt5MmMs1xd+4V/7Yr0g==", "dev": true, - "optional": true, "requires": { "async-foreach": "^0.1.3", "chalk": "^1.1.1", @@ -6847,16 +6877,14 @@ "get-stdin": "^4.0.1", "glob": "^7.0.3", "in-publish": "^2.0.0", - "lodash.assign": "^4.2.0", - "lodash.clonedeep": "^4.3.2", - "lodash.mergewith": "^4.6.0", + "lodash": "^4.17.15", "meow": "^3.7.0", "mkdirp": "^0.5.1", - "nan": "^2.10.0", + "nan": "^2.13.2", "node-gyp": "^3.8.0", "npmlog": "^4.0.0", "request": "^2.88.0", - "sass-graph": "^2.2.4", + "sass-graph": "2.2.5", "stdout-stream": "^1.4.0", "true-case-path": "^1.0.2" }, @@ -6865,15 +6893,13 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true, - "optional": true + "dev": true }, "chalk": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, - "optional": true, "requires": { "ansi-styles": "^2.2.1", "escape-string-regexp": "^1.0.2", @@ -6882,12 +6908,23 @@ "supports-color": "^2.0.0" } }, + "lodash": { + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", + "dev": true + }, + "nan": { + "version": "2.14.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", + "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==", + "dev": true + }, "supports-color": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true, - "optional": true + "dev": true } } }, @@ -6996,7 +7033,6 @@ "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", "integrity": "sha1-CKfyqL9zRgR3mp76StXMcXq7lUs=", "dev": true, - "optional": true, "requires": { "are-we-there-yet": "~1.1.2", "console-control-strings": "~1.1.0", @@ -7176,16 +7212,6 @@ "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "dev": true }, - "os-locale": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", - "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", - "dev": true, - "optional": true, - "requires": { - "lcid": "^1.0.0" - } - }, "os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", @@ -8063,7 +8089,6 @@ "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", "dev": true, - "optional": true, "requires": { "load-json-file": "^1.0.0", "normalize-package-data": "^2.3.2", @@ -8075,7 +8100,6 @@ "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", "dev": true, - "optional": true, "requires": { "graceful-fs": "^4.1.2", "pify": "^2.0.0", @@ -8086,8 +8110,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true, - "optional": true + "dev": true } } }, @@ -8096,7 +8119,6 @@ "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", "dev": true, - "optional": true, "requires": { "find-up": "^1.0.0", "read-pkg": "^1.0.0" @@ -8107,7 +8129,6 @@ "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", "dev": true, - "optional": true, "requires": { "path-exists": "^2.0.0", "pinkie-promise": "^2.0.0" @@ -8118,7 +8139,6 @@ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", "dev": true, - "optional": true, "requires": { "pinkie-promise": "^2.0.0" } @@ -8165,7 +8185,6 @@ "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", "dev": true, - "optional": true, "requires": { "indent-string": "^2.1.0", "strip-indent": "^1.0.1" @@ -8460,20 +8479,18 @@ "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "sass-graph": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.4.tgz", - "integrity": "sha1-E/vWPNHK8JCLn9k0dq1DpR0eC0k=", + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.5.tgz", + "integrity": "sha512-VFWDAHOe6mRuT4mZRd4eKE+d8Uedrk6Xnh7Sh9b4NGufQLQjOrvf/MQoOdx+0s92L89FeyUUNfU597j/3uNpag==", "dev": true, - "optional": true, "requires": { "glob": "^7.0.0", "lodash": "^4.0.0", "scss-tokenizer": "^0.2.3", - "yargs": "^7.0.0" + "yargs": "^13.3.2" } }, "sass-loader": { @@ -8521,7 +8538,6 @@ "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz", "integrity": "sha1-jrBtualyMzOCTT9VMGQRSYR85dE=", "dev": true, - "optional": true, "requires": { "js-base64": "^2.1.8", "source-map": "^0.4.2" @@ -8532,7 +8548,6 @@ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", "dev": true, - "optional": true, "requires": { "amdefine": ">=0.0.4" } @@ -9317,7 +9332,6 @@ "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.1.tgz", "integrity": "sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA==", "dev": true, - "optional": true, "requires": { "readable-stream": "^2.0.1" } @@ -9424,7 +9438,6 @@ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", "dev": true, - "optional": true, "requires": { "is-utf8": "^0.2.0" } @@ -9440,7 +9453,6 @@ "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", "dev": true, - "optional": true, "requires": { "get-stdin": "^4.0.1" } @@ -9893,8 +9905,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", - "dev": true, - "optional": true + "dev": true }, "trim-right": { "version": "1.0.1", @@ -9907,7 +9918,6 @@ "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.3.tgz", "integrity": "sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew==", "dev": true, - "optional": true, "requires": { "glob": "^7.1.2" } @@ -10791,18 +10801,16 @@ } }, "which-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", - "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=", - "dev": true, - "optional": true + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true }, "wide-align": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", "dev": true, - "optional": true, "requires": { "string-width": "^1.0.2 || 2" } @@ -10890,59 +10898,149 @@ "dev": true }, "yargs": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz", - "integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=", + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", "dev": true, - "optional": true, "requires": { - "camelcase": "^3.0.0", - "cliui": "^3.2.0", - "decamelize": "^1.1.1", - "get-caller-file": "^1.0.1", - "os-locale": "^1.4.0", - "read-pkg-up": "^1.0.1", + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", + "require-main-filename": "^2.0.0", "set-blocking": "^2.0.0", - "string-width": "^1.0.2", - "which-module": "^1.0.0", - "y18n": "^3.2.1", - "yargs-parser": "^5.0.0" + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" }, "dependencies": { - "camelcase": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "find-up": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", - "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", "dev": true, - "optional": true + "requires": { + "locate-path": "^3.0.0" + } }, - "y18n": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", - "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", "dev": true, - "optional": true + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + } } } }, "yargs-parser": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz", - "integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=", + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", "dev": true, - "optional": true, "requires": { - "camelcase": "^3.0.0" + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" }, "dependencies": { "camelcase": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", - "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", - "dev": true, - "optional": true + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true } } }, diff --git a/package.json b/package.json index 2b1cf54e..634cb1cf 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "fuse.js": "^3.4.4", "lodash": "^4.17.11", "ng-spin-kit": "^5.1.1", + "node-sass": "^4.14.1", "primeflex": "^1.0.0-rc.1", "primeicons": "^1.0.0", "primeng": "^7.1.0", @@ -48,7 +49,7 @@ "@types/jasmine": "~3.3.5", "@types/jasminewd2": "~2.0.6", "@types/lodash": "^4.14.119", - "@types/node": "~10.12.18", + "@types/node": "^10.12.30", "@types/redux-logger": "^3.0.6", "codelyzer": "~4.5.0", "jasmine-core": "~3.3.0", @@ -58,6 +59,7 @@ "karma-coverage-istanbul-reporter": "~2.0.4", "karma-jasmine": "~2.0.1", "karma-jasmine-html-reporter": "^1.4.0", + "node-sass": "^4.14.1", "protractor": "~5.4.2", "ts-node": "~7.0.1", "tslint": "~5.12.1", diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 9f6fc804..314f662d 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -15,12 +15,17 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { PageNotFoundComponent } from './page-not-found.component'; import { AboutComponent } from './about.component'; +import { SituationComponent } from './situations/situation.component'; const routes: Routes = [ { path: 'about', component: AboutComponent }, + { + path: 'situation', + component: SituationComponent + }, { path: '', redirectTo: '/applications', diff --git a/src/app/app.component.ts b/src/app/app.component.ts index b460dfcf..f425107f 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -44,6 +44,11 @@ export class AppComponent { icon: 'fa fa-cogs', routerLink: ['/administration'] }, + { + label: 'Situation', + icon: 'fas fa-tasks', + routerLink: ['/situation'] + }, { label: 'About', icon: 'fa fa-info-circle', diff --git a/src/app/app.module.ts b/src/app/app.module.ts index f760161c..b89a5f67 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -16,6 +16,7 @@ import { NgModule } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { AppComponent } from './app.component'; import { AboutComponent } from './about.component'; +import { SituationModule } from './situations/situation.module'; import { AppRoutingModule } from './app-routing.module'; import { PageNotFoundComponent } from './page-not-found.component'; import { ApplicationManagementModule } from './application-management/application-management.module'; @@ -44,6 +45,7 @@ import { TabMenuModule } from 'primeng/tabmenu'; NgReduxModule, NgReduxRouterModule, GrowlModule, + SituationModule, TabMenuModule, CardModule, ApplicationManagementModule, diff --git a/src/app/application-management/application-detail/application-detail.component.html b/src/app/application-management/application-detail/application-detail.component.html index 29fd4a3a..37e955d8 100644 --- a/src/app/application-management/application-detail/application-detail.component.html +++ b/src/app/application-management/application-detail/application-detail.component.html @@ -31,16 +31,16 @@

{{ csar.display_name }}

Instances -
-   + (click)="selectBuildPlan()">  -
+ pTooltip="Reload instances" showDelay="300" tooltipPosition="right" + (click)="triggerReloadAppInstances()" style="margin-right:10px"> +
{{ csar.display_name }} [instanceId]="instanceId" [inputValidation]="true"> + + diff --git a/src/app/application-management/application-detail/application-detail.component.ts b/src/app/application-management/application-detail/application-detail.component.ts index 9d44ada6..d20477b7 100644 --- a/src/app/application-management/application-detail/application-detail.component.ts +++ b/src/app/application-management/application-detail/application-detail.component.ts @@ -34,6 +34,7 @@ export class ApplicationDetailComponent implements OnInit, OnDestroy { @select(['container', 'application', 'csar']) csar: Observable; public dialogVisible = false; + public csarSelectDialogVisible = false; public selectedPlanType: PlanTypes; public buildPlanExists = false; public terminationPlanExists = false; @@ -108,4 +109,8 @@ export class ApplicationDetailComponent implements OnInit, OnDestroy { this.instanceId = instanceId; this.dialogVisible = true; } + + selectCSARForMigrationPlan(app: Csar): void { + this.csarSelectDialogVisible = true; + } } diff --git a/src/app/application-management/application-instance-detail/application-instance-detail.component.html b/src/app/application-management/application-instance-detail/application-instance-detail.component.html index 77ef65f4..703bfeca 100644 --- a/src/app/application-management/application-instance-detail/application-instance-detail.component.html +++ b/src/app/application-management/application-instance-detail/application-instance-detail.component.html @@ -64,119 +64,3 @@ [instance]="serviceTemplateInstance">
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/app/application-management/application-management.module.ts b/src/app/application-management/application-management.module.ts index d318e7a8..ef0c28f8 100644 --- a/src/app/application-management/application-management.module.ts +++ b/src/app/application-management/application-management.module.ts @@ -55,6 +55,7 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { ManagementPlanInstanceListComponent } from './management-plan-instance-list/management-plan-instance-list.component'; import { TableModule } from 'primeng/table'; import { ApplicationInstanceBoundaryDefinitionInterfacesListComponent } from './application-instance-boundary-definition-interfaces-list/application-instance-boundary-definition-interfaces-list.component'; +import {MigrationPlanCreationDialogComponent} from "./migration-plan-creation-dialog/migration-plan-creation-dialog.component"; // tslint:disable-line:max-line-length @NgModule({ @@ -100,6 +101,7 @@ import { ApplicationInstanceBoundaryDefinitionInterfacesListComponent } from './ BuildplanMonitorComponent, ManagementPlanListComponent, ManagementPlanExecutionDialogComponent, + MigrationPlanCreationDialogComponent, ManagementPlanInstanceListComponent, ApplicationInstanceBoundaryDefinitionInterfacesListComponent ], diff --git a/src/app/application-management/management-plan-execution-dialog/management-plan-execution-dialog.component.html b/src/app/application-management/management-plan-execution-dialog/management-plan-execution-dialog.component.html index 2c924d9c..74cda18b 100644 --- a/src/app/application-management/management-plan-execution-dialog/management-plan-execution-dialog.component.html +++ b/src/app/application-management/management-plan-execution-dialog/management-plan-execution-dialog.component.html @@ -111,7 +111,7 @@ - diff --git a/src/app/application-management/management-plan-execution-dialog/management-plan-execution-dialog.component.ts b/src/app/application-management/management-plan-execution-dialog/management-plan-execution-dialog.component.ts index c9feea51..c8915120 100644 --- a/src/app/application-management/management-plan-execution-dialog/management-plan-execution-dialog.component.ts +++ b/src/app/application-management/management-plan-execution-dialog/management-plan-execution-dialog.component.ts @@ -109,6 +109,7 @@ export class ManagementPlanExecutionDialogComponent implements OnInit, OnChanges if (this.plan) { this.showInputs = true; this.selectedPlan = this.plan + this.checkInputs(); } } } diff --git a/src/app/application-management/migration-plan-creation-dialog/migration-plan-creation-dialog.component.html b/src/app/application-management/migration-plan-creation-dialog/migration-plan-creation-dialog.component.html new file mode 100644 index 00000000..15b8f2b2 --- /dev/null +++ b/src/app/application-management/migration-plan-creation-dialog/migration-plan-creation-dialog.component.html @@ -0,0 +1,43 @@ + + + + + Select Application to create Migration Plan for migrating instances to instances of the selected Application + + + + + + +
+
+
+ +
+
+ Plan generation in progress +
+
+
+ + +
+
diff --git a/src/app/application-management/migration-plan-creation-dialog/migration-plan-creation-dialog.component.ts b/src/app/application-management/migration-plan-creation-dialog/migration-plan-creation-dialog.component.ts new file mode 100644 index 00000000..6cfd6c0b --- /dev/null +++ b/src/app/application-management/migration-plan-creation-dialog/migration-plan-creation-dialog.component.ts @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2018 University of Stuttgart. + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Apache Software License 2.0 + * which is available at https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 + */ +import { Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges } from '@angular/core'; +import { Plan } from '../../core/model/plan.model'; +import { GrowlActions } from '../../core/growl/growl-actions'; +import { NgRedux, select } from '@angular-redux/store'; +import { AppState } from '../../store/app-state.model'; +import { ApplicationManagementService } from '../../core/service/application-management.service'; +import { LoggerService } from '../../core/service/logger.service'; +import { globals } from '../../globals'; +import { Observable } from 'rxjs'; +import { Interface } from '../../core/model/interface.model'; +import {SelectItem, SelectItemGroup} from 'primeng/api'; +import { PlanTypes } from '../../core/model/plan-types.model'; +import { PlacementService } from '../../core/service/placement.service'; +import { PlacementModel } from '../../core/model/placement.model'; +import { Path } from '../../core/path'; +import { PlacementNodeTemplate } from '../../core/model/placement-node-template.model'; +import { NodeTemplateInstance } from '../../core/model/node-template-instance.model'; +import { PlacementPair } from '../../core/model/placement-pair.model'; +import { PlanParameter } from '../../core/model/plan-parameter.model'; +import {Csar} from "../../core/model/csar.model"; + +@Component({ + selector: 'opentosca-migration-plan-creation-dialog', + templateUrl: './migration-plan-creation-dialog.component.html' +}) +export class MigrationPlanCreationDialogComponent implements OnInit, OnChanges { + + @Input() visible = false; + @Output() visibleChange = new EventEmitter(); + @Input() csar: Observable; + + @select(['container', 'applications']) public readonly apps: Observable>; + + public loading = false; + public selectedApp: Csar; + public runnable: boolean; + public csars: Csar[]; + public selection : SelectItem[]; + public creationInProgress: boolean; + + + constructor( + private appService: ApplicationManagementService, + private ngRedux: NgRedux, + private logger: LoggerService) { + } + + operationSelected(app: Csar): void { + if (app) { + console.log("Selected following app:"); + console.log(app); + this.selectedApp = app; + } + } + + + ngOnInit(): void { + this.appService.getResolvedApplications().subscribe(value => {console.log("Found following csars:"); console.log(value); this.updateSelectionList(value)}); + this.creationInProgress = false; + } + + private updateSelectionList(csars: Csar[]): void { + this.selection = []; + this.csar.subscribe(value => { + csars.forEach(csar => { + if (value.id != csar.id){ + this.selection.push({ label: csar.name, value: csar }); + } + }); + }); + + + } + + ngOnChanges(changes: SimpleChanges): void { + + } + + /** + * Closes the modal and emits change event. + */ + closeInputModal(): void { + this.visible = false; + this.creationInProgress = false; + // TODO: remove this or place elsewhere + this.selectedApp = null; + this.visibleChange.emit(false); + } + + createMigrationPlan(): void { + this.creationInProgress = true; + this.csar.subscribe(value => { + this.appService.createMigrationPlan(value.id, this.selectedApp.id).subscribe( value => { + console.log("Following result was received:"); + console.log(value); + this.creationInProgress = false; + this.visible = false; + // TODO: remove this or place elsewhere + this.selectedApp = null; + this.visibleChange.emit(false); + + }); + }); + + + + + } +} diff --git a/src/app/core/service/application-management.service.ts b/src/app/core/service/application-management.service.ts index 3e0bf0ee..6681c3b9 100644 --- a/src/app/core/service/application-management.service.ts +++ b/src/app/core/service/application-management.service.ts @@ -22,7 +22,7 @@ import { Plan } from '../model/plan.model'; import { NgRedux } from '@angular-redux/store'; import { AppState } from '../../store/app-state.model'; import { Interface } from '../model/interface.model'; -import { HttpClient, HttpHeaders } from '@angular/common/http'; +import {HttpClient, HttpHeaders, HttpResponse} from '@angular/common/http'; import { forkJoin, Observable, of, throwError } from 'rxjs'; import { catchError, flatMap, map } from 'rxjs/operators'; import { InterfaceList } from '../model/interface-list.model'; @@ -129,6 +129,18 @@ export class ApplicationManagementService { return this.http.get(url, this.httpOptionsAcceptJson); } + createMigrationPlan(sourceCsarId: string, targetCsarId: string): Observable> { + console.log("requesting migration plan creation"); + const url = new Path(this.ngRedux.getState().administration.containerUrl).append('csars').append('transform').toString(); + + const httpOptions = { + headers: new HttpHeaders({ + 'Accept': 'application/json' + }), + }; + return this.http.post(url, {source_csar_name: sourceCsarId, target_csar_name: targetCsarId}, { ...httpOptions, responseType: 'text', observe: 'response' }); + } + getFirstServiceTemplateOfCsar(csarId: string): Observable { const url = new Path(this.ngRedux.getState().administration.containerUrl) .append('csars') diff --git a/src/app/globals.ts b/src/app/globals.ts index ee5d604f..a5ad55ec 100644 --- a/src/app/globals.ts +++ b/src/app/globals.ts @@ -21,6 +21,7 @@ export const globals = { 'instanceDataAPIUrl', 'planCallbackAddress_invoker', 'csarEntrypoint', - 'OpenTOSCAContainerAPIServiceInstanceID' + 'OpenTOSCAContainerAPIServiceInstanceID', + 'OpenTOSCAContainerAPIServiceInstanceURL' ] }; diff --git a/src/app/situations/model/agggregatedSituation.module.ts b/src/app/situations/model/agggregatedSituation.module.ts new file mode 100644 index 00000000..413c9086 --- /dev/null +++ b/src/app/situations/model/agggregatedSituation.module.ts @@ -0,0 +1,8 @@ +import { ResourceSupport } from '../../core/model/resource-support.model'; + +export class AggregatedSituation extends ResourceSupport { + id: string; + situation_ids: Array; + logic_expression: string; + active: string; +} \ No newline at end of file diff --git a/src/app/situations/model/situation.module.ts b/src/app/situations/model/situation.module.ts new file mode 100644 index 00000000..a373deba --- /dev/null +++ b/src/app/situations/model/situation.module.ts @@ -0,0 +1,8 @@ +import { ResourceSupport } from '../../core/model/resource-support.model'; + +export class Situation extends ResourceSupport { + id: string; + situation_template_id: string; + active: string; + thing_id: string; +} \ No newline at end of file diff --git a/src/app/situations/model/situationtrigger.module.ts b/src/app/situations/model/situationtrigger.module.ts new file mode 100644 index 00000000..d9217a94 --- /dev/null +++ b/src/app/situations/model/situationtrigger.module.ts @@ -0,0 +1,17 @@ +import { ResourceSupport } from '../../core/model/resource-support.model'; +import { Situation } from './situation.module'; +import { PlanParameter } from '../../core/model/plan-parameter.model'; + + +export class SituationTrigger extends ResourceSupport { + id: string; + situation_ids: Array; + service_instance_id: string; + aggregated_situation_ids: Array; + csar_id: string; + on_activation: string; + single_instance: string; + interface_name: string; + operation_name: string; + input_params: Array; +} diff --git a/src/app/situations/situation.component.html b/src/app/situations/situation.component.html new file mode 100644 index 00000000..66c2936b --- /dev/null +++ b/src/app/situations/situation.component.html @@ -0,0 +1,166 @@ + + Situations + +
+ +   + +   + +
+
+ + Situation Template ID : + Thing ID: + Active: + + + + + + + {{col.header}} + + + + + + + + + {{instance[col.field] | date:'yyyy-MM-dd HH:mm'}} + + +   + + + {{instance[col.field]}} + + + + +
+ + + Situation Triggers + +
+ +   + +   + +
+
+ + Situation IDs: + + + CSAR ID: + + + ServiceInstance: + + Interface: + + Operation: + + Single Instance? : + On Activation? : + + + + + + + Input Parameter + Type + value + + + + + + + + + + + {{parameter.name}} + + + + + + + + + + {{parameter.type}} + + + + + + + + + + {{parameter.value}} + + + + + + +   + + + + + {{col.header}} + + + + + + + + + + + {{params["name"]}} : {{params["value"]}}
+
+
+ +   + + + {{instance[col.field]}} + + +
+
+
diff --git a/src/app/situations/situation.component.scss b/src/app/situations/situation.component.scss new file mode 100644 index 00000000..9fb9e403 --- /dev/null +++ b/src/app/situations/situation.component.scss @@ -0,0 +1,20 @@ +/*! + * Copyright (c) 2018 University of Stuttgart. + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Apache Software License 2.0 + * which is available at https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 + */ + + +.weiss{ + background:#ffffff; + color: #000000; +} + diff --git a/src/app/situations/situation.component.ts b/src/app/situations/situation.component.ts new file mode 100644 index 00000000..7f9baeec --- /dev/null +++ b/src/app/situations/situation.component.ts @@ -0,0 +1,956 @@ +/* + * Copyright (c) 2018 University of Stuttgart. + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Apache Software License 2.0 + * which is available at https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 + */ +import { ApplicationManagementService } from './../core/service/application-management.service'; +import { Observable } from 'rxjs'; +import { Component, OnInit } from '@angular/core'; +import { BreadcrumbActions } from './../core/component/breadcrumb/breadcrumb-actions'; +import { AppState } from './../store/app-state.model'; +import { NgRedux, select } from '@angular-redux/store'; +import { HttpClient } from '@angular/common/http'; +import { Situation } from './model/situation.module'; +import { SituationTrigger } from './model/situationtrigger.module'; +import { AggregatedSituation } from './model/agggregatedSituation.module'; +import { PlanParameter } from './../core/model/plan-parameter.model'; +import { Item } from './../configuration/repository-configuration/repository-configuration.component'; +import { Plan } from '../core/model/plan.model'; +import {SelectItem} from "primeng/api"; +import {Csar} from "../core/model/csar.model"; +import {Interface} from "../core/model/interface.model"; +import {Operation} from "../core/model/operation.model"; +import {globals} from "../globals"; +import {ServiceTemplateInstance} from "../core/model/service-template-instance.model"; +import {ApplicationInstanceManagementService} from "../core/service/application-instance-management.service"; +import {GrowlActions} from "../core/growl/growl-actions"; + + +@Component({ + selector: 'opentosca-situation', + templateUrl: './situation.component.html', + styleUrls: ['./situation.component.scss'], +}) + +/** + * This class contains the management of situations, aggregated situations and situation triggers. It also contains the + * situational execution of management plans. + * + * ! Note that a new version of the container must be used because aggregated situations did not exist. + * + * @author Lavinia Stiliadou + */ +export class SituationComponent implements OnInit { + + // select the containerURL from the administration component + @select(['administration', 'containerUrl']) administrationItems$: Observable>; + + administrationItems: Array = []; + selectedRepository: string; + + console = console; + + public instanceId: string; + + situations: Array; + csars: Csar[]; + csar2interface: Map = new Map(); + csar2instance: Map = new Map(); + situationtriggers: Array; + aggregatedSituations = new Array(); + + + hiddenParams: string[]; + + cols; + colsOfTriggers; + colsOfAggregatedSituations; + + //inputs and selection + situationTemplateInput: string; + situationThingInput: string; + situationStateInput: boolean = false; + + selectableSituations: Situation[]; + selectedSituations: Situation[]; + selectOnActivation: boolean = false; + selectSingleInstance: boolean = false; + selectedCsar: Csar; + selectedInstance : ServiceTemplateInstance; + selectedInterface: Interface; + selectedOperation: Operation; + + /** + * The constructor of the class. + * + * @param ngRedux to dispatch the feedback + * @param http for the communication with the Situation API + * @param appService to access the normal execution of management plans + */ + constructor(private ngRedux: NgRedux, private http: HttpClient, private appService: ApplicationManagementService,private instancesService: ApplicationInstanceManagementService) { } + + /** + * Initialize the table for situations, aggregated situations and situationtriggers. + */ + ngOnInit(): void { + this.administrationItems$.subscribe(items => { let temp = ""; this.administrationItems = items; temp = temp + items }); + + // sets the selectedRepository to the API Endpoint + this.selectedRepository = this.administrationItems + "/situationsapi"; + + this.hiddenParams = globals.hiddenElements; + + this.appService.getResolvedApplications().subscribe(value => { + this.csars = value; + this.csars.forEach(value => { + this.appService.getInterfaces(value.id).subscribe(val => this.csar2interface.set(value,val)); + this.instancesService.getServiceTemplateInstancesOfCsar(value).subscribe(val => this.csar2instance.set(value,Array.from(val.values()))); + }); + }); + + + + this.ngRedux.dispatch(BreadcrumbActions.updateBreadcrumb([ + { label: 'Situation', routerLink: ['/situation'] } + ])); + + + // situation table columns + this.cols = [ + { field: 'id', header: 'Situation ID', sortable: true }, + { field: 'situation_template_id', header: 'Situation Template ID', sortable: true }, + { field: 'active', header: 'active', sortable: true }, + { field: 'thing_id', header: 'Thing ID', sortable: true }, + { field: 'actions', header: 'actions', sortable: false } + ]; + + // aggregated situation table columns + this.colsOfAggregatedSituations = [ + { field: 'id', header: 'Aggregated Situation ID', sortable: true }, + { field: 'situation_ids', header: 'Situation IDs', sortable: true }, + { field: 'logic_expression', header: 'Logic Expression', sortable: true }, + { field: 'active', header: 'active', sortable: true }, + { field: 'actions', header: 'actions', sortable: false } + ]; + + // situationtrigger table columns + this.colsOfTriggers = [ + { field: 'id', header: 'Trigger ID', sortable: true }, + { field: 'situation_ids', header: 'Situation IDs', sortable: true }, + { field: 'csar_id', header: 'CSAR ID', sortable: true }, + { field: 'on_activation', header: 'active', sortable: true }, + { field: 'interface_name', header: 'Interface Name', sortable: true }, + { field: 'operation_name', header: 'Operation Name', sortable: true }, + { field: 'input_params', header: 'Input Parameter', sortable: true }, + { field: 'single_instance', header: 'Single Instance', sortable: true }, + { field: 'actions', header: 'actions', sortable: false } + ]; + + this.refresh(); + } + + /** + * Sends multiple @GET requests to the Situation API + */ + refresh(): void { + console.log('Starting to refresh situations and triggers'); + this.refreshSituations(); + //this.refreshAggrSituations(); + this.refreshTriggers(); + + //this.situations = new Array(); + //this.situationtriggers = new Array(); + //this.aggregatedSituations = new Array(); + } + + // situations + + + + /** + * Format the @GET response and adds the situations from the API to the table. + * @param jsonText + */ + editGetResponseSituations(jsonText: string): void { + this.situations = this.parseSituationsResponse(jsonText); + this.selectableSituations = this.situations; + } + + /** + * Format the @GET response and adds the situations from the API to the table. + * @param jsonText + */ + parseSituationsResponse(jsonText: string): Situation[] { + let o = jsonText; + while (o.includes('_links')) { + o = o.replace(",\"_links\"", ""); + } + let situations = o; + let obj = JSON.parse(situations); + let situationsA: Situation[] = []; + // number of situations + if (obj.situations != undefined) { + let length = obj.situations.length; + + // goes over all situations in the API and adds them to the table + for (let i = 0; i < length; i++) { + let situation = new Situation(); + situation.id = obj.situations[i].id; + situation.situation_template_id = obj.situations[i].situation_template_id; + situation.active = obj.situations[i].active; + situation.thing_id = obj.situations[i].thing_id; + situationsA.push(situation); + } + console.log('Fetched situations'); + return situationsA; + } + console.log('Couldnt fetch situations'); + return null; + } + + createSituation(){ + let check: boolean = true; + check = check && this.checkString(this.situationThingInput); + check = check && this.checkString(this.situationTemplateInput); + if(check) { + let sit: Situation = new Situation(); + sit.situation_template_id = this.situationTemplateInput; + sit.thing_id = this.situationThingInput; + sit.active = String(this.situationStateInput); + this.postSituation(sit); + } + } + + checkString(string: String): boolean { + if(string == "" || string == null) { + return false; + } else { + return true; + } + } + + /** + * Sends a @POST request to the API to create a new situation. + * @param situation + */ + postSituation(situation: Situation): void { + let post = this.http.post(this.selectedRepository + '/situations', { + situation_template_id: situation.situation_template_id, + thing_id: situation.thing_id, + active: situation.active + }); + + post.subscribe(() => { this.onCompletionSuccess('post'); this.refresh() }, + () => { this.onCompletionError('post'); this.refresh() }); + } + + + /** + * Changes the active from true/false to false/true. + * + * @param index column index where the active attribut need to be changed + */ + switchActive(index) { + let table = document.getElementById('sit'), + active; + for (let i = 1; i < table.getElementsByTagName("tr").length; i++) { + // sucht die Tabellenzeile, dessen erste Spalte dem Index der Situation entspricht + if (table.getElementsByTagName("tr").item(i).cells.item(0).innerText == index) { + active = table.getElementsByTagName("tr").item(i).cells.item(2).innerText; + if (active === 'true') { + active = 'false'; + } else { + active = 'true'; + } + table.getElementsByTagName("tr").item(i).cells.item(2).innerText = active; + }; + } + return active; + } + + + /** + * Format the @GET response and adds the aggregated situations from the API to the table. + * @param jsonText + */ + editGetResponseAggregatedSituations(jsonText: string): Array { + let o = jsonText; + while (o.includes('_links')) { + o = o.replace(",\"_links\"", ""); + } + let aggregated_situations = o; + let obj = JSON.parse(aggregated_situations); + + let aggregatedSituations = new Array(); + if (obj.aggregated_situations != undefined) { + // number of aggregated situations + let length = obj.aggregated_situations.length; + + + // goes over all aggregated situations in the API and adds them to the table + for (let i = 0; i < length; i++) { + let aggregatedsituation = new AggregatedSituation(); + aggregatedsituation.id = obj.aggregated_situations[i].id; + aggregatedsituation.situation_ids = obj.aggregated_situations[i].situation_ids; + aggregatedsituation.logic_expression = obj.aggregated_situations[i].logic_expression; + aggregatedsituation.active = obj.aggregated_situations[i].active; + + aggregatedSituations.push(aggregatedsituation); + } + + return aggregatedSituations; + } + return null; + + } + + /** + * Sends a @PUT request to the API to activate/deactivate a situation with the given id. + * + * @param id + */ + putSituation(id: string): void { + let active = this.switchActive(id); + let put = this.http.put(this.selectedRepository + '/situations/' + id.toString(), { + id: id, + active: active + }, { responseType: 'text' }); + put.subscribe(() => { + this.onCompletionSuccess(id + ',P'); this.refresh(); + }, () => { + this.onCompletionError(id + ',P'); this.refresh(); + }); + } + + /** + * Deletes the situation with the given id. + * + * @param id + */ + deleteSituation(id: string): void { + let deleteRequest = this.http.delete(this.selectedRepository + '/situations/' + id, {}); + deleteRequest.subscribe(() => { this.onCompletionSuccess(id + ',D'); this.refresh() }, + err => this.onCompletionError(id + ',D')); + } + + /** + * Deletes all situations. + */ + deleteAllSituations(): void { + let arrayOfSituationIds: string[] = new Array(); + this.situations.forEach(function (value) { + arrayOfSituationIds.push(value.id); + }) + for (let i = 0; i < arrayOfSituationIds.length; i++) { + let id = arrayOfSituationIds[i]; + this.deleteSituation(id); + } + } + + + /** + * Format the @GET response and adds the situationtrigger from the API to the table. + * @param jsonText + */ + editGetResponseTriggers(jsonText: string): Array { + let o = jsonText; + + console.log("Received following json:"); + console.log(jsonText); + + while (o.includes('_links')) { + o = o.replace(",\"_links\"", ""); + } + + let situation_triggers = o; + let obj = JSON.parse(situation_triggers); + + let situationtriggers = new Array(); + if (obj.situation_triggers != undefined) { + // number of situationtriggers + let length = obj.situation_triggers.length; + + for (let i = 0; i < length; i++) { + let trigger = new SituationTrigger(); + trigger.id = obj.situation_triggers[i].id; + trigger.situation_ids = obj.situation_triggers[i].situation_ids; + trigger.aggregated_situation_ids = obj.situation_triggers[i].aggregated_situations_ids; + trigger.csar_id = obj.situation_triggers[i].csar_id; + trigger.on_activation = obj.situation_triggers[i].on_activation; + trigger.interface_name = obj.situation_triggers[i].interface_name; + trigger.operation_name = obj.situation_triggers[i].operation_name; + trigger.single_instance = obj.situation_triggers[i].single_instance; + let lengthInput = obj.situation_triggers[i].input_params.length; + console.log("Inputparams length:"); + console.log(lengthInput); + let inputParameter = new Array(); + for (let j = 0; j < lengthInput; j++) { + let planParam = new PlanParameter(); + planParam.name = obj.situation_triggers[i].input_params[j].name; + planParam.type = obj.situation_triggers[i].input_params[j].type; + planParam.value = obj.situation_triggers[i].input_params[j].value; + inputParameter.push(planParam); + } + trigger.input_params = inputParameter; + console.log("Parsed following trigger:"); + console.log(trigger); + situationtriggers.push(trigger); + } + this.situationtriggers = situationtriggers; + return situationtriggers; + } else { + return null; + } + } + + /** + * Colors the row where the situationtrigger is activated. + * @param trigger + */ + colorTriggerRow(trigger: SituationTrigger) { + let table = document.getElementById("triggers"); + let colorCells; + + for (let i = 1; i < table.getElementsByTagName("tr").length; i++) { + if (table.getElementsByTagName("tr").item(i).cells.item(0).innerText === trigger.id) { + colorCells = table.getElementsByTagName("tr").item(i).cells; + for (let j = 0; j < colorCells.length; j++) { + table.getElementsByTagName("tr").item(i).cells.item(j).style.background = "#c1c3c5"; + } + }; + } + } + + /** + * Starts the situation-dependent execution of the plan if trigger active = all situations active. + * @param trigger + */ + activateTrigger(trigger: SituationTrigger, situations: Array, aggregated_situations: Array): boolean { + let active = trigger.on_activation; + let success = true; + let index; + + // situationtriggers must contain at least one situation or aggregated situation + if (trigger.aggregated_situation_ids.length == 0 && trigger.situation_ids.length == 0) { + return false; + } + + // check if trigger active = all situations active + for (let i = 0; i < trigger.situation_ids.length; i++) { + for (let j = 0; j < situations.length; j++) { + if (situations[j].id == trigger.situation_ids[i]) { + index = j; + if (situations[index].active.toString() != active) { + return false; + } + } + } + } + + // check if trigger active = all aggr situations active + for (let i = 0; i < trigger.aggregated_situation_ids.length; i++) { + for (let j = 0; j < aggregated_situations.length; j++) { + if (aggregated_situations[j].id == trigger.aggregated_situation_ids[i]) { + index = j; + if (aggregated_situations[index].active.toString() != active) { + return false; + } + } + } + } + + return success; + } + + + /** + * + * Returns the plan based on the operation and interface of the trigger. + * + * @param text response from the http Request to the interface + * @param trigger responsible for the situation-dependent execution + */ + getPlan(text: string, trigger: SituationTrigger) { + let o = text; + let obj = JSON.parse(o); + let operation_name = trigger.operation_name; + if (operation_name !== null) { + let temp = obj.operations[operation_name]._embedded.plan; + + let plan = new Plan(); + plan.id = temp.id; + plan.plan_type = temp.plan_type; + plan.plan_language = temp.plan_language; + for (let j = 0; j < trigger.input_params.length; j++) { + for (let i = 0; i < temp.input_parameters.length; i++) { + if (typeof trigger.input_params !== 'undefined') { + if (temp.input_parameters[i].name === trigger.input_params[j].name) { + temp.input_parameters[i].value = trigger.input_params[j].value; + } + } + } + } + plan.input_parameters = temp.input_parameters; + plan.output_parameters = temp.output_parameters; + plan.plan_model_reference = temp.plan_model_reference; + plan._links = temp._links; + + return plan; + } + } + + + + /** + * Sends a @GET requests to the Situation API. + */ + refreshSituations(): void { + this.http.get(this.selectedRepository + '/situations', { responseType: 'text' }).subscribe(response => this.editGetResponseSituations(response)); + } + /** + * Sends a @GET request to the Situation API to get all triggers. + */ + refreshTriggers(): void { + let triggers = this.http.get(this.selectedRepository + '/triggers', { responseType: 'text' }); + triggers.subscribe(response2 => this.editGetResponseTriggers(response2)); + } + + createSituationTrigger(): void { + let trigger: SituationTrigger = new SituationTrigger(); + trigger.situation_ids = new Array(); + this.selectedSituations.forEach(value => trigger.situation_ids.push(value.id)); + trigger.csar_id = this.selectedCsar.id; + trigger.interface_name = this.selectedInterface.name; + trigger.operation_name = this.selectedOperation.name; + trigger.single_instance = String(this.selectSingleInstance); + trigger.on_activation = String(this.selectOnActivation); + trigger.input_params = this.selectedOperation._embedded.plan.input_parameters; + + if(this.selectedInstance !== null && this.selectedInstance !== undefined) { + trigger.service_instance_id = String(this.selectedInstance.id); + } + + this.postSituationTrigger(trigger); + } + + resetSituationTriggerInput(): void { + this.selectedSituations = new Array(); + this.selectedCsar = null; + this.selectedInterface = null; + this.selectedOperation = null; + this.selectedInstance = null; + + } + + /** + * Sends a @POST request to the API to create a new situationtrigger. + * @param trigger + */ + postSituationTrigger(trigger: SituationTrigger): void { + let result = trigger.situation_ids; + + let post = this.http.post(this.selectedRepository + '/triggers', { + situation_ids: result, + //aggregatedsituation_ids: trigger.aggregated_situation_ids, + csar_id: trigger.csar_id, + interface_name: trigger.interface_name, + operation_name: trigger.operation_name, + input_params: trigger.input_params, + on_activation: trigger.on_activation, + service_instance_id: trigger.service_instance_id, + single_instance: trigger.single_instance + }); + post.subscribe(() => { this.onCompletionSuccess('post'); this.refresh() }, err => { this.onCompletionErrorTrigger('post'); this.refresh() }); + } + + /** + * Deletes the situationtrigger with the given id. + * + * @param id + */ + deleteTrigger(id: string): void { + let deleteRequest = this.http.delete(this.selectedRepository + '/triggers/' + id, {}); + deleteRequest.subscribe(() => { this.onCompletionSuccessTrigger('delete'); this.refresh() }, + err => { this.onCompletionErrorTrigger(id +',D'); this.refresh(); }); + } + + /** + * Deletes all situationtriggers. + */ + deleteAllTriggers(): void { + let arrayOfTriggerIds: string[] = new Array(); + this.situations.forEach(function (value) { + arrayOfTriggerIds.push(value.id); + }) + for (let i = 0; i < arrayOfTriggerIds.length; i++) { + let id = arrayOfTriggerIds[i]; + this.deleteTrigger(id); + } + } + + /** + * Starts the situation-dependent execution of the plan if trigger active = all situations active. + * @param trigger + */ + activate(trigger: SituationTrigger): void { + let success = this.activateTrigger(trigger, this.situations, this.aggregatedSituations); + if (success) { + this.selectPlan(trigger); + }else{ + this.onCompletionError('activate'); + } + } + + /** + * Executes the plan (based on the interface and operation) + * @param trigger + */ + selectPlan(trigger: SituationTrigger) { + this.instanceId = null; + + let csar_id = trigger.csar_id; + let interface_name = trigger.interface_name; + + let csarRepo = this.selectedRepository.replace('/situationsapi', '/csars/'); + if (csar_id !== '' && interface_name !== '') { + let temp = this.http.get(csarRepo + csar_id, { responseType: 'text' }); + temp.subscribe(response => { + let interfacesOfCSAR = this.getServiceTemplateCsar(response) + '/boundarydefinitions/interfaces/'+ interface_name; + let httpPlanRequest = this.http.get(interfacesOfCSAR, { responseType: 'text' }); + let plan : Plan; + httpPlanRequest.subscribe(response => { + plan = this.getPlan(response, trigger); + if (plan.plan_type.match('BuildPlan')) { + this.appService.triggerManagementPlan(plan, this.instanceId).subscribe(() => { this.colorTriggerRow(trigger) }, () => this.onCompletionErrorTrigger(plan.id +',P')); + } else { + if (typeof trigger.input_params !== 'undefined') { + for (let i = 0; i < trigger.input_params.length; i++) { + if (trigger.input_params[i].name === 'OpenTOSCAContainerAPIServiceInstanceURL') { + this.instanceId = trigger.input_params[i].value + } + } + this.appService.triggerManagementPlan(plan, this.instanceId).subscribe(() => { this.colorTriggerRow(trigger) }, () => this.onCompletionErrorTrigger(plan.id +',P')); + } + } + }, () => this.onCompletionErrorTrigger(interface_name + ',I')); + }, () => this.onCompletionErrorTrigger(csar_id + ',C')); + } + } + + + /** + * Returns the servicetemplate link from the CSAR. + * @param text + */ + getServiceTemplateCsar(text: any) { + let o = text; + o = JSON.parse(text); + return o._links.servicetemplate.href; + } + + /** + * Confirmation messages + * @param typeOfRequest @POST / @DELETE / @PUT + */ + onCompletionSuccess(typeOfRequest: string) { + let filterRequest = typeOfRequest.split(','); + if (typeOfRequest === 'post') { + this.ngRedux.dispatch(GrowlActions.addGrowl( + { + severity: 'success', + summary: 'Creation of situation ', + detail: `The situation is successfully created.` + } + )); + } else if (filterRequest[1] === 'D') { + this.ngRedux.dispatch(GrowlActions.addGrowl( + { + severity: 'success', + summary: 'Delete of situation ' + filterRequest[0], + detail: `The aggregated situations and the situationtriggers which contains the situation and the situation are successfully deleted.` + } + )); + } else { + this.ngRedux.dispatch(GrowlActions.addGrowl( + { + severity: 'success', + summary: 'Update of situation ' + filterRequest[0], + detail: `The situation is successfully updated.` + } + )); + } + + } + + /** + * Error messages + * @param typeOfRequest @POST / activateTrigger / @DELETE / @PUT + */ + onCompletionError(typeOfRequest: string): void { + let filterRequest = typeOfRequest.split(','); + if (typeOfRequest === 'post') { + this.ngRedux.dispatch(GrowlActions.addGrowl( + { + severity: 'error', + summary: 'Error at creation of situation', + detail: `The situation is not created.` + } + )); + } else if (typeOfRequest === 'activate') { + this.ngRedux.dispatch(GrowlActions.addGrowl( + { + severity: 'error', + summary: 'Error', + detail: 'The active attribute of the situation trigger and of it is situations and aggregated situations are not corresponding.' + } + )); + } else if (filterRequest[1] === 'D') { + this.ngRedux.dispatch(GrowlActions.addGrowl( + { + severity: 'error', + summary: 'Error at delete of situation', + detail: 'The aggregated situations which contains the situation and the situation are not deleted.' + } + )); + } else { + this.ngRedux.dispatch(GrowlActions.addGrowl( + { + severity: 'error', + summary: 'Error at update of situation' + typeOfRequest, + detail: `The situation is not updated.` + } + )); + } + } + + /** + * Confirmation messages + * @param typeOfRequest @POST / @DELETE / @PUT + */ + onCompletionSuccessAggregatedSituation(typeOfRequest: string) { + let filterRequest = typeOfRequest.split(','); + if (typeOfRequest === 'post') { + this.ngRedux.dispatch(GrowlActions.addGrowl( + { + severity: 'success', + summary: 'Creation of a aggregated situation ', + detail: `The aggregated situation is successfully created.` + } + )); + } else if (filterRequest[1] === 'D') { + this.ngRedux.dispatch(GrowlActions.addGrowl( + { + severity: 'success', + summary: 'Delete of aggregated situation ' + filterRequest[0], + detail: `The aggregated situation is successfully deleted and the corresponding situationtriggers are deleted.` + } + )); + } else { + this.ngRedux.dispatch(GrowlActions.addGrowl( + { + severity: 'success', + summary: 'Update of aggregated situation ' + filterRequest[0], + detail: `The aggregated situation is successfully updated.` + } + )); + } + } + + /** + * Error messages + * @param typeOfRequest @POST + */ + onCompletionErrorAggregatedSituation(typeOfRequest: string) { + let filterRequest = typeOfRequest.split(','); + if (typeOfRequest === 'post') { + this.ngRedux.dispatch(GrowlActions.addGrowl( + { + severity: 'error', + summary: 'Creation of aggregation situation failed', + detail: `The aggregated situation is not created.` + } + )); + } else if (filterRequest[1] === 'D') { + this.ngRedux.dispatch(GrowlActions.addGrowl( + { + severity: 'error', + summary: 'Delete of aggregation situation ' + filterRequest[0] + ' failed.', + detail: `The aggregated situation is not deleted.` + } + )); + } else { + this.ngRedux.dispatch(GrowlActions.addGrowl( + { + severity: 'error', + summary: 'Update of aggregation situation ' + filterRequest[0] + ' failed.', + detail: `The aggregated situation is not updated.` + } + )); + } + } + + /** + * Error messages if the input is invalid. + * @param failedInput + */ + failedAggregatedSituationTextInput(failedInput: string) { + if (failedInput === 'Expression') { + this.ngRedux.dispatch(GrowlActions.addGrowl( + { + severity: 'error', + summary: 'Invalid ' + failedInput, + detail: failedInput + ' does not correspond to the scheme NumberOperatorNumber.' + })); + } else if (failedInput === 'ExpressionSit') { + this.ngRedux.dispatch(GrowlActions.addGrowl( + { + severity: 'error', + summary: 'Invalid ' + failedInput, + detail: failedInput + ' contains situation ids which are not in situation ids.' + })); + } else { + this.ngRedux.dispatch(GrowlActions.addGrowl( + { + severity: 'error', + summary: 'Invalid ' + failedInput, + detail: failedInput + ' contains some unknown situation ids.' + })); + } + } + + /** + * Error messages + * @param typeOfRequest @POST / @DELETE + */ + onCompletionErrorTrigger(typeOfRequest: string) { + let filterRequest = typeOfRequest.split(','); + if (typeOfRequest === 'post') { + this.ngRedux.dispatch(GrowlActions.addGrowl( + { + severity: 'error', + summary: 'Creation of situationtrigger failed', + detail: `The situationtrigger is not created.` + } + )); + } else if (filterRequest[1] === 'C') { + this.ngRedux.dispatch(GrowlActions.addGrowl( + { + severity: 'error', + summary: 'Activation of situationtrigger failed', + detail: `The situationtrigger is not started because the CSAR ` + filterRequest[0] + ' not exists.' + } + )); + } else if (filterRequest[1] === 'I') { + this.ngRedux.dispatch(GrowlActions.addGrowl( + { + severity: 'error', + summary: 'Activation of situationtrigger failed', + detail: `The situationtrigger is not started because the Interface ` + filterRequest[0] + ' not exists.' + } + )); + } else if ( filterRequest[1] === 'D'){ + this.ngRedux.dispatch(GrowlActions.addGrowl( + { + severity: 'error', + summary: 'Delete of situationtrigger ' + filterRequest[0]+' failed', + detail: `The situationtrigger is not deleted.` + } + )); + }else{ + this.ngRedux.dispatch(GrowlActions.addGrowl( + { + severity: 'error', + summary: 'Execution of plan failed', + detail: 'The plan ' + filterRequest[0]+ ' is not executed.' + } + )); + } + } + + /** + * Confirmation messages + * @param typeOfRequest @POST / @DELETE + */ + onCompletionSuccessTrigger(typeOfRequest: string) { + if (typeOfRequest === 'post') { + this.ngRedux.dispatch(GrowlActions.addGrowl( + { + severity: 'success', + summary: 'Creation of situationtrigger', + detail: `The situationtrigger is successfully created.` + } + )); + } else { + this.ngRedux.dispatch(GrowlActions.addGrowl( + { + severity: 'success', + summary: 'Delete of situationtrigger', + detail: `The situationtrigger is successfully deleted.` + } + )); + } + } + + /** + * Error messages if the input is invalid + * @param failedInput + */ + failedSituationTriggerTextInput(failedInput: string) { + let filterInput = failedInput.split(','); + if (failedInput === 'Active' || failedInput === 'Single Instance') { + this.ngRedux.dispatch(GrowlActions.addGrowl( + { + severity: 'error', + summary: 'Invalid ' + failedInput, + detail: failedInput + ' must be false or true.' + })); + } else if (filterInput[1] === 'O') { + this.ngRedux.dispatch(GrowlActions.addGrowl( + { + severity: 'error', + summary: 'Creation of situationtrigger failed', + detail: 'The operation ' + filterInput[0] + ' not exists for the given interface.' + } + )); + } else if (failedInput === 'Situation IDs' || failedInput === 'Aggregated Situation IDs') { + this.ngRedux.dispatch(GrowlActions.addGrowl( + { + severity: 'error', + summary: 'Invalid ' + failedInput, + detail: 'Some ' + failedInput + ' are unknown' + })); + } else if (failedInput === 'Empty') { + this.ngRedux.dispatch(GrowlActions.addGrowl( + { + severity: 'error', + summary: 'Invalid Input', + detail: 'The Id of the situations and aggregated situations can never be empty at the same time.' + })); + } else if (failedInput === 'Value') { + this.ngRedux.dispatch(GrowlActions.addGrowl( + { + severity: 'error', + summary: 'Error', + detail: `The value of an input parameter can not be empty.` + } + )); + } else { + this.ngRedux.dispatch(GrowlActions.addGrowl( + { + severity: 'error', + summary: 'Invalid ' + failedInput, + detail: failedInput + ' can not be empty.' + })); + } + } + + +} diff --git a/src/app/situations/situation.module.ts b/src/app/situations/situation.module.ts new file mode 100644 index 00000000..c8790151 --- /dev/null +++ b/src/app/situations/situation.module.ts @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2018 University of Stuttgart. + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Apache Software License 2.0 + * which is available at https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 + */ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { SituationComponent } from './situation.component'; +import { RouterModule } from '@angular/router'; +import { NgSpinKitModule } from 'ng-spin-kit'; +import { + ButtonModule, CardModule, CheckboxModule, ConfirmDialogModule, DialogModule, + DropdownModule, ScrollPanelModule, ToolbarModule, FieldsetModule, + TooltipModule, MultiSelect, MultiSelectModule, ToggleButtonModule, InputTextModule +} from 'primeng/primeng'; +import { TableModule } from 'primeng/table'; +import { FormsModule } from '@angular/forms'; + + +@NgModule({ + imports: [ + CommonModule, + CheckboxModule, + FormsModule, + RouterModule, + TooltipModule, + TableModule, + FieldsetModule, + CardModule, + ButtonModule, + ScrollPanelModule, + ToolbarModule, + DropdownModule, + DialogModule, + NgSpinKitModule, + ConfirmDialogModule, + MultiSelectModule, + ToggleButtonModule, + InputTextModule + ], + declarations: [SituationComponent], +}) +export class SituationModule { +} + +