diff --git a/build/tools/changelog.sh b/build/tools/changelog.sh index d8d4158d26..ee7301728f 100755 --- a/build/tools/changelog.sh +++ b/build/tools/changelog.sh @@ -88,6 +88,7 @@ BREAKING_CHANGES="+label:breaking-change" mv ${CHANGELOG} CHANGELOG.old + echo "" echo -e "${CYAN}${BOLD}Generating Change log - content for version ${MILESTONE} will be shown below" echo -e "---------------------------------------------------------------------${RESET}" diff --git a/deploy/ci/build-aio-image-canary.yml b/deploy/ci/build-aio-image-canary.yml index cc19412f7e..5e64ab6bcf 100644 --- a/deploy/ci/build-aio-image-canary.yml +++ b/deploy/ci/build-aio-image-canary.yml @@ -73,6 +73,7 @@ jobs: tag: stratos/deploy/ci/tasks/build-images/canary-tag tag_as_latest: false labels_file: image-tag/image-labels + squash: true build_args_file: image-tag/ui-build-args build_args: CANARY_BUILD: true diff --git a/deploy/ci/tasks/dev-releases/create-chart-helper.sh b/deploy/ci/tasks/dev-releases/create-chart-helper.sh index a08c7e13f6..2c8b5218f1 100644 --- a/deploy/ci/tasks/dev-releases/create-chart-helper.sh +++ b/deploy/ci/tasks/dev-releases/create-chart-helper.sh @@ -81,8 +81,8 @@ patchHelmChartAppVersion() { local CHART_PATH=$1 local STRATOS_FOLDER=$2 - if [ -f "${STRATOS_FOLDER}/custom-src/stratos.yaml" ]; then - PROD_VERSION=$(cat "${STRATOS_FOLDER}/custom-src/stratos.yaml" | grep "productVersion") + if [ -f "${STRATOS_FOLDER}/stratos.yaml" ]; then + PROD_VERSION=$(cat "${STRATOS_FOLDER}/stratos.yaml" | grep "productVersion") if [ ! -z "${PROD_VERSION}" ]; then PROD_VERSION=$(echo $PROD_VERSION | grep --extended --only-matching '[0-9\.]+') if [ ! -z "${PROD_VERSION}" ]; then diff --git a/deploy/ci/tasks/dev-releases/create-chart.yml b/deploy/ci/tasks/dev-releases/create-chart.yml index 043e6be351..9cb8935fd7 100644 --- a/deploy/ci/tasks/dev-releases/create-chart.yml +++ b/deploy/ci/tasks/dev-releases/create-chart.yml @@ -38,9 +38,9 @@ run: updateHelmDependency # Run customization script if there is one - if [ -f "${STRATOS}/custom-src/deploy/kubernetes/customize-helm.sh" ]; then + if [ -f "${STRATOS}/deploy/kubernetes/custom/customize-helm.sh" ]; then echo "Applying Helm Chart customizations" - ${STRATOS}/custom-src/deploy/kubernetes/customize-helm.sh "${STRATOS}/deploy/kubernetes/console" + ${STRATOS}/deploy/kubernetes/custom/customize-helm.sh "${STRATOS}/deploy/kubernetes/console" fi # Generate imagelist diff --git a/deploy/common-build.sh b/deploy/common-build.sh index 2e8906adf4..913c159a17 100644 --- a/deploy/common-build.sh +++ b/deploy/common-build.sh @@ -43,7 +43,7 @@ function buildAndPublishImage { # Proxy support # Remove intermediate containers after a successful build -BUILD_ARGS="--rm=true --squash" +BUILD_ARGS="--rm=true" RUN_ARGS="" if [ -n "${http_proxy:-}" -o -n "${HTTP_PROXY:-}" ]; then BUILD_ARGS="${BUILD_ARGS} --build-arg http_proxy=${http_proxy:-${HTTP_PROXY}}" @@ -54,6 +54,15 @@ if [ -n "${https_proxy:-}" -o -n "${HTTPS_PROXY:-}" ]; then RUN_ARGS="${RUN_ARGS} -e https_proxy=${https_proxy:-${HTTPS_PROXY}}" fi +# Check if we can squash +CAN_SQUASH=$(docker info 2>&1 | grep "Experimental: true" -c | cat) +if [ "${CAN_SQUASH}" == "1" ]; then + BUILD_ARGS="${BUILD_ARGS} --squash" + echo "Images will be squashed" +else + echo "Images will NOT be squashed" +fi + # Use correct sed command for Mac SED="sed -r" unamestr=`uname` @@ -104,7 +113,6 @@ function cleanup { echo "-- Cleaning up ${STRATOS_PATH}" rm -rf ${STRATOS_PATH}/dist rm -rf ${STRATOS_PATH}/node_modules - rm -rf ${STRATOS_PATH}/bower_components echo echo "-- Cleaning up ${STRATOS_PATH}/deploy/containers/nginx/dist" rm -rf ${STRATOS_PATH}/deploy/containers/nginx/dist diff --git a/deploy/kubernetes/build.sh b/deploy/kubernetes/build.sh index 63cc4a0f63..f838cc3549 100755 --- a/deploy/kubernetes/build.sh +++ b/deploy/kubernetes/build.sh @@ -137,8 +137,8 @@ __DIRNAME="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" STRATOS_PATH=${__DIRNAME}/../../ source "${STRATOS_PATH}/deploy/common-build.sh" -if [ -f "${STRATOS_PATH}/custom-src/deploy/kubernetes/custom-build.sh" ]; then - source "${STRATOS_PATH}/custom-src/deploy/kubernetes/custom-build.sh" +if [ -f "${STRATOS_PATH}/deploy/kubernetes/custom/custom-build.sh" ]; then + source "${STRATOS_PATH}/deploy/kubernetes/custom/custom-build.sh" HAS_CUSTOM_BUILD="true" fi @@ -169,11 +169,10 @@ function patchDockerfile { if [ "${DOCKER_REG_DEFAULTS}" == "false" ]; then sed -i.bak "s@splatform@${DOCKER_REGISTRY}/${DOCKER_ORG}@g" "${FOLDER}/${PATCHED_DOCKER_FILE}" fi - sed -i.bak "s/opensuse/${BASE_IMAGE_TAG}/g" "${FOLDER}/${PATCHED_DOCKER_FILE}" + sed -i.bak "s/leap15_1/${BASE_IMAGE_TAG}/g" "${FOLDER}/${PATCHED_DOCKER_FILE}" popd > /dev/null 2>&1 } - # # MAIN ------------------------------------------------------------------------------------------- # @@ -184,7 +183,9 @@ popd > /dev/null 2>&1 echo "Base path: ${STRATOS_PATH}" # cleanup output, intermediate artifacts -cleanup +if [ "${CHART_ONLY}" == "false" ]; then + cleanup +fi # Clean any old patched docker files left if previously errored # rm -rf ${STRATOS_PATH}/deploy/Dockerfile.*.patched @@ -237,9 +238,9 @@ rm -rf "${DEST_HELM_CHART_PATH}/**/*.orig" # Run customization script if there is one # This can do things like provide a custom __stratos.tpl file -if [ -f "${STRATOS_PATH}/custom-src/deploy/kubernetes/customize-helm.sh" ]; then +if [ -f "${STRATOS_PATH}/deploy/kubernetes/custom/customize-helm.sh" ]; then printf "${YELLOW}${BOLD}Applying Helm Chart customizations${RESET}\n" - "${STRATOS_PATH}/custom-src/deploy/kubernetes/customize-helm.sh" "${DEST_HELM_CHART_PATH}" + "${STRATOS_PATH}/deploy/kubernetes/custom/customize-helm.sh" "${DEST_HELM_CHART_PATH}" fi # Fetch subcharts diff --git a/deploy/kubernetes/console/Chart.yaml b/deploy/kubernetes/console/Chart.yaml index fcf011b8c0..22538c426e 100644 --- a/deploy/kubernetes/console/Chart.yaml +++ b/deploy/kubernetes/console/Chart.yaml @@ -5,4 +5,4 @@ version: 0.1.0 appVersion: 0.1.0 sources: - https://github.com/cloudfoundry/stratos -icon: https://raw.githubusercontent.com/cloudfoundry/stratos/master/deploy/kubernetes/icon.png \ No newline at end of file +icon: https://raw.githubusercontent.com/cloudfoundry/stratos/master/deploy/kubernetes/console/icon.png \ No newline at end of file diff --git a/deploy/kubernetes/imagelist-gen.sh b/deploy/kubernetes/imagelist-gen.sh index 77db87f409..a4bf5e5156 100755 --- a/deploy/kubernetes/imagelist-gen.sh +++ b/deploy/kubernetes/imagelist-gen.sh @@ -12,6 +12,22 @@ __DIRNAME="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" printf "${BOLD}${CYAN}Generating ${YELLOW}imagelist.txt${RESET}\n" echo "" +STRATOS_FOLDER="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && cd ../../ && pwd )" + +# Add any customizations +function addCustomizations() { + + if [ -f "${STRATOS_FOLDER}/deploy/kubernetes/custom/imagelist.txt" ];then + echo "Including custom imagelist contents" + cat "${STRATOS_FOLDER}/deploy/kubernetes/custom/imagelist.txt" >> ./imagelist.txt + + # Update version number + VERSION=$(grep -Po 'consoleVersion: \K(.*)' ./values.yaml) + echo "Image Version: ${VERSION}" + sed -i 's/_VERSION_/'"${VERSION}"'/g' imagelist.txt + fi +} + CHART_FOLDER=${1} @@ -41,6 +57,11 @@ if [ $? -ne 0 ]; then echo -e "${BOLD}${RED}ERROR: Failed to render Helm Chart in order to generate image list" exit 1 fi + +# Add any customizations to the image list +# Mainly used if there are unreferenced images that need to be included +addCustomizations + popd > /dev/null printf "${CYAN}" diff --git a/examples/custom-src/frontend/assets/custom/acme_logo.png b/examples/custom-src/frontend/assets/custom/acme_logo.png deleted file mode 100644 index 397545f790..0000000000 Binary files a/examples/custom-src/frontend/assets/custom/acme_logo.png and /dev/null differ diff --git a/examples/custom-src/frontend/assets/eula.html b/examples/custom-src/frontend/assets/eula.html deleted file mode 100644 index 1f2241fe1c..0000000000 --- a/examples/custom-src/frontend/assets/eula.html +++ /dev/null @@ -1,5 +0,0 @@ -

AMCE Corp End User License Agreement

- -

PLEASE READ THIS AGREEMENT CAREFULLY.

- -

This is an exmaple EULA.

diff --git a/examples/custom-src/frontend/assets/login-bg.jpg b/examples/custom-src/frontend/assets/login-bg.jpg deleted file mode 100644 index 2b2e8e42f7..0000000000 Binary files a/examples/custom-src/frontend/assets/login-bg.jpg and /dev/null differ diff --git a/examples/custom-src/frontend/assets/logo.png b/examples/custom-src/frontend/assets/logo.png deleted file mode 100644 index 8129763913..0000000000 Binary files a/examples/custom-src/frontend/assets/logo.png and /dev/null differ diff --git a/examples/custom-src/frontend/favicon.ico b/examples/custom-src/frontend/favicon.ico deleted file mode 100644 index 60f452c054..0000000000 Binary files a/examples/custom-src/frontend/favicon.ico and /dev/null differ diff --git a/examples/custom-src/frontend/loading.css b/examples/custom-src/frontend/loading.css deleted file mode 100644 index 9633b127db..0000000000 --- a/examples/custom-src/frontend/loading.css +++ /dev/null @@ -1,38 +0,0 @@ -.stratos-initial-load-spinner-container { - align-items: center; - background-color: #0D2C40; - display: flex; - justify-content: center; - height: 100%; - left: 0; - position: absolute; - top: 0; - width: 100%; -} - -.stratos-initial-load-spinner-logo { - position: absolute; - height: 60px; -} - -.stratos-initial-load-spinner { - animation: stratos-initial-load-animate 1s infinite linear; - box-sizing: border-box; - border: 5px solid rgba(86, 107, 121, 0.45); - border-radius: 50%; - border-top-color: #00B2E2; - display: inline-block; - height: 128px; - margin: 0 auto; - width: 128px; -} - -@keyframes stratos-initial-load-animate { - 0% { - transform: rotate(0deg); - } - - 100% { - transform: rotate(359deg); - } -} diff --git a/examples/custom-src/frontend/loading.html b/examples/custom-src/frontend/loading.html deleted file mode 100644 index cf66894e7b..0000000000 --- a/examples/custom-src/frontend/loading.html +++ /dev/null @@ -1,3 +0,0 @@ -
- -
\ No newline at end of file diff --git a/examples/custom-src/frontend/sass/custom.scss b/examples/custom-src/frontend/sass/custom.scss deleted file mode 100644 index e3eb84c38a..0000000000 --- a/examples/custom-src/frontend/sass/custom.scss +++ /dev/null @@ -1,18 +0,0 @@ -// Custom ACME Theme - -// ACME Primary Material Design pallette -// From http://mcg.mbitson.com/#!?mcgpalette0=%233f51b5 -$acme-primary: (50: #e8eaf6, 100: #c5cbe9, 200: #9fa8da, 300: #7985cb, 400: #5c6bc0, 500: #3f51b5, 600: #394aae, 700: #3140a5, 800: #29379d, 900: #1b278d, A100: #c6cbff, A200: #939dff, A400: #606eff, A700: #4757ff, contrast: ( 50: #000000, 100: #000000, 200: #000000, 300: #000000, 400: #ffffff, 500: #ffffff, 600: #ffffff, 700: #ffffff, 800: #ffffff, 900: #ffffff, A100: #000000, A200: #000000, A400: #ffffff, A700: #ffffff, )); -$mat-red: ( 50: #ffebee, 100: #ffcdd2, 200: #ef9a9a, 300: #e57373, 400: #ef5350, 500: #f44336, 600: #e53935, 700: #d32f2f, 800: #c62828, 900: #b71c1c, A100: #ff8a80, A200: #ff5252, A400: #ff1744, A700: #d50000, contrast: ( 50: $black-87-opacity, 100: $black-87-opacity, 200: $black-87-opacity, 300: $black-87-opacity, 400: $black-87-opacity, 500: white, 600: white, 700: white, 800: $white-87-opacity, 900: $white-87-opacity, A100: $black-87-opacity, A200: white, A400: white, A700: white, )); - -// Common -$acme-theme-primary: mat-palette($acme-primary); -$acme-theme-warn: mat-palette($mat-red); - -// Dark Theme -$stratos-dark-theme: mat-dark-theme($acme-theme-primary, $acme-theme-primary, $acme-theme-warn); - -// Default Theme -$stratos-theme: mat-light-theme($acme-theme-primary, $acme-theme-primary, $acme-theme-warn); - -@import 'custom/acme'; diff --git a/examples/custom-src/frontend/sass/custom/acme-colors.scss b/examples/custom-src/frontend/sass/custom/acme-colors.scss deleted file mode 100644 index 878c2cda32..0000000000 --- a/examples/custom-src/frontend/sass/custom/acme-colors.scss +++ /dev/null @@ -1,7 +0,0 @@ -$acme-primary: #00c081; -$acme-secondary: #00243e; -$acme-text: #fff; -$acme-text-gray: #ccc; -$acme-button-gray: #888; -$acme-dark-blue: #06253a; -$acme-blue: #073155; diff --git a/examples/custom-src/frontend/sass/custom/acme.scss b/examples/custom-src/frontend/sass/custom/acme.scss deleted file mode 100644 index 75794ebb6e..0000000000 --- a/examples/custom-src/frontend/sass/custom/acme.scss +++ /dev/null @@ -1,71 +0,0 @@ -// Style overrides -// Acme Brand primary colour -$acme-primary-color: #035a97; - -// Secondary blue colour -$acme-secondary: #035a97; -$acme-text: #fff; -$acme-text-gray: #ccc; -$acme-dark-blue: #06253a; -$acme-blue: #073155; -$acme-side-nav: $acme-secondary; -$acme-side-nav-active: #003358; - -body.stratos { - app-page-subheader { - .page-subheader { - background-color: #fff; - color: #333; - } - } - - .page-header__divider { - color: #333; - } - - .page-header__menu-button-icon { - background-color: $acme-primary-color; - color: $acme-text; - } - - .mat-tab-nav-bar.mat-primary.mat-background-primary { - .mat-tab-links { - background-color: #fff; - color: #333; - } - .mat-tab-link { - color: #333; - } - .mat-ink-bar { - background-color: $acme-primary-color; - } - } - - .stratos-title__logo { - width: 160px; - } - - .page-header .mat-toolbar.mat-primary { - background-color: #fff; - color: #333; - } - - // Use Acme Blue Side Navigation - .side-nav, - .side-nav__top, - .side-nav__item--active:hover { - background-color: $acme-side-nav; - } - .side-nav__item--active, - .side-nav__item:hover { - background-color: $acme-side-nav-active; - } - .side-nav__bottom { - color: $acme-text-gray; - } - - // Show the text Stratos beneath the Acme logo - .stratos-title > .stratos-title__header { - display: inline-block; - } -} diff --git a/examples/custom-src/stratos.yaml b/examples/custom-src/stratos.yaml deleted file mode 100644 index d891b8a09c..0000000000 --- a/examples/custom-src/stratos.yaml +++ /dev/null @@ -1,2 +0,0 @@ -# Change the page title of the Stratos App -title: "ACME Stratos" \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index db8663255f..30cd6d1ef7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7330,7 +7330,7 @@ }, "duplexer": { "version": "0.1.1", - "resolved": "http://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=", "dev": true }, @@ -8033,7 +8033,7 @@ }, "event-stream": { "version": "3.3.4", - "resolved": "http://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", + "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", "integrity": "sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE=", "dev": true, "requires": { @@ -11141,6 +11141,66 @@ "integrity": "sha512-nCeAiw37MIMA9w9IXso7bRaLl+c/ef3wnxsoSAlYrzS+Ot0zTG6nU8G/cIfGkqpkjX2wNaIW9RFG0TwIFnG6bA==", "dev": true }, + "jasmine-protractor-browser-log-reporter": { + "version": "github:cf-stratos/jasmine-protractor-browser-log-reporter#5c9170221db6562f6f2eb128f0652e1c3cf2d668", + "from": "github:cf-stratos/jasmine-protractor-browser-log-reporter", + "dev": true, + "requires": { + "chalk": "^4.1.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "jasmine-spec-reporter": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/jasmine-spec-reporter/-/jasmine-spec-reporter-5.0.1.tgz", @@ -12416,7 +12476,7 @@ }, "map-stream": { "version": "0.1.0", - "resolved": "http://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", "integrity": "sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ=", "dev": true }, @@ -15236,7 +15296,7 @@ }, "pause-stream": { "version": "0.0.11", - "resolved": "http://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", + "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=", "dev": true, "requires": { @@ -16342,49 +16402,6 @@ } } }, - "protractor-console": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/protractor-console/-/protractor-console-3.0.0.tgz", - "integrity": "sha512-2BTh751CMjEAMxuZXb86jvs0TDWjvCk7fCnKTyb5vX/KE5f+olTeVCmcFm+4Aretpc6q/6yryuSJ8wjgL9QTKw==", - "dev": true, - "requires": { - "chalk": "^1.1.0", - "lodash": "^3.10.0" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": 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" - } - }, - "lodash": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", - "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=", - "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 - } - } - }, "proxy-addr": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", @@ -18825,7 +18842,7 @@ }, "split": { "version": "0.3.3", - "resolved": "http://registry.npmjs.org/split/-/split-0.3.3.tgz", + "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", "integrity": "sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=", "dev": true, "requires": { @@ -19008,7 +19025,7 @@ }, "stream-combiner": { "version": "0.0.4", - "resolved": "http://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", + "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", "integrity": "sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ=", "dev": true, "requires": { diff --git a/src/frontend/packages/cloud-foundry/src/shared/components/list/list-types/app-variables/table-cell-edit-variable/table-cell-edit-variable.component.html b/src/frontend/packages/cloud-foundry/src/shared/components/list/list-types/app-variables/table-cell-edit-variable/table-cell-edit-variable.component.html index 9b9fcb9301..3f634c71e4 100644 --- a/src/frontend/packages/cloud-foundry/src/shared/components/list/list-types/app-variables/table-cell-edit-variable/table-cell-edit-variable.component.html +++ b/src/frontend/packages/cloud-foundry/src/shared/components/list/list-types/app-variables/table-cell-edit-variable/table-cell-edit-variable.component.html @@ -2,4 +2,5 @@ -{{row.value}} \ No newline at end of file +{{row.value}} \ No newline at end of file diff --git a/src/frontend/packages/cloud-foundry/src/shared/components/list/list-types/app-variables/table-cell-edit-variable/table-cell-edit-variable.component.scss b/src/frontend/packages/cloud-foundry/src/shared/components/list/list-types/app-variables/table-cell-edit-variable/table-cell-edit-variable.component.scss index eb9c8d43a8..6cd7d23bf0 100644 --- a/src/frontend/packages/cloud-foundry/src/shared/components/list/list-types/app-variables/table-cell-edit-variable/table-cell-edit-variable.component.scss +++ b/src/frontend/packages/cloud-foundry/src/shared/components/list/list-types/app-variables/table-cell-edit-variable/table-cell-edit-variable.component.scss @@ -7,3 +7,7 @@ mat-form-field { .cell-edit-variable { width: 100%; } + +.cell-value-variable { + cursor: pointer; +} diff --git a/src/frontend/packages/cloud-foundry/src/shared/components/list/list-types/cf-quotas/cf-quotas-data-source.service.ts b/src/frontend/packages/cloud-foundry/src/shared/components/list/list-types/cf-quotas/cf-quotas-data-source.service.ts index 1771755cb0..ef96d027fc 100644 --- a/src/frontend/packages/cloud-foundry/src/shared/components/list/list-types/cf-quotas/cf-quotas-data-source.service.ts +++ b/src/frontend/packages/cloud-foundry/src/shared/components/list/list-types/cf-quotas/cf-quotas-data-source.service.ts @@ -49,7 +49,7 @@ export class CfQuotasDataSourceService extends ListDataSource { map(requestInfo => ({ deleting: requestInfo.deleting.busy, error: requestInfo.deleting.error, - message: requestInfo.deleting.error ? `Failed to delete quota: ${requestInfo.message}` : null + message: requestInfo.deleting.error ? `Failed to delete quota: ${requestInfo.deleting.message}` : null })) ); }; diff --git a/src/frontend/packages/cloud-foundry/src/shared/components/list/list-types/cf-space-quotas/cf-space-quotas-data-source.service.ts b/src/frontend/packages/cloud-foundry/src/shared/components/list/list-types/cf-space-quotas/cf-space-quotas-data-source.service.ts index cd9e0e00c1..fa308e0b71 100644 --- a/src/frontend/packages/cloud-foundry/src/shared/components/list/list-types/cf-space-quotas/cf-space-quotas-data-source.service.ts +++ b/src/frontend/packages/cloud-foundry/src/shared/components/list/list-types/cf-space-quotas/cf-space-quotas-data-source.service.ts @@ -50,7 +50,7 @@ export class CfOrgSpaceQuotasDataSourceService extends ListDataSource ({ deleting: requestInfo.deleting.busy, error: requestInfo.deleting.error, - message: requestInfo.deleting.error ? `Failed to delete space quota: ${requestInfo.message}` : null + message: requestInfo.deleting.error ? `Failed to delete space quota: ${requestInfo.deleting.message}` : null })) ); }; diff --git a/src/frontend/packages/core/sass/_all-theme.scss b/src/frontend/packages/core/sass/_all-theme.scss index ccbd01c43a..c611d62d9a 100644 --- a/src/frontend/packages/core/sass/_all-theme.scss +++ b/src/frontend/packages/core/sass/_all-theme.scss @@ -55,7 +55,6 @@ @import '../src/shared/components/favorites-global-list/favorites-global-list.component.theme'; @import '../src/shared/components/favorites-meta-card/favorites-meta-card.component.theme'; - @import '../../core/src/features/error-page/error-page/error-page.component.theme'; @import '../../core/src/features/endpoints/backup-restore/restore-endpoints/restore-endpoints.component.theme'; @import '../../core/src/features/metrics/metrics/metrics.component.theme'; diff --git a/src/frontend/packages/core/sass/components/mat-table.scss b/src/frontend/packages/core/sass/components/mat-table.scss index c623288894..6c0d902e78 100644 --- a/src/frontend/packages/core/sass/components/mat-table.scss +++ b/src/frontend/packages/core/sass/components/mat-table.scss @@ -12,7 +12,7 @@ $mat-table-header-paginator-font-size: 13px; .stratos { // Only put right padding between cells - .mat-cell, .mat-header-cell { + .mat-cell, mat-footer-cell, mat-header-cell { padding: 10px 10px 10px 0; } diff --git a/src/frontend/packages/core/src/core/utils.service.ts b/src/frontend/packages/core/src/core/utils.service.ts index a99ddf6708..d3d1c3f837 100644 --- a/src/frontend/packages/core/src/core/utils.service.ts +++ b/src/frontend/packages/core/src/core/utils.service.ts @@ -292,3 +292,11 @@ export const arraysEqual = (a: any[], b: any[]): boolean => { // Falsy/Truthy return false; }; + +export const createGuid = (): string => { + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => { + var r = Math.random() * 16 | 0, + v = c == 'x' ? r : (r & 0x3 | 0x8); + return v.toString(16); + }); +} diff --git a/src/frontend/packages/core/src/favicon.ico b/src/frontend/packages/core/src/favicon.ico deleted file mode 100644 index 21f49bc43b..0000000000 Binary files a/src/frontend/packages/core/src/favicon.ico and /dev/null differ diff --git a/src/frontend/packages/core/src/features/endpoints/create-endpoint/create-endpoint-base-step/create-endpoint-base-step.component.html b/src/frontend/packages/core/src/features/endpoints/create-endpoint/create-endpoint-base-step/create-endpoint-base-step.component.html index 0aa360488a..5a3116dce9 100644 --- a/src/frontend/packages/core/src/features/endpoints/create-endpoint/create-endpoint-base-step/create-endpoint-base-step.component.html +++ b/src/frontend/packages/core/src/features/endpoints/create-endpoint/create-endpoint-base-step/create-endpoint-base-step.component.html @@ -1,5 +1,5 @@ -

Register a new Endpoint

+

Register Endpoint

diff --git a/src/frontend/packages/core/src/features/endpoints/create-endpoint/create-endpoint-base-step/create-endpoint-base-step.component.ts b/src/frontend/packages/core/src/features/endpoints/create-endpoint/create-endpoint-base-step/create-endpoint-base-step.component.ts index 85fedb80c1..e354112437 100644 --- a/src/frontend/packages/core/src/features/endpoints/create-endpoint/create-endpoint-base-step/create-endpoint-base-step.component.ts +++ b/src/frontend/packages/core/src/features/endpoints/create-endpoint/create-endpoint-base-step/create-endpoint-base-step.component.ts @@ -1,16 +1,16 @@ import { Component } from '@angular/core'; import { Store } from '@ngrx/store'; +import { Observable } from 'rxjs'; import { first, map } from 'rxjs/operators'; import { RouterNav } from '../../../../../../store/src/actions/router.actions'; import { GeneralEntityAppState } from '../../../../../../store/src/app-state'; -import { selectSessionData } from '../../../../../../store/src/reducers/auth.reducer'; import { entityCatalog } from '../../../../../../store/src/entity-catalog/entity-catalog'; +import { IStratosEndpointDefinition } from '../../../../../../store/src/entity-catalog/entity-catalog.types'; +import { selectSessionData } from '../../../../../../store/src/reducers/auth.reducer'; import { BASE_REDIRECT_QUERY } from '../../../../shared/components/stepper/stepper.types'; import { TileConfigManager } from '../../../../shared/components/tile/tile-selector.helpers'; import { ITileConfig, ITileData } from '../../../../shared/components/tile/tile-selector.types'; -import { Observable } from 'rxjs'; -import { IStratosEndpointDefinition } from '../../../../../../store/src/entity-catalog/entity-catalog.types'; interface ICreateEndpointTilesData extends ITileData { type: string; @@ -103,7 +103,8 @@ export class CreateEndpointBaseStepComponent { }, { type: endpoint.type, - parentType: endpoint.parentType + parentType: endpoint.parentType, + component: endpoint.registrationComponent, } ); }); diff --git a/src/frontend/packages/core/src/features/endpoints/create-endpoint/create-endpoint.component.html b/src/frontend/packages/core/src/features/endpoints/create-endpoint/create-endpoint.component.html index d4a8beb761..076abff82d 100644 --- a/src/frontend/packages/core/src/features/endpoints/create-endpoint/create-endpoint.component.html +++ b/src/frontend/packages/core/src/features/endpoints/create-endpoint/create-endpoint.component.html @@ -1,8 +1,8 @@ -

Register a new Endpoint

+

Register Endpoint

- + @@ -11,4 +11,6 @@

Register a new Endpoint

[finishButtonText]="connect.doConnect ? 'Connect' : 'Finish'">
-
\ No newline at end of file + + + \ No newline at end of file diff --git a/src/frontend/packages/core/src/features/endpoints/create-endpoint/create-endpoint.component.ts b/src/frontend/packages/core/src/features/endpoints/create-endpoint/create-endpoint.component.ts index 1bd0f9c07c..d44c7af725 100644 --- a/src/frontend/packages/core/src/features/endpoints/create-endpoint/create-endpoint.component.ts +++ b/src/frontend/packages/core/src/features/endpoints/create-endpoint/create-endpoint.component.ts @@ -1,4 +1,4 @@ -import { Component } from '@angular/core'; +import { Component, ViewChild, ViewContainerRef, ComponentRef, OnInit, OnDestroy, ComponentFactory, ComponentFactoryResolver } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { entityCatalog } from '../../../../../store/src/entity-catalog/entity-catalog'; @@ -10,16 +10,40 @@ import { getIdFromRoute } from '../../../core/utils.service'; templateUrl: './create-endpoint.component.html', styleUrls: ['./create-endpoint.component.scss'] }) -export class CreateEndpointComponent { +export class CreateEndpointComponent implements OnInit, OnDestroy { showConnectStep: boolean; - constructor(activatedRoute: ActivatedRoute) { + component: any; + @ViewChild('customComponent', { read: ViewContainerRef, static: true }) customComponentContainer; + componentRef: ComponentRef; + + constructor(activatedRoute: ActivatedRoute, private resolver: ComponentFactoryResolver) { const epType = getIdFromRoute(activatedRoute, 'type'); const epSubType = getIdFromRoute(activatedRoute, 'subtype'); const endpoint = entityCatalog.getEndpoint(epType, epSubType); + + this.component = endpoint.definition.registrationComponent; this.showConnectStep = !endpoint.definition.unConnectable ? endpoint.definition.authTypes && !!endpoint.definition.authTypes.length : false; } + + ngOnInit() { + this.customComponentContainer.clear(); + if (this.componentRef) { + this.componentRef.destroy(); + } + if (this.component) { + const factory: ComponentFactory = this.resolver.resolveComponentFactory(this.component); + this.componentRef = this.customComponentContainer.createComponent(factory); + } + } + + ngOnDestroy() { + if (this.componentRef) { + this.componentRef.destroy(); + } + } + } diff --git a/src/frontend/packages/core/src/shared/components/app-action-monitor-icon/app-action-monitor-icon.component.ts b/src/frontend/packages/core/src/shared/components/app-action-monitor-icon/app-action-monitor-icon.component.ts index 6637ca8748..a093ea3d6d 100644 --- a/src/frontend/packages/core/src/shared/components/app-action-monitor-icon/app-action-monitor-icon.component.ts +++ b/src/frontend/packages/core/src/shared/components/app-action-monitor-icon/app-action-monitor-icon.component.ts @@ -14,7 +14,7 @@ export enum AppMonitorComponentTypes { FETCHING = 'MONITOR_FETCHING', } -export interface IApplicationMonitorComponentState { +export interface IActionMonitorComponentState { busy: boolean; error: boolean; completed: boolean; @@ -23,7 +23,7 @@ export interface IApplicationMonitorComponentState { export class ActionMonitorComponentState { - public currentState: Observable; + public currentState: Observable; constructor( private entityMonitorFactory: EntityMonitorFactory, @@ -36,7 +36,8 @@ export class ActionMonitorComponentState { this.currentState = this.getStateObservable(entityMonitor, monitorState); } - private getStateObservable(entityMonitor: EntityMonitor, monitorState: AppMonitorComponentTypes) { + private getStateObservable(entityMonitor: EntityMonitor, monitorState: AppMonitorComponentTypes) + : Observable { switch (monitorState) { case AppMonitorComponentTypes.DELETE: return this.getDeletingState(entityMonitor); @@ -49,7 +50,7 @@ export class ActionMonitorComponentState { } } - private getDeletingState(entityMonitor: EntityMonitor): Observable { + private getDeletingState(entityMonitor: EntityMonitor): Observable { return entityMonitor.entityRequest$.pipe( map(requestState => ({ busy: requestState.deleting.busy, @@ -60,7 +61,7 @@ export class ActionMonitorComponentState { ); } - private getFetchingState(entityMonitor: EntityMonitor): Observable { + private getFetchingState(entityMonitor: EntityMonitor): Observable { const completed$ = this.getHasCompletedObservable( entityMonitor.entityRequest$.pipe( map(requestState => requestState.fetching), @@ -81,7 +82,7 @@ export class ActionMonitorComponentState { private fetchUpdatingState = (requestState: RequestInfoState): ActionState => (requestState.updating[this.updateKey] || { busy: false, error: false, message: '' }) - private getUpdatingState(entityMonitor: EntityMonitor): Observable { + private getUpdatingState(entityMonitor: EntityMonitor): Observable { const completed$ = this.getHasCompletedObservable( @@ -122,6 +123,10 @@ export class ActionMonitorComponentState { }) export class AppActionMonitorIconComponent implements OnInit { + // State observable - use this instead of creating one + @Input() + public state: Observable; + @Input() public entityKey: string; @@ -138,18 +143,22 @@ export class AppActionMonitorIconComponent implements OnInit { public updateKey = rootUpdatingKey; @Output() - public currentState: Observable; + public currentState: Observable; constructor(private entityMonitorFactory: EntityMonitorFactory) { } ngOnInit() { - const state: ActionMonitorComponentState = new ActionMonitorComponentState( - this.entityMonitorFactory, - this.id, - this.schema, - this.monitorState, - this.updateKey - ); - this.currentState = state.currentState; + if (this.state) { + this.currentState = this.state; + } else { + const state: ActionMonitorComponentState = new ActionMonitorComponentState( + this.entityMonitorFactory, + this.id, + this.schema, + this.monitorState, + this.updateKey + ); + this.currentState = state.currentState; + } } } diff --git a/src/frontend/packages/core/src/shared/components/app-action-monitor/app-action-monitor.component.html b/src/frontend/packages/core/src/shared/components/app-action-monitor/app-action-monitor.component.html index 6e9a288393..a13bfa2bf3 100644 --- a/src/frontend/packages/core/src/shared/components/app-action-monitor/app-action-monitor.component.html +++ b/src/frontend/packages/core/src/shared/components/app-action-monitor/app-action-monitor.component.html @@ -1 +1,4 @@ - +
+ + +
\ No newline at end of file diff --git a/src/frontend/packages/core/src/shared/components/app-action-monitor/app-action-monitor.component.ts b/src/frontend/packages/core/src/shared/components/app-action-monitor/app-action-monitor.component.ts index 2895bce18d..84b00b4d77 100644 --- a/src/frontend/packages/core/src/shared/components/app-action-monitor/app-action-monitor.component.ts +++ b/src/frontend/packages/core/src/shared/components/app-action-monitor/app-action-monitor.component.ts @@ -1,8 +1,7 @@ -import { DataSource } from '@angular/cdk/table'; import { Component, Input, OnInit } from '@angular/core'; import { schema } from 'normalizr'; import { never as observableNever, Observable, of as observableOf } from 'rxjs'; -import { map } from 'rxjs/operators'; +import { map, publishReplay, refCount } from 'rxjs/operators'; import { EntitySchema } from '../../../../../store/src/helpers/entity-schema'; import { EntityMonitorFactory } from '../../../../../store/src/monitors/entity-monitor.factory.service'; @@ -26,7 +25,9 @@ import { ITableColumn } from '../list/list-table/table.types'; export class AppActionMonitorComponent implements OnInit { @Input() - private data$: Observable> = observableNever(); + public data$: Observable> = observableNever(); + + public replayData$: Observable>; @Input() public entityKey: string; @@ -58,7 +59,7 @@ export class AppActionMonitorComponent implements OnInit { @Input() public columns: ITableColumn[] = []; - public dataSource: DataSource; + public dataSource: ITableListDataSource; public allColumns: ITableColumn[] = []; @@ -82,9 +83,16 @@ export class AppActionMonitorComponent implements OnInit { cellFlex: '0 0 24px' }; + // Some data$ obs only ever emit once. If we subscribed directly to this then that emit would be consumed and will not be available + // in the data source connect subscription. So wrap it in a replay to ensure the last emitted value is available + this.replayData$ = this.data$.pipe( + publishReplay(1), + refCount() + ) + this.allColumns = [...this.columns, monitorColumn]; this.dataSource = { - connect: () => this.data$, + connect: () => this.replayData$, disconnect: () => { }, trackBy: (index, item) => { const fn = monitorColumn.cellConfig(item).getId; @@ -117,7 +125,7 @@ export class AppActionMonitorComponent implements OnInit { }) ); } - } as ITableListDataSource; + }; } diff --git a/src/frontend/packages/core/src/shared/components/file-input/file-input.component.html b/src/frontend/packages/core/src/shared/components/file-input/file-input.component.html index 195ae17f56..edef8e7b79 100644 --- a/src/frontend/packages/core/src/shared/components/file-input/file-input.component.html +++ b/src/frontend/packages/core/src/shared/components/file-input/file-input.component.html @@ -1,6 +1,6 @@
- +
No file selected
{{ name }}
@@ -10,4 +10,5 @@
+
\ No newline at end of file diff --git a/src/frontend/packages/core/src/shared/components/file-input/file-input.component.ts b/src/frontend/packages/core/src/shared/components/file-input/file-input.component.ts index a08b287010..b99327bd0a 100644 --- a/src/frontend/packages/core/src/shared/components/file-input/file-input.component.ts +++ b/src/frontend/packages/core/src/shared/components/file-input/file-input.component.ts @@ -28,8 +28,12 @@ export class FileInputComponent implements OnInit, OnDestroy { @Input() accept: string; @Output() onFileSelect: EventEmitter = new EventEmitter(); + @Output() onFileData: EventEmitter = new EventEmitter(); + @Input() fileFormControlName; + @Input() buttonLabel = ''; + private files: File[]; public name = ''; @@ -65,7 +69,9 @@ export class FileInputComponent implements OnInit, OnDestroy { this.onFileSelect.emit(this.files[0]); if (!!this.formGroupControl) { - this.handleFormControl(this.files[0]); + this.handleFileData(this.files[0], (value) => this.updateFileState(value)); + } else { + this.handleFileData(this.files[0], (value) => this.onFileData.emit(value)); } if (this.files.length > 0) { this.name = this.files[0].name; @@ -79,17 +85,18 @@ export class FileInputComponent implements OnInit, OnDestroy { return false; } - handleFormControl(file) { + handleFileData(file, done) { const reader = new FileReader(); reader.onload = () => { - this.updateFileState(reader.result); + done(reader.result); }; reader.onerror = () => { // Clear the form and thus make it invalid on error - this.updateFileState(null); + done(null); }; reader.readAsText(file); } + private updateFileState(value: string | ArrayBuffer) { this.formGroupControl.control.controls[this.fileFormControlName].setValue(value); } diff --git a/src/frontend/packages/core/src/shared/components/list/data-sources-controllers/list-data-source-types.ts b/src/frontend/packages/core/src/shared/components/list/data-sources-controllers/list-data-source-types.ts index 4b1af3bd34..96c4b0443e 100644 --- a/src/frontend/packages/core/src/shared/components/list/data-sources-controllers/list-data-source-types.ts +++ b/src/frontend/packages/core/src/shared/components/list/data-sources-controllers/list-data-source-types.ts @@ -64,11 +64,29 @@ interface ICoreListDataSource extends DataSource { trackBy(index: number, item: T); } -export interface ITableListDataSource extends ICoreListDataSource { +interface ICoreTableListDataSource extends ICoreListDataSource { + isTableLoading$?: Observable; + + selectAllChecked?: boolean; // Select items - remove once ng-content can exist in md-table + selectAllIndeterminate?: boolean; // Select all checkbox as indeterminate + selectedRows?: Map; // Select items - remove once ng-content can exist in md-table + selectedRows$?: ReplaySubject>; // Select items - remove once ng-content can exist in md-table + selectAllFilteredRows?: () => void; // Select items - remove once ng-content can exist in md-table + selectedRowToggle?: (row: T, multiMode?: boolean) => void; // Select items - remove once ng-content can exist in md-table + selectClear?: () => void; + + editRow?: T; // Edit items - remove once ng-content can exist in md-table + startEdit?: (row: T) => void; // Edit items - remove once ng-content can exist in md-table + saveEdit?: () => void; // Edit items - remove once ng-content can exist in md-table + cancelEdit?: () => void; // Edit items - remove once ng-content can exist in md-table + getRowUniqueId?: getRowUniqueId; +} + +export interface ITableListDataSource extends ICoreTableListDataSource { isTableLoading$: Observable; } -export interface IListDataSource extends ICoreListDataSource, EntityCatalogEntityConfig { +export interface IListDataSource extends ICoreListDataSource, ICoreTableListDataSource, EntityCatalogEntityConfig { pagination$: Observable; isLocal?: boolean; localDataFunctions?: (( @@ -94,20 +112,11 @@ export interface IListDataSource extends ICoreListDataSource, EntityCatalo filter$: Observable; sort$: Observable; - editRow: T; // Edit items - remove once ng-content can exist in md-table - selectAllChecked: boolean; // Select items - remove once ng-content can exist in md-table - selectedRows: Map; // Select items - remove once ng-content can exist in md-table - selectedRows$: ReplaySubject>; // Select items - remove once ng-content can exist in md-table + getRowUniqueId: getRowUniqueId; entitySelectConfig?: EntitySelectConfig; // For multi action lists, this is used to configure the entity select. - selectAllFilteredRows(); // Select items - remove once ng-content can exist in md-table - selectedRowToggle(row: T, multiMode?: boolean); // Select items - remove once ng-content can exist in md-table - selectClear(); - startEdit(row: T); // Edit items - remove once ng-content can exist in md-table - saveEdit(); // Edit items - remove once ng-content can exist in md-table - cancelEdit(); // Edit items - remove once ng-content can exist in md-table destroy(); /** * Set's data source specific text filter param diff --git a/src/frontend/packages/core/src/shared/components/list/list-table/table-cell-edit/table-cell-edit.component.html b/src/frontend/packages/core/src/shared/components/list/list-table/table-cell-edit/table-cell-edit.component.html index 125c4a88fc..7762b74c15 100644 --- a/src/frontend/packages/core/src/shared/components/list/list-table/table-cell-edit/table-cell-edit.component.html +++ b/src/frontend/packages/core/src/shared/components/list/list-table/table-cell-edit/table-cell-edit.component.html @@ -1,11 +1,14 @@ -
- - - -
+ + + + + \ No newline at end of file diff --git a/src/frontend/packages/core/src/shared/components/list/list-table/table-cell-edit/table-cell-edit.component.scss b/src/frontend/packages/core/src/shared/components/list/list-table/table-cell-edit/table-cell-edit.component.scss index 9681b2e526..e964f6220f 100644 --- a/src/frontend/packages/core/src/shared/components/list/list-table/table-cell-edit/table-cell-edit.component.scss +++ b/src/frontend/packages/core/src/shared/components/list/list-table/table-cell-edit/table-cell-edit.component.scss @@ -1,4 +1,14 @@ -div { +.edit { display: flex; justify-content: flex-end; + &--subtle { + .mat-icon-button { + font-size: 18px; + height: 25px; + line-height: 25px; + opacity: 60%; + padding-left: 5px; + width: 35px; + } + } } diff --git a/src/frontend/packages/core/src/shared/components/list/list-table/table-cell-edit/table-cell-edit.component.ts b/src/frontend/packages/core/src/shared/components/list/list-table/table-cell-edit/table-cell-edit.component.ts index 5f38b44101..1aa4769a4f 100644 --- a/src/frontend/packages/core/src/shared/components/list/list-table/table-cell-edit/table-cell-edit.component.ts +++ b/src/frontend/packages/core/src/shared/components/list/list-table/table-cell-edit/table-cell-edit.component.ts @@ -1,5 +1,6 @@ -/* tslint:disable:no-access-missing-member https://github.com/mgechev/codelyzer/issues/191*/ -import { Component, OnInit } from '@angular/core'; +import { Component, Input } from '@angular/core'; + +import { IListDataSource } from '../../data-sources-controllers/list-data-source-types'; import { TableCellCustom } from '../../list.types'; @Component({ @@ -7,4 +8,20 @@ import { TableCellCustom } from '../../list.types'; templateUrl: './table-cell-edit.component.html', styleUrls: ['./table-cell-edit.component.scss'] }) -export class TableCellEditComponent extends TableCellCustom { } +export class TableCellEditComponent extends TableCellCustom { + + @Input() + row: T; + + @Input() + dataSource: IListDataSource; + + @Input() + subtle: boolean; + + isEditing(): boolean { + return this.dataSource.editRow ? + this.dataSource.getRowUniqueId(this.row) === this.dataSource.getRowUniqueId(this.dataSource.editRow) : + false + } +} diff --git a/src/frontend/packages/core/src/shared/components/list/list-table/table-header-select/table-header-select.component.html b/src/frontend/packages/core/src/shared/components/list/list-table/table-header-select/table-header-select.component.html index c2f860861e..51c00db460 100644 --- a/src/frontend/packages/core/src/shared/components/list/list-table/table-header-select/table-header-select.component.html +++ b/src/frontend/packages/core/src/shared/components/list/list-table/table-header-select/table-header-select.component.html @@ -1,2 +1,3 @@ - - + + \ No newline at end of file diff --git a/src/frontend/packages/core/src/shared/components/list/list-table/table-row/table-row.component.html b/src/frontend/packages/core/src/shared/components/list/list-table/table-row/table-row.component.html index be2ec31af4..b715c964f2 100644 --- a/src/frontend/packages/core/src/shared/components/list/list-table/table-row/table-row.component.html +++ b/src/frontend/packages/core/src/shared/components/list/list-table/table-row/table-row.component.html @@ -1,5 +1,5 @@
+ [ngClass]="{'table-row-wrapper__blocked': isBlocked$ | async, 'table-row-wrapper__info': inInfoState$ | async, 'table-row-wrapper__warning': inWarningState$ | async,'table-row-wrapper__errored': inErrorState$ | async}">
Deleting
@@ -7,7 +7,7 @@
+ [ngClass]="{'in-expanded-row': !!inExpandedRow, 'has-expanded-row': expandComponent, 'has-error-row': errorMessage$ | async}">
- warning -
+
+
+ warning + info +
\ No newline at end of file diff --git a/src/frontend/packages/core/src/shared/components/list/list-table/table-row/table-row.component.scss b/src/frontend/packages/core/src/shared/components/list/list-table/table-row/table-row.component.scss index 06621f3b97..535edff498 100644 --- a/src/frontend/packages/core/src/shared/components/list/list-table/table-row/table-row.component.scss +++ b/src/frontend/packages/core/src/shared/components/list/list-table/table-row/table-row.component.scss @@ -16,7 +16,8 @@ } } &__errored, - &__warning { + &__warning, + &__info { .table-row__error { display: flex; } @@ -64,14 +65,23 @@ &__error { align-items: center; display: none; + &-message { flex: 1; line-height: 20px; - margin: 15px 36px; + margin-left: 10px; text-align: left; } - &-icon { - padding-left: 24px; + &-spacer { + align-self: stretch; + flex: 0 0 20px; + &__prominentErrorBar { + flex: 0 0 68px; + } + } + &-content { + flex: 1; + padding: 0 10px 10px 0; } } &__blocker { @@ -93,6 +103,7 @@ } .table-row__inner__expansion.mat-expansion-panel { + border-radius: unset; width: 100%; .table-row__inner__expansion--header { @@ -108,4 +119,9 @@ &.in-expanded-row { border-left-width: 1px; } + + &.has-error-row { + // Remove the bottom border if there's an error underneath it + border-bottom-width: 0; + } } diff --git a/src/frontend/packages/core/src/shared/components/list/list-table/table-row/table-row.component.theme.scss b/src/frontend/packages/core/src/shared/components/list/list-table/table-row/table-row.component.theme.scss index 0ac4538dad..07be004cb3 100644 --- a/src/frontend/packages/core/src/shared/components/list/list-table/table-row/table-row.component.theme.scss +++ b/src/frontend/packages/core/src/shared/components/list/list-table/table-row/table-row.component.theme.scss @@ -2,6 +2,7 @@ $status-colors: map-get($app-theme, status); $error-color: map-get($status-colors, danger); $warn-color: map-get($status-colors, warning); + $info-color: map-get($status-colors, info); $text-color: map-get($status-colors, text); $primary: map-get($theme, primary); $primary-color: mat-color($primary); @@ -22,26 +23,24 @@ } .table-row-wrapper { &__errored { - .table-row { - background-color: transparentize($error-color, .9); - } - .table-row__error { - background-color: $error-color; - color: $text-color; - } - .table-row__error-message { - a { - color: $text-color; + .table-row__error-content { + mat-icon { + color: $error-color; } } } &__warning { - .table-row { - background-color: transparentize($warn-color, .9); + .table-row__error-content { + mat-icon { + color: $warn-color; + } } - .table-row__error { - background-color: $warn-color; - color: $text-color; + } + &__info { + .table-row__error-content { + mat-icon { + color: $info-color; + } } } &__highlighted { diff --git a/src/frontend/packages/core/src/shared/components/list/list-table/table-row/table-row.component.ts b/src/frontend/packages/core/src/shared/components/list/list-table/table-row/table-row.component.ts index 1c3c08d120..2c8325d488 100644 --- a/src/frontend/packages/core/src/shared/components/list/list-table/table-row/table-row.component.ts +++ b/src/frontend/packages/core/src/shared/components/list/list-table/table-row/table-row.component.ts @@ -38,13 +38,16 @@ export class TableRowComponent extends CdkRow implements OnInit { @Input() minRowHeight: string; @Input() inExpandedRow: boolean; @Input() rowId: string; + @Input() prominentErrorBar: boolean; public inErrorState$: Observable; public inWarningState$: Observable; + public inInfoState$: Observable; public errorMessage$: Observable; public isBlocked$: Observable; public isHighlighted$: Observable; public isDeleting$: Observable; + public isWarningIcon$: Observable; public defaultMinRowHeight = '50px'; private expandedComponentRef: ComponentRef; @@ -64,6 +67,9 @@ export class TableRowComponent extends CdkRow implements OnInit { this.inWarningState$ = this.rowState.pipe( map(state => state.warning) ); + this.inInfoState$ = this.rowState.pipe( + map(state => state.info) + ); this.errorMessage$ = this.rowState.pipe( map(state => state.message) ); @@ -76,6 +82,9 @@ export class TableRowComponent extends CdkRow implements OnInit { this.isDeleting$ = this.rowState.pipe( map(state => state.deleting) ); + this.isWarningIcon$ = this.rowState.pipe( + map(state => state.error || state.warning) + ); } // Ensure we 'register' with the expander service. This also helps with page changes diff --git a/src/frontend/packages/core/src/shared/components/list/list-table/table.component.html b/src/frontend/packages/core/src/shared/components/list/list-table/table.component.html index 4bd2ce8004..fe4fa52887 100644 --- a/src/frontend/packages/core/src/shared/components/list/list-table/table.component.html +++ b/src/frontend/packages/core/src/shared/components/list/list-table/table.component.html @@ -37,7 +37,7 @@ + [rowId]="dataSource.trackBy(null, row)" [prominentErrorBar]="prominentErrorBar"> diff --git a/src/frontend/packages/core/src/shared/components/list/list-table/table.component.scss b/src/frontend/packages/core/src/shared/components/list/list-table/table.component.scss index 8c2db26e14..c2dc1fe36a 100644 --- a/src/frontend/packages/core/src/shared/components/list/list-table/table.component.scss +++ b/src/frontend/packages/core/src/shared/components/list/list-table/table.component.scss @@ -5,7 +5,6 @@ mat-cell, mat-header-cell { flex: 1 1 200px; - padding: 10px; app-table-cell { width: 100%; @@ -88,6 +87,19 @@ mat-header-cell { } } + &--table-centred { + app-table-cell { + align-items: center; + display: flex; + justify-content: center; + text-align: center; + } + } + + &--table-no-v-padding { + padding: 0 10px 0 0; + } + &--table-column-additional-padding { app-table-cell { padding-left: 15px; diff --git a/src/frontend/packages/core/src/shared/components/list/list-table/table.component.ts b/src/frontend/packages/core/src/shared/components/list/list-table/table.component.ts index 10f0637f6a..ab1af731f3 100644 --- a/src/frontend/packages/core/src/shared/components/list/list-table/table.component.ts +++ b/src/frontend/packages/core/src/shared/components/list/list-table/table.component.ts @@ -64,6 +64,7 @@ export class TableComponent implements OnInit, OnDestroy { public columnNames: string[]; @Input() minRowHeight: string; + @Input() prominentErrorBar: boolean = true; ngOnInit() { if (this.addSelect || this.expandComponent || this.addActions) { diff --git a/src/frontend/packages/core/src/shared/components/ssh-viewer/ssh-viewer.component.html b/src/frontend/packages/core/src/shared/components/ssh-viewer/ssh-viewer.component.html index 3d71c6e1a8..d5cee755ee 100644 --- a/src/frontend/packages/core/src/shared/components/ssh-viewer/ssh-viewer.component.html +++ b/src/frontend/packages/core/src/shared/components/ssh-viewer/ssh-viewer.component.html @@ -1,12 +1,17 @@
-
+
- Error occurred establishing SSH connection + {{ errorMessage || 'Error occurred establishing SSH connection' }}
Disconnected -
+
+
+
+ {{ message }} +
+
diff --git a/src/frontend/packages/core/src/shared/components/ssh-viewer/ssh-viewer.component.ts b/src/frontend/packages/core/src/shared/components/ssh-viewer/ssh-viewer.component.ts index b2162e00b7..8d9cedccb8 100644 --- a/src/frontend/packages/core/src/shared/components/ssh-viewer/ssh-viewer.component.ts +++ b/src/frontend/packages/core/src/shared/components/ssh-viewer/ssh-viewer.component.ts @@ -29,6 +29,8 @@ export class SshViewerComponent implements OnInit, OnDestroy { public isConnecting = false; private isDestroying = false; + public message = ''; + @ViewChild('terminal', { static: true }) container: ElementRef; private xterm: Terminal; @@ -66,7 +68,6 @@ export class SshViewerComponent implements OnInit, OnDestroy { this.xterm = new Terminal(); this.xterm.loadAddon(this.xtermFitAddon); this.xterm.open(this.container.nativeElement); - // this.xtermFitAddon.fit(); this.resize(); this.xterm.onKey(e => { @@ -115,8 +116,15 @@ export class SshViewerComponent implements OnInit, OnDestroy { this.msgSubscription = this.sshStream .subscribe( (data: string) => { - for (const c of data.split(' ')) { - this.xterm.write(String.fromCharCode(parseInt(c, 16))); + // Check for a window title message + if (!this.isWindowTitle(data)) { + for (const c of data.split(' ')) { + this.xterm.write(String.fromCharCode(parseInt(c, 16))); + } + } else { + console.log('Error') + const eMsg = this.errorMessage; + this.errorMessage = eMsg; } }, (err) => { @@ -130,4 +138,24 @@ export class SshViewerComponent implements OnInit, OnDestroy { } ); } + private isWindowTitle(data: string): boolean { + const chars = data.split(' '); + if (chars.length > 4 && + parseInt(chars[0], 16) === 27 && + parseInt(chars[1], 16) === 93 && + parseInt(chars[2], 16) === 50 && + parseInt(chars[3], 16) === 59) { + let title = ''; + for (let i = 4; i < chars.length - 1; i++) { + title += String.fromCharCode(parseInt(chars[i], 16)); + } + if (title.length > 0 && title.charAt(0) === '!') { + this.errorMessage = title.substr(1); + console.log(this.errorMessage); + return true; + } + this.message = title; + } + return false; + } } diff --git a/src/frontend/packages/core/src/shared/shared.module.ts b/src/frontend/packages/core/src/shared/shared.module.ts index 60241f3d73..e5915d0af6 100644 --- a/src/frontend/packages/core/src/shared/shared.module.ts +++ b/src/frontend/packages/core/src/shared/shared.module.ts @@ -54,6 +54,7 @@ import { TableCellSidePanelComponent, } from './components/list/list-table/table-cell-side-panel/table-cell-side-panel.component'; import { TableCellStatusDirective } from './components/list/list-table/table-cell-status.directive'; +import { listTableCells } from './components/list/list-table/table-cell/table-cell.component'; import { TableComponent } from './components/list/list-table/table.component'; import { listTableComponents } from './components/list/list-table/table.types'; import { EndpointCardComponent } from './components/list/list-types/endpoint/endpoint-card/endpoint-card.component'; @@ -310,7 +311,8 @@ import { UserPermissionDirective } from './user-permission.directive'; SidepanelPreviewComponent, TableCellEndpointNameComponent, CardProgressOverlayComponent, - MaxListMessageComponent + MaxListMessageComponent, + ...listTableCells ], providers: [ ListConfig, diff --git a/src/frontend/packages/devkit/src/build/extensions.ts b/src/frontend/packages/devkit/src/build/extensions.ts index 93c42193f1..cbd7d43090 100644 --- a/src/frontend/packages/devkit/src/build/extensions.ts +++ b/src/frontend/packages/devkit/src/build/extensions.ts @@ -17,7 +17,7 @@ const importModuleRegex = /src\/frontend\/packages\/core\/src\/custom-import.mod export class ExtensionsHandler { - constructor() {} + constructor() { } // Write out the _custom-import.module.ts file importing all of the required extensions public apply(webpackConfig: any, config: StratosConfig, options: any) { diff --git a/src/frontend/packages/store/src/entity-catalog/entity-catalog.types.ts b/src/frontend/packages/store/src/entity-catalog/entity-catalog.types.ts index 21ec6ec6e0..fa574a1754 100644 --- a/src/frontend/packages/store/src/entity-catalog/entity-catalog.types.ts +++ b/src/frontend/packages/store/src/entity-catalog/entity-catalog.types.ts @@ -68,6 +68,7 @@ export interface IStratosBaseEntityDefinition[]; readonly paginationConfig?: PaginationPageIteratorConfig; readonly tableConfig?: EntityTableConfig; + readonly registrationComponent?: any; /** * Hook that will fire before an entity is emitted by an entity service. This could be used, for example, entity validation */ diff --git a/src/frontend/packages/store/src/jetstream.ts b/src/frontend/packages/store/src/jetstream.ts index 12477fabba..fb8cb84451 100644 --- a/src/frontend/packages/store/src/jetstream.ts +++ b/src/frontend/packages/store/src/jetstream.ts @@ -67,7 +67,7 @@ export function hasJetStreamError(pages: Partial[]): Jet }) as JetStreamErrorResponse; } -function isJetstreamError(err: any): JetStreamErrorResponse { +export function isJetstreamError(err: any): JetStreamErrorResponse { return !!( err && err.error && diff --git a/src/frontend/packages/store/src/reducers/api-request-reducer/fail-request.ts b/src/frontend/packages/store/src/reducers/api-request-reducer/fail-request.ts index b62fee8a48..1a3826a515 100644 --- a/src/frontend/packages/store/src/reducers/api-request-reducer/fail-request.ts +++ b/src/frontend/packages/store/src/reducers/api-request-reducer/fail-request.ts @@ -26,8 +26,8 @@ export function failRequest(state, action: IFailedRequestAction) { busy: false, deleted: false, error: true, + message: action.message }; - requestFailedState.message = action.message; } else { requestFailedState.fetching = false; requestFailedState.error = true; diff --git a/src/frontend/packages/store/src/reducers/api-request-reducer/types.ts b/src/frontend/packages/store/src/reducers/api-request-reducer/types.ts index 8b514a0c4a..0f1b724a3e 100644 --- a/src/frontend/packages/store/src/reducers/api-request-reducer/types.ts +++ b/src/frontend/packages/store/src/reducers/api-request-reducer/types.ts @@ -14,6 +14,14 @@ export interface ActionState { message: string; } +// Status of an action +export interface ActionStatus { + busy: boolean; + error: boolean; + message?: string; + completed: boolean; +} + /** * Multi action lists can have different entity types per page * We use schemaKey to track this type diff --git a/src/frontend/packages/store/src/selectors/api.selectors.ts b/src/frontend/packages/store/src/selectors/api.selectors.ts index ed793c7678..b1eaa34938 100644 --- a/src/frontend/packages/store/src/selectors/api.selectors.ts +++ b/src/frontend/packages/store/src/selectors/api.selectors.ts @@ -31,7 +31,7 @@ export const getEntityUpdateSections = ( export const getUpdateSectionById = (guid: string) => ( updating ): ActionState => { - return updating[guid]; + return updating ? updating[guid] : null; }; export function selectUpdateInfo( diff --git a/src/frontend/packages/store/src/selectors/endpoint.selectors.ts b/src/frontend/packages/store/src/selectors/endpoint.selectors.ts index a77a92e217..9f64ad9b11 100644 --- a/src/frontend/packages/store/src/selectors/endpoint.selectors.ts +++ b/src/frontend/packages/store/src/selectors/endpoint.selectors.ts @@ -14,7 +14,7 @@ export const endpointStatusSelector = (state: InternalAppState): EndpointState = const endpointEntityKey = EntityCatalogHelpers.buildEntityKey(endpointEntityType, STRATOS_ENDPOINT_TYPE); export const endpointEntitiesSelector = selectEntities(endpointEntityKey); -const endpointOfType = (type: string) => +export const endpointOfType = (type: string) => (endpoints: IRequestEntityTypeState): IRequestEntityTypeState => { return Object.values(endpoints || {}).reduce((endpointsOfType, endpoint) => { if (endpoint.cnsi_type === type) { diff --git a/src/jetstream/main.go b/src/jetstream/main.go index 9b649da31d..f6f90c60c6 100644 --- a/src/jetstream/main.go +++ b/src/jetstream/main.go @@ -10,6 +10,7 @@ import ( "fmt" "io" "io/ioutil" + "math/rand" "net" "net/http" "os" @@ -115,6 +116,8 @@ func main() { } } + rand.Seed(time.Now().UnixNano()) + log.SetOutput(os.Stdout) log.Info("========================================") diff --git a/src/jetstream/repository/interfaces/sessiondata.go b/src/jetstream/repository/interfaces/sessiondata.go index 4128f4280f..37884b576a 100644 --- a/src/jetstream/repository/interfaces/sessiondata.go +++ b/src/jetstream/repository/interfaces/sessiondata.go @@ -10,9 +10,13 @@ type SessionDataStore interface { SetValues(session, group string, values map[string]string, autoExpire bool) error DeleteValues(session, group string) error + IsValidSession(id int) (bool, error) + // Cleanup runs a background goroutine every interval that deletes expired sessions from the database Cleanup(interval time.Duration) (chan<- struct{}, <-chan struct{}) // StopCleanup stops the background cleanup from running StopCleanup(quit chan<- struct{}, done <-chan struct{}) + + } diff --git a/src/jetstream/repository/sessiondata/psql_sessiondata.go b/src/jetstream/repository/sessiondata/psql_sessiondata.go index d0c3e7c1a0..c60ad32b63 100644 --- a/src/jetstream/repository/sessiondata/psql_sessiondata.go +++ b/src/jetstream/repository/sessiondata/psql_sessiondata.go @@ -3,6 +3,8 @@ package sessiondata import ( "database/sql" "fmt" + "strconv" + "time" log "github.com/sirupsen/logrus" @@ -16,12 +18,15 @@ var insertSessionDataValue = `INSERT INTO session_data (session, groupName, name var deleteSessionGroupData = `DELETE FROM session_data WHERE session=$1 AND groupName=$2` -// Expire data for sessions that not longer exist +// Expire data for sessions that no longer exist var expireSessionData = `UPDATE session_data SET expired=true WHERE session NOT IN (SELECT id from sessions)` // Delete data for sessions that no longer exist var deleteSessionData = `DELETE FROM session_data WHERE expired=true AND keep_on_expire=false` +// Check if a session valid +var isValidSession = `SELECT id, expires_on from sessions WHERE id=$1` + // SessionDataRepository is a RDB-backed Session Data repository type SessionDataRepository struct { db *sql.DB @@ -40,6 +45,7 @@ func InitRepositoryProvider(databaseProvider string) { deleteSessionGroupData = datastore.ModifySQLStatement(deleteSessionGroupData, databaseProvider) expireSessionData = datastore.ModifySQLStatement(expireSessionData, databaseProvider) deleteSessionData = datastore.ModifySQLStatement(deleteSessionData, databaseProvider) + isValidSession = datastore.ModifySQLStatement(isValidSession, databaseProvider) } // GetValues returns all values from the config table as a map @@ -100,3 +106,27 @@ func (c *SessionDataRepository) SetValues(session, group string, values map[stri return nil } + +// IsValidSession - Determines if the given session ID is still valid (has not expired) +func (c *SessionDataRepository) IsValidSession(session int) (bool, error) { + var ( + id string + expiry time.Time + ) + + err := c.db.QueryRow(isValidSession, strconv.Itoa(session)).Scan(&id, &expiry) + + switch { + case err == sql.ErrNoRows: + // No record with this ID - session does not exist + return false, nil + case err != nil: + return false, fmt.Errorf("Error trying to find Session record: %v", err) + default: + // do nothing + } + + // Check if the session has expired + now := time.Now() + return expiry.After(now), nil +} diff --git a/src/test-e2e/endpoints/register-dialog.po.ts b/src/test-e2e/endpoints/register-dialog.po.ts index 30e52790e7..44423a0d97 100644 --- a/src/test-e2e/endpoints/register-dialog.po.ts +++ b/src/test-e2e/endpoints/register-dialog.po.ts @@ -18,7 +18,7 @@ export class RegisterStepper extends Page { } isRegisterDialog(): promise.Promise { - return this.header.getTitleText().then(title => title === 'Register a new Endpoint'); + return this.header.getTitleText().then(title => title === 'Register Endpoint'); } getName = () => this.form.getFormField('name');