diff --git a/analytics-service/Dockerfile b/analytics-service/Dockerfile index e8043ae..4001b2a 100644 --- a/analytics-service/Dockerfile +++ b/analytics-service/Dockerfile @@ -1,4 +1,24 @@ -FROM python:3.8-slim +# Use the base image +FROM softwareag/apama-cumulocity-builder + +RUN apt-get update && apt-get -y upgrade + +# Set the working directory +WORKDIR /usr/src/app + +# Clone the apama-analytics-builder-block-sdk repository +RUN git clone https://github.com/SoftwareAG/apama-analytics-builder-block-sdk.git + +COPY requirements.txt /usr/src/app +COPY flask_wrapper.py /usr/src/app +# Used for tes purposes +RUN mkdir -p /tmp/builder +COPY Math_AB_Extension.zip /tmp/builder/ + +RUN pip install --upgrade pip +RUN pip install -r requirements.txt + +ENTRYPOINT ["python"FROM python:3.8-slim RUN apt-get update && apt-get -y upgrade @@ -10,4 +30,4 @@ RUN pip install --upgrade pip RUN pip install -r requirements.txt -ENTRYPOINT ["python", "-u", "/flask_wrapper.py"] \ No newline at end of file +ENTRYPOINT ["python" \ No newline at end of file diff --git a/analytics-service/Dockerfile_Standard b/analytics-service/Dockerfile_Standard new file mode 100644 index 0000000..2a6ea27 --- /dev/null +++ b/analytics-service/Dockerfile_Standard @@ -0,0 +1,15 @@ +FROM python:3.8-slim + +RUN apt-get update && apt-get -y upgrade + +COPY requirements.txt / +COPY flask_wrapper.py / +RUN mkdir -p /tmp/builder + +COPY Math_AB_Extension.zip /tmp/builder/ + +RUN pip install --upgrade pip +RUN pip install -r requirements.txt + + +ENTRYPOINT ["python", "-u", "/flask_wrapper.py"] \ No newline at end of file diff --git a/analytics-service/cumulocity.json b/analytics-service/cumulocity.json index 1a128ca..4c94cbb 100644 --- a/analytics-service/cumulocity.json +++ b/analytics-service/cumulocity.json @@ -1,4 +1,23 @@ { + "apiVersion": "2", + "version": "{VERSION}", + "provider": { + "name": "Software AG" + }, + "isolation": "PER_TENANT", + "requiredRoles": [ + "ROLE_INVENTORY_READ", + "ROLE_INVENTORY_ADMIN", + "ROLE_OPTION_MANAGEMENT_READ", + "ROLE_OPTION_MANAGEMENT_ADMIN", + "ROLE_USER_MANAGEMENT_ADMIN", + "ROLE_USER_MANAGEMENT_READ" + ], + "roles":[ + "ROLE_ANALYTICSBUILDER_ADMIN", + "ROLE_ANALYTICSBUILDER_CREATE" + ] + { "apiVersion": "2", "version": "{VERSION}", "provider": { @@ -17,4 +36,4 @@ "ROLE_TWINMAKER_ADMIN", "ROLE_TWINMAKER_CREATE" ] - } \ No newline at end of file + \ No newline at end of file diff --git a/analytics-service/flask_wrapper.py b/analytics-service/flask_wrapper.py new file mode 100644 index 0000000..8b4b297 --- /dev/null +++ b/analytics-service/flask_wrapper.py @@ -0,0 +1,114 @@ +from requests import HTTPError +from flask import Flask, request, jsonify, send_file +import logging +from github import Github +import tempfile +import os +import re +import io +import subprocess + +app = Flask(__name__) +# initialize github endpoint +gh = Github() +# define work directory +WORK_DIR_BASE = "/tmp/builder" +# define logging +logging.basicConfig( + level=logging.INFO, + format="[%(asctime)s] %(levelname)s [%(name)s.%(funcName)s:%(lineno)d] %(message)s", + datefmt="%d/%b/%Y %H:%M:%S", +) +logger = logging.getLogger("flask_wrapper") +logger.setLevel(logging.INFO) + + +@app.route("/health") +def health(): + return '{"status":"UP"}' + + +@app.route("/extension", methods=["POST"]) +def create_extension(): + # sample url + # "https://api.github.com/repos/SoftwareAG/apama-analytics-builder-block-sdk/contents/samples/blocks/CreateEvent.mon?ref=rel/10.18.0.x" + + # extract parameter + data = request.get_json() + try: + extension_name = data.get("extension_name", "") + monitors = data.get("monitors", False) + if extension_name != "": + with tempfile.TemporaryDirectory() as work_temp_dir: + logger.info( + f"Create extension POST for {work_temp_dir} {extension_name} {monitors} " + ) + # step 1: download all monitors + for monitor in monitors: + organization, repository_name, file_path, file_name = extract_path( + monitor + ) + logger.info( + f"File ( path / file_name ) : ({file_path} / {file_name} )" + ) + + # get repository + repo = gh.get_repo(f"{organization}/{repository_name}") + + # get the contents of the file + try: + file_content = repo.get_contents(file_path).decoded_content + + # Combine output directory and filename + logger.info(f"File downloaded and saved to: {file_name}") + + named_file = open(os.path.join(work_temp_dir, file_name), "wb") + named_file.write(file_content) + named_file.close() + + except Exception as e: + logger.info( + f"Error downloading file: {file_path} {monitor} {e}" + ) + + files_in_directory = [ + f + for f in os.listdir(work_temp_dir) + if os.path.isfile(os.path.join(work_temp_dir, f)) + ] + for file_name in files_in_directory: + logger.info(f"File in work_temp_dir: {file_name}") + + # step 2: run analytics_builder script + # analytics_builder build extension --input work_temp_dir --output zip_name + zip_name = f"{extension_name}.zip" + # subprocess.call(["analytics_builder", "build extension", f"--input {work_temp_dir}", f"--output {zip_name}"]) + + # step 3 : return result + # extension_result = f"{work_temp_dir}/{zip_name}" + + except Exception as e: + logger.error(f"Exception synchronisation!", exc_info=True) + + with open(f"/tmp/builder/Math_AB_Extension.zip", "rb") as extension_zip: + return send_file( + io.BytesIO(extension_zip.read()), + # attachment_filename=f"{extension_name}.zip", + mimetype="zip", + ) + + +def extract_path(path): + # Extract information from the API URL + delimiters = r"[/?]" + parts = re.split(delimiters, path) + organization = parts[4] + repository_name = parts[5] + file_path = "/".join(parts[7:-2]) # Extract path excluding "contents" and "ref" + file_name = parts[-3] # Extract path excluding "contents" and "ref" + return organization, repository_name, file_path, file_name + + +if __name__ == "__main__": + # app.run(host="0.0.0.0", port=80, debug=False) + app.run(host="127.0.0.1", port=9080, debug=False) diff --git a/analytics-ui/package-lock.json b/analytics-ui/package-lock.json index 16d7876..541aa01 100644 --- a/analytics-ui/package-lock.json +++ b/analytics-ui/package-lock.json @@ -24,7 +24,6 @@ "@c8y/ngx-components": "1018.0.125", "@c8y/style": "1018.0.125", "@ngx-translate/core": "14.0.0", - "@octokit/rest": "^20.0.2", "angular": "1.6.9", "ngx-bootstrap": "6.2.0", "rxjs": "~6.6.3", @@ -5750,149 +5749,6 @@ "node": ">=0.10.0" } }, - "node_modules/@octokit/rest": { - "version": "20.0.2", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-20.0.2.tgz", - "integrity": "sha512-Ux8NDgEraQ/DMAU1PlAohyfBBXDwhnX2j33Z1nJNziqAfHi70PuxkFYIcIt8aIAxtRE7KVuKp8lSR8pA0J5iOQ==", - "dependencies": { - "@octokit/core": "^5.0.0", - "@octokit/plugin-paginate-rest": "^9.0.0", - "@octokit/plugin-request-log": "^4.0.0", - "@octokit/plugin-rest-endpoint-methods": "^10.0.0" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/@octokit/rest/node_modules/@octokit/auth-token": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-4.0.0.tgz", - "integrity": "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==", - "engines": { - "node": ">= 18" - } - }, - "node_modules/@octokit/rest/node_modules/@octokit/core": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.0.2.tgz", - "integrity": "sha512-cZUy1gUvd4vttMic7C0lwPed8IYXWYp8kHIMatyhY8t8n3Cpw2ILczkV5pGMPqef7v0bLo0pOHrEHarsau2Ydg==", - "dependencies": { - "@octokit/auth-token": "^4.0.0", - "@octokit/graphql": "^7.0.0", - "@octokit/request": "^8.0.2", - "@octokit/request-error": "^5.0.0", - "@octokit/types": "^12.0.0", - "before-after-hook": "^2.2.0", - "universal-user-agent": "^6.0.0" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/@octokit/rest/node_modules/@octokit/endpoint": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.3.tgz", - "integrity": "sha512-TXVX57fJV7SA6LvRkeXPIOBr8AKvKDlhwNVBP/26O9DjIFi+CkYZGFLP9WtPdVOicRIhqGHxBCC6Fdj5AWWGgQ==", - "dependencies": { - "@octokit/types": "^12.0.0", - "universal-user-agent": "^6.0.0" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/@octokit/rest/node_modules/@octokit/graphql": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.0.2.tgz", - "integrity": "sha512-OJ2iGMtj5Tg3s6RaXH22cJcxXRi7Y3EBqbHTBRq+PQAqfaS8f/236fUrWhfSn8P4jovyzqucxme7/vWSSZBX2Q==", - "dependencies": { - "@octokit/request": "^8.0.1", - "@octokit/types": "^12.0.0", - "universal-user-agent": "^6.0.0" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/@octokit/rest/node_modules/@octokit/openapi-types": { - "version": "19.0.2", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-19.0.2.tgz", - "integrity": "sha512-8li32fUDUeml/ACRp/njCWTsk5t17cfTM1jp9n08pBrqs5cDFJubtjsSnuz56r5Tad6jdEPJld7LxNp9dNcyjQ==" - }, - "node_modules/@octokit/rest/node_modules/@octokit/plugin-paginate-rest": { - "version": "9.1.4", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-9.1.4.tgz", - "integrity": "sha512-MvZx4WvfhBnt7PtH5XE7HORsO7bBk4er1FgRIUr1qJ89NR2I6bWjGyKsxk8z42FPQ34hFQm0Baanh4gzdZR4gQ==", - "dependencies": { - "@octokit/types": "^12.3.0" - }, - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@octokit/core": ">=5" - } - }, - "node_modules/@octokit/rest/node_modules/@octokit/plugin-request-log": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-4.0.0.tgz", - "integrity": "sha512-2uJI1COtYCq8Z4yNSnM231TgH50bRkheQ9+aH8TnZanB6QilOnx8RMD2qsnamSOXtDj0ilxvevf5fGsBhBBzKA==", - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@octokit/core": ">=5" - } - }, - "node_modules/@octokit/rest/node_modules/@octokit/plugin-rest-endpoint-methods": { - "version": "10.1.5", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-10.1.5.tgz", - "integrity": "sha512-LMEdsMV8TTMjMTqVoqMzV95XTbv0ZsWxCxQtjAunQOCdwoDH4BVF/Ke5JMSZEVCWGI2kzxnUNbFnK/MxwV7NjA==", - "dependencies": { - "@octokit/types": "^12.3.0" - }, - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@octokit/core": ">=5" - } - }, - "node_modules/@octokit/rest/node_modules/@octokit/request": { - "version": "8.1.6", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.1.6.tgz", - "integrity": "sha512-YhPaGml3ncZC1NfXpP3WZ7iliL1ap6tLkAp6MvbK2fTTPytzVUyUesBBogcdMm86uRYO5rHaM1xIWxigWZ17MQ==", - "dependencies": { - "@octokit/endpoint": "^9.0.0", - "@octokit/request-error": "^5.0.0", - "@octokit/types": "^12.0.0", - "universal-user-agent": "^6.0.0" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/@octokit/rest/node_modules/@octokit/request-error": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.0.1.tgz", - "integrity": "sha512-X7pnyTMV7MgtGmiXBwmO6M5kIPrntOXdyKZLigNfQWSEQzVxR4a4vo49vJjTWX70mPndj8KhfT4Dx+2Ng3vnBQ==", - "dependencies": { - "@octokit/types": "^12.0.0", - "deprecation": "^2.0.0", - "once": "^1.4.0" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/@octokit/rest/node_modules/@octokit/types": { - "version": "12.3.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.3.0.tgz", - "integrity": "sha512-nJ8X2HRr234q3w/FcovDlA+ttUU4m1eJAourvfUUtwAWeqL8AsyRqfnLvVnYn3NFbUnsmzQCzLNdFerPwdmcDQ==", - "dependencies": { - "@octokit/openapi-types": "^19.0.2" - } - }, "node_modules/@octokit/tsconfig": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@octokit/tsconfig/-/tsconfig-1.0.2.tgz", @@ -7996,7 +7852,9 @@ "node_modules/before-after-hook": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", - "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==" + "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==", + "dev": true, + "peer": true }, "node_modules/big.js": { "version": "5.2.2", @@ -10287,7 +10145,9 @@ "node_modules/deprecation": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", - "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==", + "dev": true, + "peer": true }, "node_modules/destroy": { "version": "1.0.4", @@ -22939,6 +22799,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, "dependencies": { "wrappy": "1" } @@ -29348,7 +29209,9 @@ "node_modules/universal-user-agent": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", - "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==" + "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==", + "dev": true, + "peer": true }, "node_modules/universalify": { "version": "2.0.1", @@ -30628,7 +30491,8 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true }, "node_modules/write-file-atomic": { "version": "3.0.3", @@ -34957,113 +34821,6 @@ "once": "^1.4.0" } }, - "@octokit/rest": { - "version": "20.0.2", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-20.0.2.tgz", - "integrity": "sha512-Ux8NDgEraQ/DMAU1PlAohyfBBXDwhnX2j33Z1nJNziqAfHi70PuxkFYIcIt8aIAxtRE7KVuKp8lSR8pA0J5iOQ==", - "requires": { - "@octokit/core": "^5.0.0", - "@octokit/plugin-paginate-rest": "^9.0.0", - "@octokit/plugin-request-log": "^4.0.0", - "@octokit/plugin-rest-endpoint-methods": "^10.0.0" - }, - "dependencies": { - "@octokit/auth-token": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-4.0.0.tgz", - "integrity": "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==" - }, - "@octokit/core": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.0.2.tgz", - "integrity": "sha512-cZUy1gUvd4vttMic7C0lwPed8IYXWYp8kHIMatyhY8t8n3Cpw2ILczkV5pGMPqef7v0bLo0pOHrEHarsau2Ydg==", - "requires": { - "@octokit/auth-token": "^4.0.0", - "@octokit/graphql": "^7.0.0", - "@octokit/request": "^8.0.2", - "@octokit/request-error": "^5.0.0", - "@octokit/types": "^12.0.0", - "before-after-hook": "^2.2.0", - "universal-user-agent": "^6.0.0" - } - }, - "@octokit/endpoint": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.3.tgz", - "integrity": "sha512-TXVX57fJV7SA6LvRkeXPIOBr8AKvKDlhwNVBP/26O9DjIFi+CkYZGFLP9WtPdVOicRIhqGHxBCC6Fdj5AWWGgQ==", - "requires": { - "@octokit/types": "^12.0.0", - "universal-user-agent": "^6.0.0" - } - }, - "@octokit/graphql": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.0.2.tgz", - "integrity": "sha512-OJ2iGMtj5Tg3s6RaXH22cJcxXRi7Y3EBqbHTBRq+PQAqfaS8f/236fUrWhfSn8P4jovyzqucxme7/vWSSZBX2Q==", - "requires": { - "@octokit/request": "^8.0.1", - "@octokit/types": "^12.0.0", - "universal-user-agent": "^6.0.0" - } - }, - "@octokit/openapi-types": { - "version": "19.0.2", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-19.0.2.tgz", - "integrity": "sha512-8li32fUDUeml/ACRp/njCWTsk5t17cfTM1jp9n08pBrqs5cDFJubtjsSnuz56r5Tad6jdEPJld7LxNp9dNcyjQ==" - }, - "@octokit/plugin-paginate-rest": { - "version": "9.1.4", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-9.1.4.tgz", - "integrity": "sha512-MvZx4WvfhBnt7PtH5XE7HORsO7bBk4er1FgRIUr1qJ89NR2I6bWjGyKsxk8z42FPQ34hFQm0Baanh4gzdZR4gQ==", - "requires": { - "@octokit/types": "^12.3.0" - } - }, - "@octokit/plugin-request-log": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-4.0.0.tgz", - "integrity": "sha512-2uJI1COtYCq8Z4yNSnM231TgH50bRkheQ9+aH8TnZanB6QilOnx8RMD2qsnamSOXtDj0ilxvevf5fGsBhBBzKA==", - "requires": {} - }, - "@octokit/plugin-rest-endpoint-methods": { - "version": "10.1.5", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-10.1.5.tgz", - "integrity": "sha512-LMEdsMV8TTMjMTqVoqMzV95XTbv0ZsWxCxQtjAunQOCdwoDH4BVF/Ke5JMSZEVCWGI2kzxnUNbFnK/MxwV7NjA==", - "requires": { - "@octokit/types": "^12.3.0" - } - }, - "@octokit/request": { - "version": "8.1.6", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.1.6.tgz", - "integrity": "sha512-YhPaGml3ncZC1NfXpP3WZ7iliL1ap6tLkAp6MvbK2fTTPytzVUyUesBBogcdMm86uRYO5rHaM1xIWxigWZ17MQ==", - "requires": { - "@octokit/endpoint": "^9.0.0", - "@octokit/request-error": "^5.0.0", - "@octokit/types": "^12.0.0", - "universal-user-agent": "^6.0.0" - } - }, - "@octokit/request-error": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.0.1.tgz", - "integrity": "sha512-X7pnyTMV7MgtGmiXBwmO6M5kIPrntOXdyKZLigNfQWSEQzVxR4a4vo49vJjTWX70mPndj8KhfT4Dx+2Ng3vnBQ==", - "requires": { - "@octokit/types": "^12.0.0", - "deprecation": "^2.0.0", - "once": "^1.4.0" - } - }, - "@octokit/types": { - "version": "12.3.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.3.0.tgz", - "integrity": "sha512-nJ8X2HRr234q3w/FcovDlA+ttUU4m1eJAourvfUUtwAWeqL8AsyRqfnLvVnYn3NFbUnsmzQCzLNdFerPwdmcDQ==", - "requires": { - "@octokit/openapi-types": "^19.0.2" - } - } - } - }, "@octokit/tsconfig": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@octokit/tsconfig/-/tsconfig-1.0.2.tgz", @@ -36737,7 +36494,9 @@ "before-after-hook": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", - "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==" + "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==", + "dev": true, + "peer": true }, "big.js": { "version": "5.2.2", @@ -38468,7 +38227,9 @@ "deprecation": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", - "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==", + "dev": true, + "peer": true }, "destroy": { "version": "1.0.4", @@ -47912,6 +47673,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, "requires": { "wrappy": "1" } @@ -52661,7 +52423,9 @@ "universal-user-agent": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", - "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==" + "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==", + "dev": true, + "peer": true }, "universalify": { "version": "2.0.1", @@ -53643,7 +53407,8 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true }, "write-file-atomic": { "version": "3.0.3", diff --git a/analytics-ui/src/analytics-extension.module.ts b/analytics-ui/src/analytics-extension.module.ts index c1d08c3..d1f2573 100644 --- a/analytics-ui/src/analytics-extension.module.ts +++ b/analytics-ui/src/analytics-extension.module.ts @@ -12,7 +12,7 @@ import { import { BinaryFileDownloadModule } from "@c8y/ngx-components/binary-file-download"; import { DefaultSubscriptionsModule } from "@c8y/ngx-components/default-subscriptions"; import { BsDropdownModule } from "ngx-bootstrap/dropdown"; -import { AnalyticsExtensionWizardComponent } from "./wizard/analytics-extension-wizard.component"; +import { AddExtensionWizardComponent } from "./wizard/add-extension-wizard.component"; import { AnalyticsExtensionCardComponent } from "./analytics/manage/extension-card.component"; import { AnalyticsExtensionComponent } from "./analytics/manage/extension.component"; import { AnalyticsService } from "./shared/analytics.service"; @@ -22,6 +22,8 @@ import { BlockGridComponent } from "./analytics/list/block.component"; import { AnalyticsAddExtensionComponent } from "./analytics/manage/extension-add.component"; import { SampleGridComponent } from "./sample/list/sample.component"; import { HttpClientModule } from "@angular/common/http"; +import { NameExtensionComponent } from "./wizard/name-extension-modal.component"; +import { EditorStepperComponent } from "./sample/editor/editor-stepper.component"; const routes: Route[] = [ { @@ -48,30 +50,33 @@ const routes: Route[] = [ RouterModule.forRoot(), //RouterModule.forChild(routes), DefaultSubscriptionsModule, - HttpClientModule + HttpClientModule, ], declarations: [ AnalyticsExtensionComponent, AnalyticsExtensionCardComponent, AnalyticsAddExtensionComponent, - AnalyticsExtensionWizardComponent, + AddExtensionWizardComponent, + NameExtensionComponent, BlockGridComponent, SampleGridComponent, + EditorStepperComponent ], entryComponents: [ AnalyticsExtensionComponent, AnalyticsExtensionCardComponent, AnalyticsAddExtensionComponent, - AnalyticsExtensionWizardComponent, + AnalyticsExtensionCardComponent, BlockGridComponent, SampleGridComponent, + EditorStepperComponent ], providers: [ AnalyticsService, hookNavigator(AnalyticsNavigationFactory), hookWizard({ wizardId: "uploadAnalyticsExtention", - component: AnalyticsExtensionWizardComponent, + component: AnalyticsExtensionCardComponent, name: "Upload analytics extension", c8yIcon: "upload", }), diff --git a/analytics-ui/src/sample/editor/editor-stepper.component.css b/analytics-ui/src/sample/editor/editor-stepper.component.css new file mode 100644 index 0000000..2654c1c --- /dev/null +++ b/analytics-ui/src/sample/editor/editor-stepper.component.css @@ -0,0 +1,41 @@ +textarea.numbered { + /*background: url(http://i.imgur.com/2cOaJ.png);*/ + background-attachment: local; + background-repeat: no-repeat; + padding-left: 35px; + padding-top: 10px; + border-color:#ccc; +} + +pre { + tab-size: 4; + counter-reset: linecounter; + padding: 0; + color: #060606; + background-color: #333; + font-size: 16px; + line-height: 16px; + max-height: 700px; +} + +pre span.line { + counter-increment: linecounter; + line-height: 16px; +} + +pre span.line::before { + content: counter(linecounter); + color: red; + width: 30px; + display: inline-block; + border-right: 1px dotted #ccc; + padding-right: 3px; + margin-right: 5px; + text-align: right; + font-size: 16px; + line-height: 16px; +} + +/* pre span.line:nth-child(odd)::before { + background-color: #555; +} */ \ No newline at end of file diff --git a/analytics-ui/src/sample/editor/editor-stepper.component.html b/analytics-ui/src/sample/editor/editor-stepper.component.html new file mode 100644 index 0000000..1531012 --- /dev/null +++ b/analytics-ui/src/sample/editor/editor-stepper.component.html @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + {{source}} + + + + + + + + diff --git a/analytics-ui/src/sample/editor/editor-stepper.component.ts b/analytics-ui/src/sample/editor/editor-stepper.component.ts new file mode 100644 index 0000000..7d9b785 --- /dev/null +++ b/analytics-ui/src/sample/editor/editor-stepper.component.ts @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2022 Software AG, Darmstadt, Germany and/or Software AG USA Inc., Reston, VA, USA, + * and/or its subsidiaries and/or its affiliates and/or their licensors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * distributed under the License is distributed on an "AS IS" BASIS, + * Unless required by applicable law or agreed to in writing, software + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @authors Christof Strack + */ +import { CdkStep } from "@angular/cdk/stepper"; +import { + AfterViewInit, + Component, + ElementRef, + EventEmitter, + Input, + OnInit, + Output, + ViewChild, + ViewEncapsulation, +} from "@angular/core"; +import { C8yStepper } from "@c8y/ngx-components"; +import * as _ from "lodash"; +import { BsModalService } from "ngx-bootstrap/modal"; +import { BehaviorSubject, Subject } from "rxjs"; +import { AnalyticsService } from "../../shared/analytics.service"; + +@Component({ + selector: "c8y-editor-stepper", + templateUrl: "editor-stepper.component.html", + styleUrls: ["./editor-stepper.component.css"], + encapsulation: ViewEncapsulation.None, +}) +export class EditorStepperComponent implements OnInit, AfterViewInit{ + @Input() source: string; + @Output() onCommit = new EventEmitter(); + @Output() onCancel = new EventEmitter(); + @ViewChild("sourceEditor", { static: false }) + sourceEditor: ElementRef; + + selectedResult$: BehaviorSubject = new BehaviorSubject(0); + step: any; + onDestroy$ = new Subject(); + + constructor( + public bsModalService: BsModalService, + public analyticsService: AnalyticsService, + ) {} + + ngAfterViewInit(): void { + this.addLineClass(); + } + + ngOnInit() { + console.log("Monitor to view.:", this.source); + } + + async onCommitButton() { + this.onCommit.emit(true); + } + + async onCancelButton() { + this.onCancel.emit(false); + } + + public async onStepChange(event): Promise { + console.log("OnStepChange", event); + } + + public async onNextStep(event: { + stepper: C8yStepper; + step: CdkStep; + }): Promise { + event.stepper.next(); + } + + public addLineClass() { + const ne = this.sourceEditor.nativeElement; + const lines = ne.innerText.split("\n"); // can use innerHTML also + while (ne.childNodes.length > 0) { + this.sourceEditor.nativeElement.removeChild(ne.childNodes[0]); + } + for (var i = 0; i < lines.length; i++) { + var span = document.createElement("span"); + span.className = "line"; + span.innerText = lines[i]; // can use innerHTML also + ne.appendChild(span); + ne.appendChild(document.createTextNode("\n")); + } + } +} diff --git a/analytics-ui/src/sample/list/sample.component.html b/analytics-ui/src/sample/list/sample.component.html index 989a640..d926271 100644 --- a/analytics-ui/src/sample/list/sample.component.html +++ b/analytics-ui/src/sample/list/sample.component.html @@ -21,7 +21,7 @@ AnalyticsBuilder Community Samples - {{ "Refresh" | translate }} - @@ -41,15 +40,26 @@ [pagination]="pagination" [selectable]="'true'" [actionControls]="actionControls" + [bulkActionControls]="bulkActionControls" > - - No samples loaded. - + No samples loaded. - \ No newline at end of file + + + + + + + + diff --git a/analytics-ui/src/sample/list/sample.component.ts b/analytics-ui/src/sample/list/sample.component.ts index 4464dc8..d3cce94 100644 --- a/analytics-ui/src/sample/list/sample.component.ts +++ b/analytics-ui/src/sample/list/sample.component.ts @@ -27,25 +27,31 @@ import { import { ActionControl, AlertService, + BulkActionControl, Column, ColumnDataType, Pagination, + gettext, } from "@c8y/ngx-components"; import { AnalyticsService } from "../../shared/analytics.service"; import { CEP_Block } from "../../shared/analytics.model"; +import { BsModalService } from "ngx-bootstrap/modal"; +import { NameExtensionComponent } from "../../wizard/name-extension-modal.component"; @Component({ selector: "c8y-sample-grid", templateUrl: "sample.component.html", - styleUrls:['./sample.component.css'], + styleUrls: ["./sample.component.css"], encapsulation: ViewEncapsulation.None, }) export class SampleGridComponent implements OnInit { showConfigSample: boolean = false; refresh: EventEmitter = new EventEmitter(); - - samples: Partial[] = []; + showMonitorEditor: boolean = false; + samples: any[] = []; actionControls: ActionControl[] = []; + bulkActionControls: BulkActionControl[] = []; + source: string = ""; titleSample: string = "AnalyticsBuilder Community Samples"; @@ -54,7 +60,14 @@ export class SampleGridComponent implements OnInit { name: "name", header: "Name", path: "name", - filterable: false, + dataType: ColumnDataType.TextLong, + gridTrackSize: "15%", + visible: true, + }, + { + name: "url", + header: "URL", + path: "url", dataType: ColumnDataType.TextLong, visible: true, }, @@ -67,7 +80,8 @@ export class SampleGridComponent implements OnInit { constructor( public analyticsService: AnalyticsService, - public alertService: AlertService + public alertService: AlertService, + private bsModalService: BsModalService ) {} async ngOnInit() { @@ -75,6 +89,54 @@ export class SampleGridComponent implements OnInit { this.refresh.subscribe(() => { this.loadSamples(); }); + + this.bulkActionControls.push({ + type: "CREATE", + text: "Create Extension", + icon: "export", + callback: this.createExtension.bind(this), + }); + + this.actionControls.push({ + text: "View Source", + type: "VIEW", + icon: "document-with-code", + callback: this.viewMonitor.bind(this), + }); + } + + async viewMonitor(block: CEP_Block) { + try { + this.source = await this.analyticsService.getBlock_Sample_Content( + block.name + ); + } catch (error) { + console.log ("Something happended:",error); + } + this.showMonitorEditor = true; + } + + public async createExtension(ids: string[]) { + const initialState = {}; + const modalRef = this.bsModalService.show(NameExtensionComponent, { + initialState, + }); + modalRef.content.closeSubject.subscribe(async (conf) => { + console.log("Configuration after edit:", conf); + if (conf) { + const response = await this.analyticsService.createExtensionsZIP( + conf.name, + ids + ); + if (response) { + this.alertService.success( + gettext(`Created extension ${conf.name}.zip successfully‚`) + ); + } else { + this.alertService.danger(gettext("Failed to create extension")); + } + } + }); } async loadSamples() { diff --git a/analytics-ui/src/shared/analytics.model.ts b/analytics-ui/src/shared/analytics.model.ts index e0c035b..1488531 100644 --- a/analytics-ui/src/shared/analytics.model.ts +++ b/analytics-ui/src/shared/analytics.model.ts @@ -5,17 +5,17 @@ export interface ApplicationState { /** Wizard types */ export enum Wizards { - APPLICATION_UPLOAD = 'applicationUpload', - MICROSERVICE_UPLOAD = 'microserviceUpload' + APPLICATION_UPLOAD = "applicationUpload", + MICROSERVICE_UPLOAD = "microserviceUpload", } export enum ERROR_TYPE { - TYPE_VALIDATION = 'TYPE_VALIDATION', - ALREADY_SUBSCRIBED = 'ALREADY_SUBSCRIBED', - INTERNAL_ERROR = 'INTERNAL_ERROR', - NO_MANIFEST_FILE = 'NO_MANIFEST_FILE', - INVALID_PACKAGE = 'INVALID_PACKAGE', - INVALID_APPLICATION = 'INVALID_APPLICATION' + TYPE_VALIDATION = "TYPE_VALIDATION", + ALREADY_SUBSCRIBED = "ALREADY_SUBSCRIBED", + INTERNAL_ERROR = "INTERNAL_ERROR", + NO_MANIFEST_FILE = "NO_MANIFEST_FILE", + INVALID_PACKAGE = "INVALID_PACKAGE", + INVALID_APPLICATION = "INVALID_APPLICATION", } export interface ApplicationPlugin { @@ -32,13 +32,13 @@ export interface ApplicationPlugin { /** Wizard types */ export enum Category { - INPUT = 'INPUT', - OUPUT = 'OUTPUT', - LOGIC = 'LOGIC', - CALCULATION = 'CALCULATION', - AGGREGATE = 'AGGREGATE', - FLOW_MANIPULATION = 'FLOW_MANIPULATION', - UTILITY = 'UTILITY', + INPUT = "INPUT", + OUPUT = "OUTPUT", + LOGIC = "LOGIC", + CALCULATION = "CALCULATION", + AGGREGATE = "AGGREGATE", + FLOW_MANIPULATION = "FLOW_MANIPULATION", + UTILITY = "UTILITY", } // export interface Block { @@ -61,6 +61,7 @@ export interface CEP_Block { id: string; producesOutput: string; description: string; + url: string; custom: boolean; extension: string; name: string; @@ -81,6 +82,10 @@ export const REPO_SAMPLES_NAME = "apama-analytics-builder-block-sdk"; export const REPO_SAMPLES_OWNER = "SoftwareAG"; export const REPO_SAMPLES_PATH = "samples/blocks"; +export const BASE_URL = "service/analytics-extension-service"; +export const ENDPOINT_EXTENSION = "extension"; + + // https://api.github.com/repos/SoftwareAG/apama-analytics-builder-block-sdk/contents/samples // http://localhost:9000/cep/apamacorrelator/EN/block-metadata.json -// http://localhost:9000/service/cep/apamacorrelator/EN/core.json \ No newline at end of file +// http://localhost:9000/service/cep/apamacorrelator/EN/core.json diff --git a/analytics-ui/src/shared/analytics.service.ts b/analytics-ui/src/shared/analytics.service.ts index f063d59..8fa6fec 100644 --- a/analytics-ui/src/shared/analytics.service.ts +++ b/analytics-ui/src/shared/analytics.service.ts @@ -33,6 +33,8 @@ import { REPO_SAMPLES_NAME, REPO_SAMPLES_PATH, STATUS_MESSAGE_01, + BASE_URL, + ENDPOINT_EXTENSION, } from "./analytics.model"; import { filter, map, pairwise, tap } from "rxjs/operators"; import { HttpClient } from "@angular/common/http"; @@ -57,7 +59,6 @@ export class AnalyticsService { private githubFetchClient: HttpClient ) { this.realtime = new Realtime(this.fetchClient); - //this.githubFetchClient.baseUrl = GITHUB_BASE; } getExtensions(customFilter: any = {}): Promise> { @@ -78,6 +79,24 @@ export class AnalyticsService { } return result; } + + async createExtensionsZIP(name: string, monitors: string[]): Promise { + console.log(`Create extensions for : ${name}, ${monitors},`); + return this.fetchClient.fetch( + `${BASE_URL}/${ENDPOINT_EXTENSION}`, + { + headers: { + "content-type": "application/json", + }, + body: JSON.stringify({ + extension_name: name, + monitors: monitors, + }), + method: "POST", + } + ); + } + async getWebExtensions(customFilter: any = {}): Promise { return (await this.getExtensions(customFilter)).data; } @@ -128,7 +147,6 @@ export class AnalyticsService { async getBlock_Samples(): Promise[]> { const sampleUrl = `${GITHUB_BASE}/repos/${REPO_SAMPLES_OWNER}/${REPO_SAMPLES_NAME}/contents/${REPO_SAMPLES_PATH}`; - //const result: Partial[] = this.githubFetchClient const result: any = this.githubFetchClient .get(sampleUrl, { headers: { @@ -138,9 +156,31 @@ export class AnalyticsService { .pipe( map((data) => { const name = _.values(data); - return name ; - }), - ).toPromise(); + name.forEach((b) => (b.id = b.sha)); + return name; + }) + ) + .toPromise(); + return result; + } + + async getBlock_Sample_Content(name: string): Promise { + const sampleUrl = `${GITHUB_BASE}/repos/${REPO_SAMPLES_OWNER}/${REPO_SAMPLES_NAME}/contents/${REPO_SAMPLES_PATH}/${name}`; + const result: any = this.githubFetchClient + .get(sampleUrl, { + headers: { + // "content-type": "application/json", + "content-type": "application/text", + Accept: "application/vnd.github.raw", + }, + responseType: "text", + }) + // .pipe( + // map((data) => { + // const name = _.values(data); + // return name; + // }),) + .toPromise(); return result; } diff --git a/analytics-ui/src/wizard/analytics-extension-wizard.component.ts b/analytics-ui/src/wizard/add-extension-wizard.component.ts similarity index 95% rename from analytics-ui/src/wizard/analytics-extension-wizard.component.ts rename to analytics-ui/src/wizard/add-extension-wizard.component.ts index 9276cb6..dc08df0 100644 --- a/analytics-ui/src/wizard/analytics-extension-wizard.component.ts +++ b/analytics-ui/src/wizard/add-extension-wizard.component.ts @@ -13,7 +13,7 @@ import { AnalyticsService } from '../shared/analytics.service'; [canGoBack]="true" >` }) - export class AnalyticsExtensionWizardComponent { + export class AddExtensionWizardComponent { headerText: string = gettext('Upload analytics extension'); successText: string = gettext('Extension created'); diff --git a/analytics-ui/src/wizard/name-extension-modal.component.ts b/analytics-ui/src/wizard/name-extension-modal.component.ts new file mode 100644 index 0000000..f135767 --- /dev/null +++ b/analytics-ui/src/wizard/name-extension-modal.component.ts @@ -0,0 +1,60 @@ +import { Component, Input, OnInit, Output } from "@angular/core"; +import { ModalLabels } from "@c8y/ngx-components"; +import { Subject } from "rxjs"; +import { FormlyFieldConfig } from "@ngx-formly/core"; +import { FormGroup } from "@angular/forms"; + +@Component({ + selector: "name-extension-modal", + template: ` + + + + + + `, +}) +export class NameExtensionComponent implements OnInit { + @Output() closeSubject: Subject = new Subject(); + configuration: any = {}; + + configFormlyFields: FormlyFieldConfig[] = []; + configFormly: FormGroup = new FormGroup({}); + labels: ModalLabels = { ok: "Save", cancel: "Dismiss" }; + + ngOnInit(): void { + this.configFormlyFields = [ + { + className: "col-lg-12", + key: "name", + type: "input", + wrappers: ["c8y-form-field"], + templateOptions: { + label: "Name Extension", + required: true, + }, + }, + ]; + } + + onDismiss(event) { + console.log("Dismiss"); + this.closeSubject.next(undefined); + } + + onSave(event) { + console.log("Save"); + this.closeSubject.next(this.configuration); + } + +} diff --git a/attic/Montag_01.py b/attic/Montag_01.py new file mode 100644 index 0000000..7ed370b --- /dev/null +++ b/attic/Montag_01.py @@ -0,0 +1,30 @@ +from requests import HTTPError +from flask import Flask, request, jsonify, send_file +import logging +from github import Github +import tempfile +import os +import re + + +logging.basicConfig( + level=logging.DEBUG, + format="[%(asctime)s] %(levelname)s [%(name)s.%(funcName)s:%(lineno)d] %(message)s", + datefmt="%d/%b/%Y %H:%M:%S", +) +logger = logging.getLogger("flask_wrapper") +logger.setLevel(logging.INFO) + +delimiters = r'[/?]' + +monitor = "https://api.github.com/repos/SoftwareAG/apama-analytics-builder-block-sdk/contents/samples/blocks/CreateEvent.mon?ref=rel/10.18.0.x" +parts = re.split(delimiters, monitor) +logger.info(f"Variable: {parts}") +organization = parts[4] +repository_name = parts[5] +file_path = "/".join( + parts[7:-2] +) # Extract path excluding "contents" and "ref" +file_name = parts[-2] # Extract path excluding "contents" and "ref" + +logger.info(f"Variable: {organization} {file_path}")
+ {{source}} +
- No samples loaded. -
No samples loaded.