From 0b557a3f8e59d5546cba7bceb560148c1f50b56e Mon Sep 17 00:00:00 2001
From: moabu <47318409+moabu@users.noreply.github.com>
Date: Tue, 22 Nov 2022 07:58:41 +0000
Subject: [PATCH 01/32] refactor: remove jans-cli
---
.github/CODEOWNERS | 2 +-
.github/workflows/build-packages.yml | 52 +-
.../workflows/central_code_quality_check.yml | 20 +-
.github/workflows/release.yaml | 2 +-
automation/github-labels/labels-schema.json | 6 +-
.../scripts/utils.py | 2 +-
{jans-cli => jans-cli-tui}/CHANGELOG.md | 0
jans-cli-tui/cli_tui/version.py | 2 +-
jans-cli/.gitignore | 134 -
jans-cli/LICENSE | 201 --
jans-cli/Makefile | 13 -
jans-cli/README.md | 173 --
jans-cli/cli/__init__.py | 0
jans-cli/cli/config_cli.py | 2401 -----------------
jans-cli/cli/pylib/__init__.py | 0
jans-cli/cli/pylib/tabulate/LICENSE | 20 -
jans-cli/cli/pylib/tabulate/README | 1 -
jans-cli/cli/pylib/tabulate/README.md | 747 -----
jans-cli/cli/pylib/tabulate/__init__.py | 0
jans-cli/cli/pylib/tabulate/tabulate.py | 1792 ------------
jans-cli/cli/scim_cli.py | 1 -
jans-cli/cli/version.py | 6 -
jans-cli/setup.py | 84 -
23 files changed, 43 insertions(+), 5616 deletions(-)
rename {jans-cli => jans-cli-tui}/CHANGELOG.md (100%)
delete mode 100644 jans-cli/.gitignore
delete mode 100644 jans-cli/LICENSE
delete mode 100644 jans-cli/Makefile
delete mode 100644 jans-cli/README.md
delete mode 100644 jans-cli/cli/__init__.py
delete mode 100755 jans-cli/cli/config_cli.py
delete mode 100644 jans-cli/cli/pylib/__init__.py
delete mode 100644 jans-cli/cli/pylib/tabulate/LICENSE
delete mode 100644 jans-cli/cli/pylib/tabulate/README
delete mode 100644 jans-cli/cli/pylib/tabulate/README.md
delete mode 100644 jans-cli/cli/pylib/tabulate/__init__.py
delete mode 100644 jans-cli/cli/pylib/tabulate/tabulate.py
delete mode 100644 jans-cli/cli/scim_cli.py
delete mode 100644 jans-cli/cli/version.py
delete mode 100644 jans-cli/setup.py
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 59bf47437fe..b6fb98a0c05 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -21,7 +21,7 @@
/jans-fido2/ @yurem
/jans-scim/ @jgomer2001
/jans-config-api/ @pujavs @yuriyz
-/jans-cli/ @devrimyatar
+/jans-cli-tui/ @devrimyatar
/jans-linux-setup/ @devrimyatar @smansoft @yuriyz @yurem
/jans-linux-setup/jans_setup/setup_app/version.py @moabu
/jans-linux-setup/static/scripts/admin_ui_plugin.py @devrimyatar @duttarnab
diff --git a/.github/workflows/build-packages.yml b/.github/workflows/build-packages.yml
index 9affed0a13d..3069b0f1b33 100644
--- a/.github/workflows/build-packages.yml
+++ b/.github/workflows/build-packages.yml
@@ -77,7 +77,7 @@ jobs:
cp -r /opt/dist jans-src/opt/
cp -r /opt/jans jans-src/opt/
touch jans-src/opt/jans/jans-setup/package
- rm -rf install.py install jans-cli
+ rm -rf install.py install jans-cli-tui
rm -rf jans-src/opt/jans/jans-setup/logs/setup.log
rm -rf jans-src/opt/jans/jans-setup/logs/setup_error.log
sed -i "s/%VERSION%/${{ steps.previoustag.outputs.version }}/g" run-build.sh
@@ -134,10 +134,10 @@ jobs:
make zipapp
mv jans-linux-setup.pyz jans-linux-suse-X86-64-setup.pyz
sha256sum jans-linux-suse-X86-64-setup.pyz > jans-linux-suse-X86-64-setup.pyz.sha256sum
- cd ../jans-cli
+ cd ../jans-cli-tui
make zipapp
- mv config-cli.pyz jans-cli-linux-suse-X86-64.pyz
- sha256sum jans-cli-linux-suse-X86-64.pyz > jans-cli-linux-suse-X86-64.pyz.sha256sum
+ mv config-cli.pyz jans-cli-tui-linux-suse-X86-64.pyz
+ sha256sum jans-cli-tui-linux-suse-X86-64.pyz > jans-cli-tui-linux-suse-X86-64.pyz.sha256sum
# To be removed once we get SUSE build working
- uses: addnab/docker-run-action@v3
name: Build with CentOS7
@@ -163,10 +163,10 @@ jobs:
make zipapp
mv jans-linux-setup.pyz jans-linux-X86-64-setup.pyz
sha256sum jans-linux-X86-64-setup.pyz > jans-linux-X86-64-setup.pyz.sha256sum
- cd ../jans-cli
+ cd ../jans-cli-tui
make zipapp
- mv config-cli.pyz jans-cli-linux-X86-64.pyz
- sha256sum jans-cli-linux-X86-64.pyz > jans-cli-linux-X86-64.pyz.sha256sum
+ mv config-cli.pyz jans-cli-tui-linux-X86-64.pyz
+ sha256sum jans-cli-tui-linux-X86-64.pyz > jans-cli-tui-linux-X86-64.pyz.sha256sum
- name: Set up Python 3.6
uses: actions/setup-python@v4
with:
@@ -183,10 +183,10 @@ jobs:
make zipapp
mv jans-linux-setup.pyz jans-linux-ubuntu-X86-64-setup.pyz
sha256sum jans-linux-ubuntu-X86-64-setup.pyz > jans-linux-ubuntu-X86-64-setup.pyz.sha256sum
- cd ../jans-cli
+ cd ../jans-cli-tui
make zipapp
- mv config-cli.pyz jans-cli-linux-ubuntu-X86-64.pyz
- sha256sum jans-cli-linux-ubuntu-X86-64.pyz > jans-cli-linux-ubuntu-X86-64.pyz.sha256sum
+ mv config-cli.pyz jans-cli-tui-linux-ubuntu-X86-64.pyz
+ sha256sum jans-cli-tui-linux-ubuntu-X86-64.pyz > jans-cli-tui-linux-ubuntu-X86-64.pyz.sha256sum
- uses: actions/cache@v3
id: cache-installers
with:
@@ -197,12 +197,12 @@ jobs:
${{github.workspace}}/jans-linux-setup/jans-linux-suse-X86-64-setup.pyz.sha256sum
${{github.workspace}}/jans-linux-setup/jans-linux-ubuntu-X86-64-setup.pyz
${{github.workspace}}/jans-linux-setup/jans-linux-ubuntu-X86-64-setup.pyz.sha256sum
- ${{github.workspace}}/jans-cli/jans-cli-linux-X86-64.pyz
- ${{github.workspace}}/jans-cli/jans-cli-linux-X86-64.pyz.sha256sum
- ${{github.workspace}}/jans-cli/jans-cli-linux-suse-X86-64.pyz
- ${{github.workspace}}/jans-cli/jans-cli-linux-suse-X86-64.pyz.sha256sum
- ${{github.workspace}}/jans-cli/jans-cli-linux-ubuntu-X86-64.pyz
- ${{github.workspace}}/jans-cli/jans-cli-linux-ubuntu-X86-64.pyz.sha256sum
+ ${{github.workspace}}/jans-cli-tui/jans-cli-tui-linux-X86-64.pyz
+ ${{github.workspace}}/jans-cli-tui/jans-cli-tui-linux-X86-64.pyz.sha256sum
+ ${{github.workspace}}/jans-cli-tui/jans-cli-tui-linux-suse-X86-64.pyz
+ ${{github.workspace}}/jans-cli-tui/jans-cli-tui-linux-suse-X86-64.pyz.sha256sum
+ ${{github.workspace}}/jans-cli-tui/jans-cli-tui-linux-ubuntu-X86-64.pyz
+ ${{github.workspace}}/jans-cli-tui/jans-cli-tui-linux-ubuntu-X86-64.pyz.sha256sum
key: ${{ github.sha }}
upload_python_packages:
@@ -224,12 +224,12 @@ jobs:
${{github.workspace}}/jans-linux-setup/jans-linux-suse-X86-64-setup.pyz.sha256sum
${{github.workspace}}/jans-linux-setup/jans-linux-ubuntu-X86-64-setup.pyz
${{github.workspace}}/jans-linux-setup/jans-linux-ubuntu-X86-64-setup.pyz.sha256sum
- ${{github.workspace}}/jans-cli/jans-cli-linux-X86-64.pyz
- ${{github.workspace}}/jans-cli/jans-cli-linux-X86-64.pyz.sha256sum
- ${{github.workspace}}/jans-cli/jans-cli-linux-suse-X86-64.pyz
- ${{github.workspace}}/jans-cli/jans-cli-linux-suse-X86-64.pyz.sha256sum
- ${{github.workspace}}/jans-cli/jans-cli-linux-ubuntu-X86-64.pyz
- ${{github.workspace}}/jans-cli/jans-cli-linux-ubuntu-X86-64.pyz.sha256sum
+ ${{github.workspace}}/jans-cli-tui/jans-cli-tui-linux-X86-64.pyz
+ ${{github.workspace}}/jans-cli-tui/jans-cli-tui-linux-X86-64.pyz.sha256sum
+ ${{github.workspace}}/jans-cli-tui/jans-cli-tui-linux-suse-X86-64.pyz
+ ${{github.workspace}}/jans-cli-tui/jans-cli-tui-linux-suse-X86-64.pyz.sha256sum
+ ${{github.workspace}}/jans-cli-tui/jans-cli-tui-linux-ubuntu-X86-64.pyz
+ ${{github.workspace}}/jans-cli-tui/jans-cli-tui-linux-ubuntu-X86-64.pyz.sha256sum
key: ${{ github.sha }}
- name: Get latest tag
id: previoustag
@@ -262,8 +262,8 @@ jobs:
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.MOAUTO_WORKFLOW_TOKEN }}
- file: ${{github.workspace}}/jans-cli/jans-cli-linux-${{ matrix.name }}-X86-64.pyz
- asset_name: jans-cli-linux-${{ matrix.name }}-X86-64.pyz
+ file: ${{github.workspace}}/jans-cli-tui/jans-cli-tui-linux-${{ matrix.name }}-X86-64.pyz
+ asset_name: jans-cli-tui-linux-${{ matrix.name }}-X86-64.pyz
tag: ${{ steps.previoustag.outputs.tag }}
- name: Upload checksum to release
id: upload_shas_cli
@@ -271,6 +271,6 @@ jobs:
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.MOAUTO_WORKFLOW_TOKEN }}
- file: ${{github.workspace}}/jans-cli/jans-cli-linux-${{ matrix.name }}-X86-64.pyz.sha256sum
- asset_name: jans-cli-linux-${{ matrix.name }}-X86-64.pyz.sha256sum
+ file: ${{github.workspace}}/jans-cli-tui/jans-cli-tui-linux-${{ matrix.name }}-X86-64.pyz.sha256sum
+ asset_name: jans-cli-tui-linux-${{ matrix.name }}-X86-64.pyz.sha256sum
tag: ${{ steps.previoustag.outputs.tag }}
diff --git a/.github/workflows/central_code_quality_check.yml b/.github/workflows/central_code_quality_check.yml
index c2f13cac27a..8f0778ae02b 100644
--- a/.github/workflows/central_code_quality_check.yml
+++ b/.github/workflows/central_code_quality_check.yml
@@ -20,7 +20,7 @@ on:
- 'jans-eleven/**'
- 'agama/**'
- 'jans-linux-setup/**'
- - 'jans-cli/**'
+ - 'jans-cli-tui/**'
- 'jans-pycloudlib/**'
- '!**/CHANGELOG.md'
- '!**.txt'
@@ -42,7 +42,7 @@ on:
- 'jans-eleven/**'
- 'agama/**'
- 'jans-linux-setup/**'
- - 'jans-cli/**'
+ - 'jans-cli-tui/**'
- 'jans-pycloudlib/**'
- '!**/CHANGELOG.md'
- '!**.txt'
@@ -56,7 +56,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- module: [jans-auth-server, agama, jans-config-api, jans-core, jans-linux-setup, jans-cli, jans-fido2, jans-notify, jans-orm, jans-scim, jans-eleven, jans-pycloudlib]
+ module: [jans-auth-server, agama, jans-config-api, jans-core, jans-linux-setup, jans-cli-tui, jans-fido2, jans-notify, jans-orm, jans-scim, jans-eleven, jans-pycloudlib]
env:
JVM_PROJECTS: |
jans-auth-server
@@ -70,7 +70,7 @@ jobs:
agama
NON_JVM_PROJECTS: |
jans-linux-setup
- jans-cli
+ jans-cli-tui
jans-pycloudlib
steps:
@@ -112,16 +112,16 @@ jobs:
echo GH sha: $GITHUB_SHA
- name: Set up JDK 11
- # JanssenProject/jans-cli is too similar to JanssenProject/jans-client-api as the contains function is returning it belonging to the JVM_PROJECT
- if: contains(env.CHANGED_DIR, matrix.module) && contains(env.JVM_PROJECTS, matrix.module) && matrix.module != 'jans-cli'
+ # JanssenProject/jans-cli-tui is too similar to JanssenProject/jans-client-api as the contains function is returning it belonging to the JVM_PROJECT
+ if: contains(env.CHANGED_DIR, matrix.module) && contains(env.JVM_PROJECTS, matrix.module) && matrix.module != 'jans-cli-tui'
uses: actions/setup-java@v3
with:
java-version: '11'
distribution: 'adopt'
- name: Cache SonarCloud packages for JVM based project
- # JanssenProject/jans-cli is too similar to JanssenProject/jans-client-api as the contains function is returning it belonging to the JVM_PROJECT
- if: contains(env.CHANGED_DIR, matrix.module) && contains(env.JVM_PROJECTS, matrix.module) && matrix.module != 'jans-cli'
+ # JanssenProject/jans-cli-tui is too similar to JanssenProject/jans-client-api as the contains function is returning it belonging to the JVM_PROJECT
+ if: contains(env.CHANGED_DIR, matrix.module) && contains(env.JVM_PROJECTS, matrix.module) && matrix.module != 'jans-cli-tui'
uses: actions/cache@v3
with:
path: ~/.sonar/cache
@@ -129,8 +129,8 @@ jobs:
restore-keys: ${{ runner.os }}-sonar
- name: Build and analyze JVM based project
- # JanssenProject/jans-cli is too similar to JanssenProject/jans-client-api as the contains function is returning it belonging to the JVM_PROJECT
- if: contains(env.CHANGED_DIR, matrix.module) && contains(env.JVM_PROJECTS, matrix.module) && matrix.module != 'jans-cli'
+ # JanssenProject/jans-cli-tui is too similar to JanssenProject/jans-client-api as the contains function is returning it belonging to the JVM_PROJECT
+ if: contains(env.CHANGED_DIR, matrix.module) && contains(env.JVM_PROJECTS, matrix.module) && matrix.module != 'jans-cli-tui'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
index ee49e5de573..3a365461246 100644
--- a/.github/workflows/release.yaml
+++ b/.github/workflows/release.yaml
@@ -143,7 +143,7 @@ jobs:
#max-parallel: 1
fail-fast: false
matrix:
- python-projects: ["jans-pycloudlib", "jans-cli", "jans-linux-setup"]
+ python-projects: ["jans-pycloudlib", "jans-cli-tui", "jans-linux-setup"]
steps:
- name: Checkout
uses: actions/checkout@v3
diff --git a/automation/github-labels/labels-schema.json b/automation/github-labels/labels-schema.json
index e23fb26a1b1..07b44b57a51 100644
--- a/automation/github-labels/labels-schema.json
+++ b/automation/github-labels/labels-schema.json
@@ -152,12 +152,12 @@
"title-prefixes": []
}
},
- "comp-jans-cli": {
+ "comp-jans-cli-tui": {
"color": "0052CC",
- "description": "Touching folder /jans-cli",
+ "description": "Touching folder /jans-cli-tui",
"auto-label": {
"branch": "",
- "paths": ["jans-cli"],
+ "paths": ["jans-cli-tui"],
"title-prefixes": []
}
},
diff --git a/docker-jans-persistence-loader/scripts/utils.py b/docker-jans-persistence-loader/scripts/utils.py
index ef54bd882dc..236dd0a58bb 100644
--- a/docker-jans-persistence-loader/scripts/utils.py
+++ b/docker-jans-persistence-loader/scripts/utils.py
@@ -204,7 +204,7 @@ def merge_jans_cli_ctx(manager, ctx):
# - move the configs and secrets creation to configurator
# - remove them on future release
- # jans-cli client
+ # jans-cli-tui client
ctx["role_based_client_id"] = manager.config.get("role_based_client_id")
if not ctx["role_based_client_id"]:
ctx["role_based_client_id"] = f"2000.{uuid4()}"
diff --git a/jans-cli/CHANGELOG.md b/jans-cli-tui/CHANGELOG.md
similarity index 100%
rename from jans-cli/CHANGELOG.md
rename to jans-cli-tui/CHANGELOG.md
diff --git a/jans-cli-tui/cli_tui/version.py b/jans-cli-tui/cli_tui/version.py
index 80381fb7ede..9fbd42fb085 100644
--- a/jans-cli-tui/cli_tui/version.py
+++ b/jans-cli-tui/cli_tui/version.py
@@ -3,4 +3,4 @@
https://www.apache.org/licenses/LICENSE-2.0
"""
-__version__ = "1.0.1-dev"
+__version__ = "1.0.5-dev"
diff --git a/jans-cli/.gitignore b/jans-cli/.gitignore
deleted file mode 100644
index 14c925f58d3..00000000000
--- a/jans-cli/.gitignore
+++ /dev/null
@@ -1,134 +0,0 @@
-swagger_yaml.json
-config.ini
-
-# Byte-compiled / optimized / DLL files
-__pycache__/
-*.py[cod]
-*$py.class
-
-# C extensions
-*.so
-
-# Distribution / packaging
-.Python
-build/
-develop-eggs/
-dist/
-downloads/
-eggs/
-.eggs/
-lib/
-lib64/
-parts/
-sdist/
-var/
-wheels/
-pip-wheel-metadata/
-share/python-wheels/
-*.egg-info/
-.installed.cfg
-*.egg
-MANIFEST
-
-# PyInstaller
-# Usually these files are written by a python script from a template
-# before PyInstaller builds the exe, so as to inject date/other infos into it.
-*.manifest
-*.spec
-
-# Installer logs
-pip-log.txt
-pip-delete-this-directory.txt
-
-# Unit test / coverage reports
-htmlcov/
-.tox/
-.nox/
-.coverage
-.coverage.*
-.cache
-nosetests.xml
-coverage.xml
-*.cover
-*.py,cover
-.hypothesis/
-.pytest_cache/
-
-# Translations
-*.mo
-*.pot
-
-# Django stuff:
-*.log
-local_settings.py
-db.sqlite3
-db.sqlite3-journal
-
-# Flask stuff:
-instance/
-.webassets-cache
-
-# Scrapy stuff:
-.scrapy
-
-# Sphinx documentation
-docs/_build/
-
-# PyBuilder
-target/
-
-# Jupyter Notebook
-.ipynb_checkpoints
-
-# IPython
-profile_default/
-ipython_config.py
-
-# pyenv
-.python-version
-
-# pipenv
-# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
-# However, in case of collaboration, if having platform-specific dependencies or dependencies
-# having no cross-platform support, pipenv may install dependencies that don't work, or not
-# install all needed dependencies.
-#Pipfile.lock
-
-# PEP 582; used by e.g. github.com/David-OConnor/pyflow
-__pypackages__/
-
-# Celery stuff
-celerybeat-schedule
-celerybeat.pid
-
-# SageMath parsed files
-*.sage.py
-
-# Environments
-.env
-.venv
-env/
-venv/
-ENV/
-env.bak/
-venv.bak/
-
-# Spyder project settings
-.spyderproject
-.spyproject
-
-# Rope project settings
-.ropeproject
-
-# mkdocs documentation
-/site
-
-# mypy
-.mypy_cache/
-.dmypy.json
-dmypy.json
-
-# Pyre type checker
-.pyre/
-.idea/
-.idea
diff --git a/jans-cli/LICENSE b/jans-cli/LICENSE
deleted file mode 100644
index 261eeb9e9f8..00000000000
--- a/jans-cli/LICENSE
+++ /dev/null
@@ -1,201 +0,0 @@
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright [yyyy] [name of copyright owner]
-
- 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
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- 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.
diff --git a/jans-cli/Makefile b/jans-cli/Makefile
deleted file mode 100644
index 5a916486caa..00000000000
--- a/jans-cli/Makefile
+++ /dev/null
@@ -1,13 +0,0 @@
-.DEFAULT_GOAL := develop
-
-develop:
- pip3 install -e .
-
-install:
- pip3 install .
-
-uninstall:
- pip3 uninstall jans-cli -y
-
-zipapp:
- shiv --compressed -o config-cli.pyz -p '/usr/bin/env python3' -e cli.config_cli:main . --no-cache
diff --git a/jans-cli/README.md b/jans-cli/README.md
deleted file mode 100644
index fb917ef7a23..00000000000
--- a/jans-cli/README.md
+++ /dev/null
@@ -1,173 +0,0 @@
-# _Janssen Command Line Interface_
-`jans-cli` is a **Command Line Interface** for Janssen Configuration. It also has `menu-driven` interface that makes it easier to understand how to use [Janssen Server](https://github.com/JanssenProject/home) through the Interactive Mode.
-
-Table of Contents
-=================
-
- * [Janssen Command Line Interface](#_janssen-command-line-interface_)
- * [Installation](#_installation_)
- * [Quick Start](#_quick-start_)
-
-# _Installation_
-
-You can directly download the `jans-cli` package file as below:
-
-### For macOs:
-
-```
-wget https://github.com/JanssenProject/jans-cli/releases/latest/download/jans-cli-macos-amd64.pyz
-```
-
-### for linux:
-
-```
-wget https://github.com/JanssenProject/jans-cli/releases/latest/download/jans-cli-linux-amd64.pyz
-```
-
-## Build `jans-cli.pyz` manually
-
-If you would like to build `jans-cli` manually, you can go through the following steps noted here:
-
-## Prerequisites
-1. wget
-2. unzip
-3. Python 3.6+.
-4. Python `pip3` package.
-
-### Building
-
-1. Install dependencies
-
- * On Ubuntu 20
- ```sh
- apt install -y wget unzip python3-pip python3-dev
- ```
- * On CentOS Stream 8
- ```sh
- yum install -y wget unzip python3-pip python3-devel make
- pip3 install --upgrade pip
- ```
-
- Install Shiv
-
- ```sh
- pip3 install shiv
- ```
-
-2. Download the repository:
-
- ```sh
- wget https://github.com/JanssenProject/jans/archive/refs/heads/main.zip
- ```
-
-3. Unzip package, and change to directory
-
- ```sh
- unzip main.zip
- cd jans-main/jans-cli
- ```
-
-4. Build
-
- ```sh
- make zipapp
- ```
-
- You can verify with the following command line if everything is done successfully.
-
- ```
- python3 config-cli.pyz -h
- ```
-
-5. Executing config-cli.pyz Remotely
- Login your Jans Server. Execute the following command to find **client-id** and **client-secret**:
- ```sh
- cat /opt/jans/jans-setup/setup.properties.last | grep "role_based_client"
- ```
- It will output like this:
- ```sh
- role_based_client_encoded_pw=+U3XiW2uM/rnidqZ2mv9sw\=\=
- role_based_client_id=2000.09b47f56-1b9e-4443-bebd-bdf970406a15
- role_based_client_pw=T68kLUz4YXnR
- ```
- **client-id** is the value of **role_based_client_id** and **client-secret** is the value of **role_based_client_pw**
- Thus we can execute CLI as:
-
- ```sh
- python3 config-cli.pyz --host demoexmple.gluu.org --client-id 2000.09b47f56-1b9e-4443-bebd-bdf970406a15 --client-secret T68kLUz4YXnR
- ```
-
-
-### Standard Python package
-1. Install venv module
- ```sh
- pip3 install virtualenv
- ```
-
-2. Create virtual environment and activate:
-
- ```sh
- python3 -m virtualenv .venv
- source .venv/bin/activate
- ```
-
-3. Download and install the package:
-
- ```
- wget https://github.com/JanssenProject/jans/archive/refs/heads/main.zip
- unzip main.zip
- cd jans-main/jans-cli
- make install
- ```
-
- This command will install executable called `jans-cli` available in virtual environment `PATH`.
-
-
-![](../docs/assets/image-build-jans-cli-pyz-manually-03042021.png)
-
-
-## Virtual Machine Setup
-
-**jans-cli** is automatically installed if you choose `jans-config-api` during [Janssen Server](https://github.com/JanssenProject/home/blob/main/development.md#install-janssen-into-vm) Installation on Virtual Machine.
-
-![](../docs/assets/image-jans-config-api-03042021.png)
-
-After successfully installed Janssen Server, you will get two command-line arguments as below:
-
-![](../docs/assets/image-installed-03042021.png)
-
-# _Quick Start_
-
-As you have seen, CLI supports both of the `config-cli` and `scim-cli`. For a quick start, let's run the following command.
-
-```
-/opt/jans/jans-cli/config-cli.py
-```
-If you get an error, you can try in this way:
-
-```
-python3 /opt/jans/jans-cli/config-cli.py
-```
-
-Alternatively, you can make python3 to default version:
-```
-sudo update-alternatives --install /usr/bin/python python /usr/bin/python3 10
-/opt/jans/jans-cli/config-cli.py
-```
-
-You will get a menu as below image:
-
-![main-menu.png](../docs/assets/image-im-main-03042021.png)
-
-From the following list, you can choose any options by selecting its number. For example, let's say number 2,
-to get **Default Authentication Method**.
-
-That returns another two options as below:
-
-![option-2-option.png](../docs/assets/image-im-default-auth-02-03042021.png)
-
-Now by selecting 1 it returns our desired result as below image:
-
-![default-authentication-method.png](../docs/assets/image-im-cur-default-auth-03042021.png)
-
-So, That was a quick start to view how this _jans-cli_ Interactive Mode works. Please, follow this [link](https://github.com/JanssenProject/jans/blob/main/docs/admin/config-guide/jans-cli/index.md) to read the _jans-cli_ docs for a better understanding of the Janssen Command-Line.
diff --git a/jans-cli/cli/__init__.py b/jans-cli/cli/__init__.py
deleted file mode 100644
index e69de29bb2d..00000000000
diff --git a/jans-cli/cli/config_cli.py b/jans-cli/cli/config_cli.py
deleted file mode 100755
index 33da70bd508..00000000000
--- a/jans-cli/cli/config_cli.py
+++ /dev/null
@@ -1,2401 +0,0 @@
-#!/usr/bin/env python3
-
-import sys
-import os
-import json
-import re
-import urllib3
-import configparser
-import readline
-import argparse
-import inspect
-import random
-import datetime
-import ruamel.yaml
-import importlib
-import code
-import traceback
-import ast
-import base64
-import pprint
-import copy
-
-from pathlib import Path
-from types import SimpleNamespace
-from urllib.parse import urlencode
-from collections import OrderedDict
-
-home_dir = Path.home()
-config_dir = home_dir.joinpath('.config')
-config_dir.mkdir(parents=True, exist_ok=True)
-config_ini_fn = config_dir.joinpath('jans-cli.ini')
-cur_dir = os.path.dirname(os.path.realpath(__file__))
-log_dir = os.environ.get('cli_log_dir', cur_dir)
-sys.path.append(cur_dir)
-
-from pylib.tabulate.tabulate import tabulate
-try:
- import jwt
-except ModuleNotFoundError:
- from pylib import jwt
-
-tabulate_endpoints = {
- 'jca.get-config-scripts': ['scriptType', 'name', 'enabled', 'inum'],
- 'jca.get-user': ['inum', 'userId', 'mail','sn', 'givenName', 'jansStatus'],
- 'jca.get-all-attribute': ['inum', 'name', 'displayName', 'status', 'dataType', 'claimName'],
- 'jca.get-oauth-openid-clients': ['inum', 'displayName', 'clientName', 'applicationType'],
- 'jca.get-oauth-scopes': ['dn', 'id', 'scopeType'],
- 'jca.get-oauth-uma-resources': ['dn', 'name', 'expirationDate'],
- 'scim.get-users': ['id', 'userName', 'displayName', 'active']
-}
-
-tabular_dataset = {'scim.get-users': 'Resources'}
-excluded_operations = {'scim': ['search-user'], 'jca':[]}
-
-my_op_mode = 'scim' if 'scim' in os.path.basename(sys.argv[0]) else 'jca'
-sys.path.append(os.path.join(cur_dir, my_op_mode))
-swagger_client = importlib.import_module(my_op_mode + '.swagger_client')
-swagger_client.models = importlib.import_module(my_op_mode + '.swagger_client.models')
-swagger_client.api = importlib.import_module(my_op_mode + '.swagger_client.api')
-swagger_client.rest = importlib.import_module(my_op_mode + '.swagger_client.rest')
-plugins = []
-
-warning_color = 214
-error_color = 196
-success_color = 10
-bold_color = 15
-grey_color = 242
-
-clear = lambda: os.system('clear')
-
-urllib3.disable_warnings()
-config = configparser.ConfigParser()
-
-host = os.environ.get('jans_host')
-client_id = os.environ.get(my_op_mode + 'jca_client_id')
-client_secret = os.environ.get(my_op_mode + 'jca_client_secret')
-access_token = None
-debug = os.environ.get('jans_client_debug')
-
-def encode_decode(s, decode=False):
- cmd = '/opt/jans/bin/encode.py '
- if decode:
- cmd += '-D '
- result = os.popen(cmd + s + ' 2>/dev/null').read()
- return result.strip()
-
-
-# dummy api class to reach private ApiClient methods
-class MyApiClient(swagger_client.ApiClient):
- pass
-
-
-class DummyPool:
- def close(self):
- pass
-
- def join(self):
- pass
-
-
-myapi = MyApiClient()
-myapi.pool = DummyPool()
-
-##################### arguments #####################
-op_list = []
-
-for api_name in dir(swagger_client.api):
- if api_name.endswith('Api') and inspect.isclass(getattr(swagger_client.api, api_name)):
- op_list.append(api_name[:-3])
-
-parser = argparse.ArgumentParser()
-parser.add_argument("--host", help="Hostname of server")
-parser.add_argument("--client-id", help="Jans Config Api Client ID")
-parser.add_argument("--client-secret", "--client_secret", help="Jans Config Api Client ID secret")
-parser.add_argument("--access-token", help="JWT access token or path to file containing JWT access token")
-parser.add_argument("--plugins", help="Available plugins separated by comma")
-parser.add_argument("-debug", help="Run in debug mode", action='store_true')
-parser.add_argument("--operation-id", help="Operation ID to be done")
-parser.add_argument("--url-suffix", help="Argument to be added api endpoint url. For example inum:2B29")
-parser.add_argument("--info", choices=op_list, help="Help for operation")
-parser.add_argument("--op-mode", choices=['get', 'post', 'put', 'patch', 'delete'], default='get',
- help="Operation mode to be done")
-parser.add_argument("--endpoint-args",
- help="Arguments to pass endpoint separated by comma. For example limit:5,status:INACTIVE")
-parser.add_argument("--schema", help="Get sample json schema")
-
-parser.add_argument("-CC", "--config-api-mtls-client-cert", help="Path to SSL Certificate file")
-parser.add_argument("-CK", "--config-api-mtls-client-key", help="Path to SSL Key file")
-parser.add_argument("--key-password", help="Password for SSL Key file")
-parser.add_argument("-noverify", help="Ignore verifying the SSL certificate", action='store_true', default=True)
-
-parser.add_argument("-use-test-client", help="Use test client without device authorization", action='store_true')
-
-
-parser.add_argument("--patch-add", help="Colon delimited key:value pair for add patch operation. For example loggingLevel:DEBUG")
-parser.add_argument("--patch-replace", help="Colon delimited key:value pair for replace patch operation. For example loggingLevel:DEBUG")
-parser.add_argument("--patch-remove", help="Key for remove patch operation. For example imgLocation")
-parser.add_argument("--no-suggestion", help="Do not use prompt toolkit to display word completer", action='store_true')
-parser.add_argument("-log-dir", help="Do not use prompt toolkit to display word completer", default=log_dir)
-
-
-# parser.add_argument("-show-data-type", help="Show data type in schema query", action='store_true')
-parser.add_argument("--data", help="Path to json data file")
-args = parser.parse_args()
-
-if args.config_api_mtls_client_cert and args.config_api_mtls_client_key:
- excluded_operations['jca'] += [
- 'get-user', 'post-user', 'put-user', 'get-user-by-inum', 'delete-user', 'patch-user-by-inum',
- 'get-properties-fido2', 'put-properties-fido2', 'get-registration-entries-fido2',
- ]
-
-if not args.no_suggestion:
- from prompt_toolkit import prompt, HTML
- from prompt_toolkit.completion import WordCompleter
-
-
-################## end of arguments #################
-
-test_client = args.use_test_client
-
-
-if args.plugins:
- for plugin in args.plugins.split(','):
- plugins.append(plugin.strip())
-
-def write_config():
- with open(config_ini_fn, 'w') as w:
- config.write(w)
-
-if not(host and (client_id and client_secret or access_token)):
- host = args.host
- client_id = args.client_id
- client_secret = args.client_secret
- debug = args.debug
-
- access_token = args.access_token
- if access_token and os.path.isfile(access_token):
- with open(access_token) as f:
- access_token = f.read()
-
-
-if not(host and (client_id and client_secret or access_token)):
-
- if config_ini_fn.exists():
- config.read_string(config_ini_fn.read_text())
- host = config['DEFAULT']['jans_host']
-
- if 'jca_test_client_id' in config['DEFAULT'] and test_client:
- client_id = config['DEFAULT']['jca_test_client_id']
- secret_key_str = 'jca_test_client_secret'
- else:
- client_id = config['DEFAULT']['jca_client_id']
- secret_key_str = 'jca_client_secret'
-
- secret_enc_key_str = secret_key_str + '_enc'
- if config['DEFAULT'].get(secret_key_str):
- client_secret = config['DEFAULT'][secret_key_str]
- elif config['DEFAULT'].get(secret_enc_key_str):
- client_secret_enc = config['DEFAULT'][secret_enc_key_str]
- client_secret = encode_decode(client_secret_enc, decode=True)
-
- debug = config['DEFAULT'].get('debug')
- log_dir = config['DEFAULT'].get('log_dir', log_dir)
-
- else:
- config['DEFAULT'] = {'jans_host': 'jans server hostname,e.g, jans.foo.net',
- 'jca_client_id': 'your jans config api client id',
- 'jca_client_secret': 'client secret for your jans config api client',
- 'scim_client_id': 'your jans scim client id',
- 'scim_client_secret': 'client secret for your jans scim client'}
-
- write_config()
-
- print(
- "Pelase fill {} or set environmental variables jans_host, jans_client_id ,and jans_client_secret and re-run".format(config_ini_fn)
- )
- sys.exit()
-
-
-def get_bool(val):
- if str(val).lower() in ('yes', 'true', '1', 'on'):
- return True
- return False
-
-
-debug = get_bool(debug)
-
-
-class Menu(object):
-
- def __init__(self, name, method='', info={}, path=''):
- self.name = name
- self.display_name = name
- self.method = method
- self.info = info
- self.path = path
- self.children = []
- self.parent = None
- self.ignore = False
-
- def __iter__(self):
- self.current_index = 0
- return self
-
- def __repr__(self):
- return self.display_name
- self.__print_child(self)
-
- def tree(self):
- print(self.name)
- self.__print_child(self)
-
- def __get_parent_number(self, child):
- n = 0
- while True:
- if not child.parent:
- break
- n += 1
- child = child.parent
-
- return n
-
- def __print_child(self, menu):
- if menu.children:
- for child in menu.children:
- print(' ' * self.__get_parent_number(child) * 2, child)
- self.__print_child(child)
-
- def add_child(self, node):
- assert isinstance(node, Menu)
- node.parent = self
- self.children.append(node)
-
- def get_child(self, n):
- if len(self.children) > n:
- return self.children[n]
-
- def __next__(self):
- if self.current_index < len(self.children):
- retVal = self.children[self.current_index]
- self.current_index += 1
- return retVal
-
- else:
- raise StopIteration
-
- def __contains__(self, child):
- for child_ in self.children:
- if child_.name == child:
- return True
- return False
-
-
-class JCA_CLI:
-
- def __init__(self, host, client_id, client_secret, access_token, test_client=False):
- self.host = host
- self.client_id = client_id
- self.client_secret = client_secret
- self.use_test_client = test_client
-
- self.swagger_configuration = swagger_client.Configuration()
- self.swagger_configuration.host = 'https://{}'.format(self.host)
- self.access_token = access_token or config['DEFAULT'].get('access_token')
-
- self.set_user()
- self.plugins()
-
- if not self.access_token and config['DEFAULT'].get('access_token_enc'):
- self.access_token = encode_decode(config['DEFAULT']['access_token_enc'], decode=True)
-
- if my_op_mode == 'scim':
- self.swagger_configuration.host += '/jans-scim/restv1/v2'
-
- self.ssl_settings()
-
- self.swagger_configuration.debug = debug
- if self.swagger_configuration.debug:
- self.swagger_configuration.logger_file = os.path.join(log_dir, 'swagger.log')
-
- self.swagger_yaml_fn = os.path.join(cur_dir, my_op_mode + '.yaml')
-
- self.cfg_yml = self.get_yaml()
- self.make_menu()
- self.current_menu = self.menu
- self.enums()
-
- def enums(self):
- self.enum_dict = {
- "CustomAttribute": {
- "properties.name": {
- "f": "get_attrib_list"
- }
- }
- }
-
- def set_user(self):
- self.auth_username = None
- self.auth_password = None
- self.askuser = get_bool(config['DEFAULT'].get('askuser'))
-
- if self.askuser:
- if args.username:
- self.auth_username = args.username
- if args.password:
- self.auth_password = args.password
- elif args.j:
- if os.path.isfile(args.j):
- with open(args.j) as reader:
- self.auth_password = reader.read()
- else:
- print(args.j, "does not exist. Exiting ...")
- sys.exit()
- if not (self.auth_username and self.auth_password):
- print("I need username and password. Exiting ...")
- sys.exit()
-
- def plugins(self):
- for plugin_s in config['DEFAULT'].get(my_op_mode + '_plugins', '').split(','):
- plugin = plugin_s.strip()
- if plugin:
- plugins.append(plugin)
-
- def ssl_settings(self):
- if args.noverify:
- self.swagger_configuration.verify_ssl = False
- else:
- self.swagger_configuration.verify_ssl = True
-
- if args.config_api_mtls_client_cert:
- self.swagger_configuration.cert_file = args.config_api_mtls_client_cert
-
- if args.config_api_mtls_client_key:
- self.swagger_configuration.key_file = args.config_api_mtls_client_key
-
- def drop_to_shell(self, mylocals):
- locals_ = locals()
- locals_.update(mylocals)
- code.interact(local=locals_)
- sys.exit()
-
- def get_yaml(self):
- debug_json = 'swagger_yaml.json'
- if os.path.exists(debug_json):
- with open(debug_json) as f:
- return json.load(f, object_pairs_hook=OrderedDict)
-
- with open(self.swagger_yaml_fn) as f:
- self.cfg_yml = ruamel.yaml.load(f.read().replace('\t', ''), ruamel.yaml.RoundTripLoader)
- if os.environ.get('dump_yaml'):
- with open(debug_json, 'w') as w:
- json.dump(self.cfg_yml, w, indent=2)
- return self.cfg_yml
-
- def get_rest_client(self):
- rest = swagger_client.rest.RESTClientObject(self.swagger_configuration)
- if args.key_password:
- rest.pool_manager.connection_pool_kw['key_password'] = args.key_password
- return rest
-
- def check_connection(self):
- rest = self.get_rest_client()
- headers = urllib3.make_headers(basic_auth='{}:{}'.format(self.client_id, self.client_secret))
- url = 'https://{}/jans-auth/restv1/token'.format(self.host)
- headers['Content-Type'] = 'application/x-www-form-urlencoded'
-
- response = rest.POST(
- url,
- headers=headers,
- post_params={"grant_type": "client_credentials"}
- )
-
- if response.status != 200:
- raise ValueError(
- self.colored_text("Unable to connect jans-auth server: {}".format(response.reason), error_color))
-
-
- def check_access_token(self):
- if not self.access_token :
- return False
-
- try:
- jwt.decode(self.access_token,
- options={
- 'verify_signature': False,
- 'verify_exp': True,
- 'verify_aud': False
- }
- )
- return True
- except Exception as e:
- print(self.colored_text("Unable to validate access token: {}".format(e), error_color))
- self.access_token = None
-
- return False
-
-
- def guess_param_mapping(self, param_s):
- word_list = re.sub( r"([A-Z])", r" \1", param_s).split()
- word_list = [w.lower() for w in word_list]
- param_name = '_'.join(word_list)
- return param_name
-
-
- def make_menu(self):
-
- menu_groups = []
-
- def get_sep_pos(s):
- for i, c in enumerate(s):
- if c in ('-', '–'):
- return i
- return -1
-
- def get_group_obj(mname):
- for grp in menu_groups:
- if grp.mname == mname:
- return grp
-
-
- for tag in self.cfg_yml['tags']:
- tname = tag['name'].strip()
- if tname == 'developers':
- continue
- n = get_sep_pos(tname)
- mname = tname[:n].strip() if n > -1 else tname
- grp = get_group_obj(mname)
- if not grp:
- grp = SimpleNamespace()
- grp.tag = None if n > -1 else tname
- grp.mname = mname
- grp.submenu = []
- menu_groups.append(grp)
-
- if n > -1:
- sname = tname[n+1:].strip()
- sub = SimpleNamespace()
- sub.tag = tname
- sub.mname = sname
- grp.submenu.append(sub)
-
-
- def get_methods_of_tag(tag):
- methods = []
- if tag:
- for path_name in self.cfg_yml['paths']:
- path = self.cfg_yml['paths'][path_name]
- path_parameters = []
- if 'parameters' in path:
- for pparam in path['parameters']:
- if pparam.get('in') == 'path':
- path_parameters.append(dict(pparam))
-
- for method_name in path:
- method = path[method_name]
-
-
-
- if hasattr(method, 'get') and method.get('operationId') in excluded_operations[my_op_mode]:
- continue
- if 'tags' in method and tag in method['tags'] and 'operationId' in method:
- if method.get('x-cli-plugin') and method['x-cli-plugin'] not in plugins:
- continue
- if path_parameters:
- method['__path_parameters__'] = path_parameters
- method['__method_name__'] = method_name
- method['__path_name__'] = path_name
- methods.append(method)
-
- return methods
-
- menu = Menu('Main Menu')
-
- for grp in menu_groups:
- methods = get_methods_of_tag(grp.tag)
- m = Menu(name=grp.mname)
- m.display_name = m.name + ' ˅'
- menu.add_child(m)
-
- for method in methods:
- for tag in method['tags']:
- menu_name = method.get('summary') or method.get('description')
- sm = Menu(
- name=menu_name.strip('.'),
- method=method['__method_name__'],
- info=method,
- path=method['__path_name__'],
- )
- m.add_child(sm)
-
- if grp.submenu:
- m.display_name = m.name + ' ˅'
- for sub in grp.submenu:
- methods = get_methods_of_tag(sub.tag)
- if not methods:
- continue
- smenu = Menu(name=sub.mname)
- smenu.display_name = smenu.name + ' ˅'
- m.add_child(smenu)
-
- for method in methods:
- for tag in method['tags']:
-
- sub_menu_name = method.get('summary') or method.get('description')
- ssm = Menu(
- name=sub_menu_name.strip('.'),
- method=method['__method_name__'],
- info=method,
- path=method['__path_name__'],
- )
- smenu.add_child(ssm)
-
- self.menu = menu
-
-
- def get_json_from_response(self, response):
- js_data = {}
- data = response.data
- if data:
- try:
- js_data = json.loads(data.decode())
- except:
- pass
- return js_data
-
- def get_scoped_access_token(self, scope):
- sys.stderr.write("Getting access token for scope {}\n".format(scope))
- rest = self.get_rest_client()
- headers = urllib3.make_headers(basic_auth='{}:{}'.format(self.client_id, self.client_secret))
- url = 'https://{}/jans-auth/restv1/token'.format(self.host)
- headers['Content-Type'] = 'application/x-www-form-urlencoded'
- if self.askuser:
- post_params = {"grant_type": "password", "scope": scope, "username": self.auth_username,
- "password": self.auth_password}
- else:
- post_params = {"grant_type": "client_credentials", "scope": scope}
-
- response = rest.POST(
- url,
- headers=headers,
- post_params=post_params
- )
-
- try:
- data = json.loads(response.data)
- if 'access_token' in data:
- self.swagger_configuration.access_token = data['access_token']
- else:
- sys.stderr.write("Error while getting access token")
- sys.stderr.write(data)
- sys.stderr.write('\n')
- except Exception as e:
- print("Error while getting access token")
- sys.stderr.write(response.data)
- sys.stderr.write(e)
- sys.stderr.write('\n')
-
-
- def get_jwt_access_token(self):
-
- rest = self.get_rest_client()
-
- """
- STEP 1: Get device verification code
- This fucntion requests user code from jans-auth, print result and
- waits untill verification done.
- """
-
- headers_basic_auth = urllib3.make_headers(basic_auth='{}:{}'.format(self.client_id, self.client_secret))
- headers_basic_auth['Content-Type'] = 'application/x-www-form-urlencoded'
- response = rest.POST(
- 'https://{}/jans-auth/restv1/device_authorization'.format(host),
- headers=headers_basic_auth,
- post_params={
- 'client_id': self.client_id,
- 'scope': 'openid+profile+email+offline_access'
- }
- )
-
- if response.status != 200:
- raise ValueError(
- self.colored_text("Unable to get device authorization user code: {}".format(response.reason), error_color))
-
- result = self.get_json_from_response(response.urllib3_response)
-
- if 'verification_uri' in result and 'user_code' in result:
-
- print("Please visit verification url {} and enter user code {} in {} secods".format(
- self.colored_text(result['verification_uri'], success_color),
- self.colored_text(result['user_code'], bold_color),
- result['expires_in']
- )
- )
-
- input(self.colored_text("Please press «Enter» when ready", warning_color))
-
- else:
- raise ValueError(self.colored_text("Unable to get device authorization user code"))
-
- """
- STEP 2: Get access token for retreiving user info
- After device code was verified, we use it to retreive refresh token
- """
- response = rest.POST(
- 'https://{}/jans-auth/restv1/token'.format(host),
- headers=headers_basic_auth,
- post_params=[
- ('client_id',self.client_id),
- ('scope','openid+profile+email+offline_access'),
- ('grant_type', 'urn:ietf:params:oauth:grant-type:device_code'),
- ('grant_type', 'refresh_token'),
- ('device_code',result['device_code'])
- ]
- )
-
- if response.status != 200:
- raise ValueError(
- self.colored_text("Unable to get access token"))
-
- result = self.get_json_from_response(response.urllib3_response)
-
-
- """
- STEP 3: Get user info
- refresh token is used for retreiving user information to identify user roles
- """
- headers_bearer = urllib3.make_headers()
- headers_bearer['Content-Type'] = 'application/x-www-form-urlencoded'
- headers_bearer['Authorization'] = 'Bearer {}'.format(result['access_token'])
- response = rest.POST(
- 'https://{}/jans-auth/restv1/userinfo'.format(host),
- headers=headers_bearer,
- post_params={
- 'access_token': result['access_token'],
- },
- )
-
- if response.status != 200:
- raise ValueError(
- self.colored_text("Unable to get access token"))
-
- result = response.urllib3_response.data.decode()
-
- """
- STEP 4: Get access token for config-api endpoints
- Use client creditentials to retreive access token for client endpoints.
- Since introception script will be executed, access token will have permissions with all scopes
- """
- response = rest.POST(
- 'https://{}/jans-auth/restv1/token'.format(host),
- headers=headers_basic_auth,
- post_params={
- 'grant_type': 'client_credentials',
- 'scope': 'openid',
- 'ujwt': result,
- },
- )
-
- if response.status != 200:
- raise ValueError(
- self.colored_text("Unable to get access token"))
-
- result = self.get_json_from_response(response.urllib3_response)
-
- self.access_token = result['access_token']
- access_token_enc = encode_decode(self.access_token)
- config['DEFAULT']['access_token_enc'] = access_token_enc
- write_config()
-
-
- def get_access_token(self, scope):
- if self.use_test_client:
- self.get_scoped_access_token(scope)
- else:
- if not self.check_access_token():
- self.get_jwt_access_token()
-
- if not self.use_test_client:
- self.swagger_configuration.access_token = self.access_token
-
- def print_exception(self, e):
- error_printed = False
- if hasattr(e, 'body'):
- try:
- jsdata = json.loads(e.body.decode())
- print(self.colored_text(e.body.decode(), error_color))
- error_printed = True
- except:
- pass
- if not error_printed:
- print(self.colored_text("Error retreiving data", warning_color))
- print('\u001b[38;5;196m')
- if hasattr(e, 'reason'):
- print(e.reason)
- if hasattr(e, 'body'):
- print(e.body)
- if hasattr(e, 'args'):
- print(', '.join(e.args))
- print('\u001b[0m')
-
- def colored_text(self, text, color=255):
- return u"\u001b[38;5;{}m{}\u001b[0m".format(color, text)
-
-
- def guess_bool(self, val):
- if val == '_false':
- return False
- if val == '_true':
- return True
-
-
- def check_type(self, val, vtype):
- if vtype == 'string' and val:
- return str(val)
- elif vtype == 'integer':
- if isinstance(val, int):
- return val
- if val.isnumeric():
- return int(val)
- elif vtype == 'object':
- try:
- retVal = json.loads(val)
- if isinstance(retVal, dict):
- return retVal
- except:
- pass
- elif vtype == 'boolean':
- guessed_val = self.guess_bool(val)
- if not guessed_val is None:
- return guessed_val
-
- error_text = "Please enter a(n) {} value".format(vtype)
- if vtype == 'boolean':
- error_text += ': _true, _false'
-
- raise TypeError(self.colored_text(error_text, warning_color))
-
- def get_input(self, values=[], text='Selection', default=None, itype=None,
- help_text=None, sitype=None, enforce='__true__',
- example=None, spacing=0
- ):
- if 'b' in values and 'q' in values and 'x' in values:
- greyed_help_list = [ ('b', 'back'), ('q', 'quit'), ('x', 'logout and quit') ]
- for k,v in (('w', 'write result'), ('y', 'yes'), ('n', 'no')):
- if k in values:
- greyed_help_list.insert(1, (k, v))
- grey_help_text = ', '.join(['{}: {}'.format(k,v) for k,v in greyed_help_list])
- print(self.colored_text(grey_help_text, grey_color))
- print()
- type_text = ''
- if itype:
- if itype == 'array':
- type_text = "Type: array of {} separated by _,".format(sitype)
- if values:
- type_text += ' Valid values: {}'.format(', '.join(values))
- elif itype == 'boolean':
- type_text = "Type: " + itype
- if default is None:
- default = False
- else:
- type_text = "Type: " + itype
- if values:
- type_text += ', Valid values: {}'.format(self.colored_text(', '.join(values), bold_color))
-
- if help_text:
- help_text = help_text.strip('.') + '. ' + type_text
- else:
- help_text = type_text
-
- if help_text:
- print(' ' * spacing, self.colored_text('«{}»'.format(help_text), 244), sep='')
-
- if example:
- join_char = '_,' if itype == 'array' else ', '
- if isinstance(example, list):
- example_str = join_char.join(example)
- else:
- example_str = str(example)
- if join_char == '_,':
- example_str = example_str.replace(' ', join_char)
-
- print(' ' * spacing, self.colored_text('Example: {}'.format(example_str), 244), sep='')
-
- if not default is None:
- default_text = str(default).lower() if itype == 'boolean' else str(default)
- text += ' [' + default_text + ']'
- if itype == 'integer':
- default = int(default)
-
- if not text.endswith('?'):
- text += ':'
-
- if itype == 'boolean' and not values:
- values = ['_true', '_false']
-
- while True:
-
- if args.no_suggestion:
- selection = input(' ' * spacing + self.colored_text(text, 20) + ' ')
- else:
- html_completer = WordCompleter(values)
- selection = prompt(HTML(' ' * spacing + text + ' '), completer=html_completer)
-
- selection = selection.strip()
-
- if selection == '_b':
- self.display_menu(self.current_menu)
- break
-
- if selection.startswith('_file '):
- fname = selection.split()[1]
- if os.path.isfile(fname):
- with open(fname) as f:
- selection = f.read().strip()
- else:
- print(self.colored_text("File {} does not exist".format(fname), warning_color))
- continue
-
- if itype == 'boolean' and not selection:
- return False
-
- if not selection and default:
- return default
-
- if enforce and not selection:
- continue
-
- if not enforce and not selection:
- if itype == 'array':
- return []
- return None
-
- if 'q' in values and selection == 'q':
- print("Quiting...")
- sys.exit()
-
- if 'x' in values and selection == 'x':
- print("Logging out...")
- if 'access_token_enc' in config['DEFAULT']:
- config['DEFAULT'].pop('access_token_enc')
- write_config()
- print("Quiting...")
- sys.exit()
- break
-
-
- if itype == 'object' and sitype:
- try:
- object_ = self.check_type(selection, itype)
- except Exception as e:
- print(' ' * spacing, e, sep='')
- continue
-
- data_ok = True
- for items in object_:
- try:
- self.check_type(object_[items], sitype)
- except Exception as e:
- print(' ' * spacing, e, sep='')
- data_ok = False
- if data_ok:
- return object_
- else:
- continue
-
- if itype == 'array' and default and not selection:
- return default
-
- if itype == 'array' and sitype:
- if selection == '_null':
- selection = []
- data_ok = True
- else:
- selection = selection.split('_,')
- for i, item in enumerate(selection):
- data_ok = True
- try:
- selection[i] = self.check_type(item.strip(), sitype)
- if selection[i] == '_null':
- selection[i] = None
- if values:
- if not selection[i] in values:
- data_ok = False
- print(' ' * spacing, self.colored_text(
- "Please enter array of {} separated by _,".format(', '.join(values)),
- warning_color), sep='')
- break
- except TypeError as e:
- print(' ' * spacing, e, sep='')
- data_ok = False
- if data_ok:
- break
- else:
- if not itype is None:
- try:
- selection = self.check_type(selection, itype)
- except TypeError as e:
- if enforce:
- print(' ' * spacing, e, sep='')
- continue
-
- if values:
- if selection in values:
- break
- elif itype == 'boolean':
- if isinstance(selection, bool):
- break
- else:
- continue
- else:
- print(' ' * spacing,
- self.colored_text('Please enter one of {}'.format(', '.join(values)), warning_color),
- sep='')
-
- if not values and not selection and not enforce:
- break
-
- if not values and selection:
- break
-
- if selection == '_null':
- selection = None
- elif selection == '_q':
- selection = 'q'
-
- return selection
-
- def print_underlined(self, text):
- print()
- print(text)
- print('-' * len(text.splitlines()[-1]))
-
- def print_colored_output(self, data):
- try:
- data_json = json.dumps(data, indent=2)
- except:
- data = ast.literal_eval(str(data))
- data_json = json.dumps(data, indent=2)
-
- print(self.colored_text(data_json, success_color))
-
- def pretty_print(self, data):
- pp = pprint.PrettyPrinter(indent=2)
- pp_string = pp.pformat(data)
- print(self.colored_text(pp_string, success_color))
-
- def get_url_param(self, url):
- if url.endswith('}'):
- pname = re.findall('/\{(.*?)\}$', url)[0]
- return pname
-
- def get_endpiont_url_param(self, endpoint):
- param = {}
- pname = self.get_url_param(endpoint.path)
- if pname:
- param = {'name': pname, 'description': pname, 'schema': {'type': 'string'}}
-
- return param
-
- def make_swagger_var(self, varname):
- word_list = re.sub(r'([A-Z])', r' \1', varname).lower().split()
- return '_'.join(word_list)
-
- def obtain_parameters(self, endpoint, single=False):
- parameters = {}
- end_point_param = {}
- endpoint_parameters = []
- if 'parameters' in endpoint.info:
- endpoint_parameters = endpoint.info['parameters']
-
-
- if '__path_parameters__' in endpoint.info:
- end_point_param = endpoint.info['__path_parameters__'][0]
-
- if end_point_param and not end_point_param in endpoint_parameters:
- endpoint_parameters.insert(0, end_point_param)
-
- n = 1 if single else len(endpoint_parameters)
-
- for param in endpoint_parameters[0:n]:
- param_name = self.make_swagger_var(param['name'])
- if not param_name in parameters:
- text_ = param['name']
- help_text = param.get('description') or param.get('summary')
- enforce = True if param['schema']['type'] == 'integer' or (end_point_param and end_point_param['name'] == param['name']) else False
-
- parameters[param_name] = self.get_input(
- text=text_.strip('.'),
- itype=param['schema']['type'],
- default=param['schema'].get('default'),
- enforce=enforce,
- help_text=help_text,
- example=param.get('example'),
- values=param['schema'].get('enum', [])
- )
-
- return parameters
-
- def get_name_from_string(self, txt):
- return re.sub(r'[^0-9a-zA-Z\s]+', '', txt)
-
- def get_api_class_name(self, name):
- namle_list = self.get_name_from_string(name).split()
- for i, w in enumerate(namle_list[:]):
- if len(w) > 1:
- w = w[0].upper() + w[1:]
- else:
- w = w.upper()
-
- namle_list[i] = w
-
- return ''.join(namle_list) + 'Api'
-
- def get_path_by_id(self, operation_id):
- retVal = {}
- for path in self.cfg_yml['paths']:
- for method in self.cfg_yml['paths'][path]:
- if 'operationId' in self.cfg_yml['paths'][path][method] and self.cfg_yml['paths'][path][method]['operationId'] == operation_id:
- retVal = self.cfg_yml['paths'][path][method].copy()
- retVal['__path__'] = path
- retVal['__method__'] = method
- retVal['__urlsuffix__'] = self.get_url_param(path)
-
- return retVal
-
- def get_tag_from_api_name(self, api_name, qmethod=None):
-
- for tag in self.cfg_yml['tags']:
- api_class_name = self.get_api_class_name(tag['name'])
- if api_class_name == api_name:
- break
-
- paths = []
-
- for path in self.cfg_yml['paths']:
-
- for method in self.cfg_yml['paths'][path]:
-
- if 'tags' in self.cfg_yml['paths'][path][method] and tag['name'] in self.cfg_yml['paths'][path][method]['tags'] and 'operationId' in self.cfg_yml['paths'][path][method]:
- retVal = self.cfg_yml['paths'][path][method].copy()
- retVal['__path__'] = path
- retVal['__method__'] = method
- retVal['__urlsuffix__'] = self.get_url_param(path)
- if qmethod:
- if method == qmethod:
- paths.append(retVal)
- else:
- paths.append(retVal)
-
- return paths
-
- def get_scope_for_endpoint(self, endpoint):
- scope = []
- for security in endpoint.info.get('security', []):
- for stype in security:
- scope += security[stype]
-
- return ' '.join(scope)
-
- def unmap_model(self, model, data_dict=None):
- if data_dict is None:
- data_dict = {}
-
- for key_ in model.attribute_map:
-
- val = getattr(model, key_)
- if isinstance(val, datetime.date):
- val = str(val)
-
- if isinstance(val, list):
- sub_list = []
- for entry in val:
- if hasattr(entry, 'swagger_types'):
- sub_list_dict = {}
- self.unmap_model(entry, sub_list_dict)
- sub_list.append(sub_list_dict)
- else:
- sub_list.append(entry)
- data_dict[model.attribute_map[key_]] = sub_list
- elif hasattr(val, 'swagger_types'):
- sub_data_dict = {}
- self.unmap_model(val, sub_data_dict)
- data_dict[model.attribute_map[key_]] = sub_data_dict
- else:
- data_dict[model.attribute_map[key_]] = val
-
- return data_dict
-
- def get_model_key_map(self, model, key):
- key_underscore = key.replace('-', '_')
- for key_ in model.attribute_map:
- if model.attribute_map[key_] == key or model.attribute_map[key_] == key_underscore:
- return key_
-
- def tabular_data(self, data, ome):
- tab_data = []
- headers = tabulate_endpoints[ome]
- for i, entry in enumerate(data):
- row_ = [i + 1]
- for header in headers:
- row_.append(str(entry.get(header, '')))
- tab_data.append(row_)
-
- print(tabulate(tab_data, headers, tablefmt="grid"))
-
- def process_get(self, endpoint, return_value=False, parameters=None):
- clear()
- if not return_value:
- title = endpoint.name
- if endpoint.name != endpoint.info['description'].strip('.'):
- title += '\n' + endpoint.info['description']
-
- self.print_underlined(title)
-
- if not parameters:
- parameters = self.obtain_parameters(endpoint, single=return_value)
-
- for param in parameters.copy():
- if not parameters[param]:
- del parameters[param]
-
- if parameters and not return_value:
- print("Calling Api with parameters:", parameters)
-
- print("Please wait while retreiving data ...\n")
-
- api_caller = self.get_api_caller(endpoint)
-
- api_response = None
- raise_error = False
- try:
- api_response = api_caller(**parameters)
- except Exception as e:
- if return_value:
- raise_error = True
- else:
- self.print_exception(e)
-
- if raise_error:
- raise ValueError('Not found')
-
- if return_value:
- if api_response:
- return api_response
- return False
-
- selections = ['q', 'x', 'b']
- item_counters = []
- tabulated = False
-
- if api_response:
- try:
- if 'response' in api_response:
- api_response = api_response['response']
- except:
- pass
-
- selections.append('w')
- api_response_unmapped = []
- if isinstance(api_response, list):
- for model in api_response:
- data_dict = self.unmap_model(model)
- api_response_unmapped.append(data_dict)
- elif isinstance(api_response, dict):
- api_response_unmapped = api_response
- else:
- data_dict = self.unmap_model(api_response)
- api_response_unmapped = data_dict
-
- op_mode_endpoint = my_op_mode + '.' + endpoint.info['operationId']
-
- if op_mode_endpoint in tabulate_endpoints:
- api_response_unmapped_ext = copy.deepcopy(api_response_unmapped)
- if 'entries' in api_response_unmapped_ext:
- api_response_unmapped_ext = api_response_unmapped_ext['entries']
-
- if endpoint.info['operationId'] == 'get-user':
- for entry in api_response_unmapped_ext:
- if entry.get('customAttributes'):
- for attrib in entry['customAttributes']:
- if attrib['name'] == 'mail':
- entry['mail'] = ', '.join(attrib['values'])
- elif attrib['name'] in tabulate_endpoints[op_mode_endpoint]:
- entry[attrib['name']] = attrib['values'][0]
-
- if endpoint.info['operationId'] == 'get-oauth-openid-clients':
- for entry in api_response_unmapped_ext:
- for custom_attrib in entry.get('customAttributes', []):
- if custom_attrib.get('name') == 'displayName':
- entry['displayName'] = custom_attrib.get('value') or custom_attrib.get('values',['?'])[0]
- break
- if isinstance(entry['clientName'], dict) and 'value' in entry['clientName']:
- entry['clientName'] = entry['clientName']['value']
-
- tab_data = api_response_unmapped_ext
- if op_mode_endpoint in tabular_dataset:
- tab_data = api_response_unmapped_ext[tabular_dataset[op_mode_endpoint]]
- self.tabular_data(tab_data, op_mode_endpoint)
- item_counters = [str(i + 1) for i in range(len(tab_data))]
- tabulated = True
- else:
- self.print_colored_output(api_response_unmapped)
-
- selections += item_counters
- while True:
- selection = self.get_input(selections)
- if selection == 'b':
- self.display_menu(endpoint.parent)
- break
- elif selection == 'w':
- fn = input('File name: ')
- try:
- with open(fn, 'w') as w:
- json.dump(api_response_unmapped, w, indent=2)
- print("Output was written to", fn)
- except Exception as e:
- print("An error ocurred while saving data")
- self.print_exception(e)
- elif selection in item_counters:
- if my_op_mode == 'scim' and 'Resources' in api_response_unmapped:
- items = api_response_unmapped['Resources']
- elif my_op_mode == 'jca' and 'entries' in api_response_unmapped:
- items = api_response_unmapped['entries']
- self.pretty_print(items[int(selection) - 1])
-
- def get_schema_from_reference(self, ref):
- schema_path_list = ref.strip('/#').split('/')
- schema = self.cfg_yml[schema_path_list[0]]
-
- schema_ = schema.copy()
-
- for p in schema_path_list[1:]:
- schema_ = schema_[p]
-
- if 'allOf' in schema_:
- all_schema = OrderedDict()
- all_schema['required'] = []
-
- all_schema['properties'] = OrderedDict()
- for sch in schema_['allOf']:
- if '$ref' in sch:
- all_schema.update(self.get_schema_from_reference(sch['$ref']))
- elif 'properties' in sch:
- for sprop in sch['properties']:
- all_schema['properties'][sprop] = sch['properties'][sprop]
- all_schema['required'] += sch.get('required', [])
-
- schema_ = all_schema
-
- for key_ in schema_.get('properties', []):
- if '$ref' in schema_['properties'][key_]:
- schema_['properties'][key_] = self.get_schema_from_reference(schema_['properties'][key_]['$ref'])
- elif schema_['properties'][key_].get('type') == 'array' and '$ref' in schema_['properties'][key_]['items']:
- ref_path = schema_['properties'][key_]['items'].pop('$ref')
- ref_schema = self.get_schema_from_reference(ref_path)
- schema_['properties'][key_]['properties'] = ref_schema['properties']
- schema_['properties'][key_]['title'] = ref_schema['title']
- schema_['properties'][key_]['description'] = ref_schema.get('description', '')
- schema_['properties'][key_]['__schema_name__'] = ref_schema['__schema_name__']
-
- if not 'title' in schema_:
- schema_['title'] = p
-
- schema_['__schema_name__'] = p
-
- return schema_
-
- def get_scheme_for_endpoint(self, endpoint):
- schema_ = {}
- for content_type in endpoint.info.get('requestBody', {}).get('content', {}):
- if 'schema' in endpoint.info['requestBody']['content'][content_type]:
- schema = endpoint.info['requestBody']['content'][content_type]['schema']
- break
- else:
- return schema_
-
- schema_ = schema.copy()
-
- if schema_.get('type') == 'array':
- schma_ref = schema_.get('items', {}).pop('$ref')
- else:
- schma_ref = schema_.pop('$ref')
-
- if schma_ref:
- schema_ref_ = self.get_schema_from_reference(schma_ref)
- schema_.update(schema_ref_)
-
- return schema_
-
- def get_swagger_types(self, model, name):
- for attribute in model.swagger_types:
- if model.swagger_types[attribute] == name:
- return attribute
-
- def get_attrib_list(self):
- for parent in self.menu:
- for children in parent:
- if children.info.get('operationId') == 'get-attributes':
- attributes = self.process_get(children, return_value=True, parameters={'limit': 1000} )
- attrib_names = []
- for a in attributes:
- attrib_names.append(a.name)
- attrib_names.sort()
- return attrib_names
-
- def get_enum(self, schema):
- if schema['__schema_name__'] in self.enum_dict:
- enum_obj = schema
-
- for path in self.enum_dict[schema['__schema_name__']].copy():
- for p in path.split('.'):
- enum_obj = enum_obj[p]
-
- if not 'enum' in self.enum_dict[schema['__schema_name__']][path]:
- self.enum_dict[schema['__schema_name__']][path]['enum'] = getattr(self, self.enum_dict[schema['__schema_name__']][path]['f'])()
-
- enum_obj['enum'] = self.enum_dict[schema['__schema_name__']][path]['enum']
-
-
- def get_input_for_schema_(self, schema, model, spacing=0, initialised=False, getitem=None, required_only=False):
-
- self.get_enum(schema)
- data = {}
- for prop in schema['properties']:
- item = schema['properties'][prop]
- if getitem and prop != getitem['__name__'] or prop in ('dn', 'inum'):
- continue
-
- if required_only and not prop in schema.get('required', []):
- continue
-
- prop_ = self.get_model_key_map(model, prop)
- if item['type'] == 'object' and 'properties' in item:
- print()
- print("Data for object {}. {}".format(prop, item.get('description', '')))
-
- model_name_str = item.get('__schema_name__') or item.get('title') or item.get('description')
- model_name = self.get_name_from_string(model_name_str)
-
- if initialised and getattr(model, prop_):
- sub_model = getattr(model, prop_)
- self.get_input_for_schema_(item, sub_model, spacing=3, initialised=initialised)
- elif isinstance(model, type) and hasattr(swagger_client.models, model_name):
- sub_model_class = getattr(swagger_client.models, model_name)
- result = self.get_input_for_schema_(item, sub_model_class, spacing=3, initialised=initialised)
- setattr(model, prop_, result)
- elif hasattr(swagger_client.models, model.swagger_types[prop_]):
- sub_model = getattr(swagger_client.models, model.swagger_types[prop_])
- result = self.get_input_for_schema_(item, sub_model, spacing=3, initialised=initialised)
- setattr(model, prop_, result)
- else:
- sub_model = getattr(model, prop_)
- self.get_input_for_schema_(item, sub_model, spacing=3, initialised=initialised)
- # print(self.colored_text("Fix me: can't find model", error_color))
-
- elif item['type'] == 'array' and '__schema_name__' in item:
- model_name = item['__schema_name__']
- sub_model_class = getattr(swagger_client.models, model_name)
- sub_model_list = []
- sub_model_list_help_text = ''
- sub_model_list_title_text = item.get('title')
- if sub_model_list_title_text:
- sub_model_list_help_text = item.get('description')
- else:
- sub_model_list_title_text = item.get('description')
-
- cur_model_data = getattr(model, prop_)
-
- if cur_model_data and initialised:
- for cur_data in cur_model_data:
- print("\nUpdate {}".format(sub_model_list_title_text))
- cur_model_data = self.get_input_for_schema_(item, cur_data, spacing=spacing + 3)
- sub_model_list.append(cur_model_data)
-
- sub_model_list_selection = self.get_input(text="Add {}?".format(sub_model_list_title_text),
- values=['y', 'n'], help_text=sub_model_list_help_text)
-
- if sub_model_list_selection == 'y':
- while True:
- sub_model_list_data = self.get_input_for_schema_(item, sub_model_class, spacing=spacing + 3)
- sub_model_list.append(sub_model_list_data)
- sub_model_list_selection = self.get_input(
- text="Add another {}?".format(sub_model_list_title_text), values=['y', 'n'])
- if sub_model_list_selection == 'n':
- break
-
- data[prop_] = sub_model_list
-
- else:
- default = getattr(model, prop_)
- if isinstance(default, property):
- default = None
- enforce = True if item['type'] == 'boolean' else False
-
- if prop in schema.get('required', []):
- enforce = True
-
- if not default:
- default = item.get('default')
-
- values_ = item.get('enum', [])
- if not values_ and item['type'] == 'array' and 'enum' in item['items']:
- values_ = item['items']['enum']
- if item['type'] == 'object' and not default:
- default = {}
-
- if not values_:
- values_ = []
-
- val = self.get_input(
- values=values_,
- text=prop,
- default=default,
- itype=item['type'],
- help_text=item.get('description'),
- sitype=item.get('items', {}).get('type'),
- enforce=enforce,
- example=item.get('example'),
- spacing=spacing
- )
- data[prop_] = val
-
- if model.__class__.__name__ == 'type':
- modelObject = model(**data)
- for key_ in data:
- if data[key_] and not getattr(modelObject, key_, None):
- setattr(modelObject, key_, data[key_])
- return modelObject
- else:
- for key_ in data:
- setattr(model, key_, data[key_])
-
- return model
-
- def get_api_caller(self, endpoint):
- security = self.get_scope_for_endpoint(endpoint)
- if security.strip():
- self.get_access_token(security)
-
- client = getattr(swagger_client, self.get_api_class_name(endpoint.info['tags'][0]))
- api_instance = self.get_api_instance(client)
- api_caller = getattr(api_instance, endpoint.info['operationId'].replace('-', '_'))
-
- return api_caller
-
- def process_post(self, endpoint):
- schema = self.get_scheme_for_endpoint(endpoint)
-
- if schema:
-
- title = schema.get('description') or schema['title']
- data_dict = {}
-
- model_class = getattr(swagger_client.models, schema['__schema_name__'])
-
- if my_op_mode == 'scim':
- if endpoint.path == '/jans-scim/restv1/v2/Groups':
- schema['properties']['schemas']['default'] = ['urn:ietf:params:scim:schemas:core:2.0:Group']
- elif endpoint.path == '/jans-scim/restv1/v2/Users':
- schema['properties']['schemas']['default'] = ['urn:ietf:params:scim:schemas:core:2.0:User']
- if endpoint.info['operationId'] == 'create-user':
- schema['required'] = ['userName', 'name', 'displayName', 'emails', 'password']
-
- model = self.get_input_for_schema_(schema, model_class, required_only=True)
-
- optional_fields = []
- required_fields = schema.get('required', []) + ['dn', 'inum']
- for field in schema['properties']:
- if not field in required_fields:
- optional_fields.append(field)
-
- optional_fields.sort()
- if optional_fields:
- fill_optional = self.get_input(values=['y', 'n'], text='Populate optional fields?')
- fields_numbers = []
- if fill_optional == 'y':
- print("Optional Fields:")
- for i, field in enumerate(optional_fields):
- print(i + 1, field)
- fields_numbers.append(str(i + 1))
-
- while True:
- optional_selection = self.get_input(values=['q', 'x', 'c'] + fields_numbers,
- help_text="c: continue, #: populate field")
- if optional_selection == 'c':
- break
- if optional_selection in fields_numbers:
- item_name = optional_fields[int(optional_selection) - 1]
- schema_item = schema['properties'][item_name].copy()
- schema_item['__name__'] = item_name
- self.get_input_for_schema_(schema, model, initialised=True, getitem=schema_item)
-
- print("Obtained Data:\n")
- model_unmapped = self.unmap_model(model)
- self.print_colored_output(model_unmapped)
-
- selection = self.get_input(values=['q', 'x', 'b', 'y', 'n'], text='Continue?')
-
- else:
- selection = 'y'
- model = None
-
-
- path_vals = {}
- if '__path_parameters__' in endpoint.info:
- for pparam in endpoint.info['__path_parameters__']:
- swagger_var = self.make_swagger_var(pparam['name'])
- path_vals[swagger_var] = self.get_input(
- values=pparam['schema'].get('enum', []),
- text=pparam['name'],
- itype=pparam['schema']['type'],
- help_text= pparam.get('description'),
- enforce='__true__',
- )
-
-
- if selection == 'y':
- api_caller = self.get_api_caller(endpoint)
- print("Please wait while posting data ...\n")
-
- try:
- if model:
- if path_vals:
- api_response = api_caller(**path_vals, body=model)
- else:
- api_response = api_caller(body=model)
-
- else:
- api_response = api_caller(**path_vals)
-
- except Exception as e:
- api_response = None
- self.print_exception(e)
-
- if api_response:
- try:
- api_response_unmapped = self.unmap_model(api_response)
- self.print_colored_output(api_response_unmapped)
- except:
- print(self.colored_text(str(api_response), success_color))
-
- selection = self.get_input(values=['q', 'x', 'b'])
- if selection in ('b', 'n'):
- self.display_menu(endpoint.parent)
-
- def process_delete(self, endpoint):
- url_param = self.get_endpiont_url_param(endpoint)
- if url_param:
- url_param_val = self.get_input(text=url_param['name'], help_text='Entry to be deleted')
- else:
- url_param_val = ''
- selection = self.get_input(text="Are you sure want to delete {} ?".format(url_param_val),
- values=['b', 'y', 'n', 'q', 'x'])
- if selection in ('b', 'n'):
- self.display_menu(endpoint.parent)
- elif selection == 'y':
- api_caller = self.get_api_caller(endpoint)
- print("Please wait while deleting {} ...\n".format(url_param_val))
- api_response = '__result__'
-
- try:
- api_response = api_caller(url_param_val) if url_param_val else api_caller()
- except Exception as e:
- self.print_exception(e)
-
- if api_response is None:
- print(self.colored_text("\nEntry {} was deleted successfully\n".format(url_param_val), success_color))
-
- selection = self.get_input(['b', 'q', 'x'])
- if selection == 'b':
- self.display_menu(endpoint.parent)
-
- def process_patch(self, endpoint):
- if endpoint.info['operationId'] == 'patch-user-by-inum':
- schema = self.cfg_yml['components']['schemas']['CustomAttribute'].copy()
- schema['__schema_name__'] = 'CustomAttribute'
- model = getattr(swagger_client.models, 'CustomAttribute')
- elif 'PatchOperation' in self.cfg_yml['components']['schemas']:
- schema = self.cfg_yml['components']['schemas']['PatchOperation'].copy()
- model = getattr(swagger_client.models, 'PatchOperation')
- for item in schema['properties']:
- if not 'type' in schema['properties'][item]:
- schema['properties'][item]['type'] = 'string'
- schema['__schema_name__'] = 'PatchOperation'
- else:
- schema = self.cfg_yml['components']['schemas']['PatchRequest'].copy()
- schema['__schema_name__'] = 'PatchRequest'
- model = getattr(swagger_client.models, 'PatchRequest')
-
- parent_schema = {}
-
- schema_ref = endpoint.info.get('responses', {}).get('200', {}).get('content', {}).get('application/json', {}).get('schema', {}).get('$ref')
- if schema_ref:
- parent_schema = self.get_schema_from_reference(schema_ref)
-
- url_param_val = None
- url_param = self.get_endpiont_url_param(endpoint)
- if 'name' in url_param:
- url_param_val = self.get_input(text=url_param['name'], help_text='Entry to be patched')
- body = []
-
- if endpoint.info['operationId'] == 'patch-user-by-inum':
- patch_op = self.get_input(text="Patch operation", values=['add', 'remove', 'replace'], help_text='The operation to be performed')
-
- while True:
- data = self.get_input_for_schema_(schema, model)
- if endpoint.info['operationId'] != 'patch-user-by-inum':
- guessed_val = self.guess_bool(data.value)
- if not guessed_val is None:
- data.value = guessed_val
- if my_op_mode != 'scim' and not data.path.startswith('/'):
- data.path = '/' + data.path
-
- if my_op_mode == 'scim':
- data.path = data.path.replace('/', '.')
-
- if parent_schema and 'properties' in parent_schema:
- for prop_ in parent_schema['properties']:
- if data.path.lstrip('/') == prop_:
- if parent_schema['properties'][prop_]['type'] == 'array':
- data.value = data.value.split('_,')
- body.append(data)
- selection = self.get_input(text='Another patch operation?', values=['y', 'n'])
- if selection == 'n':
- break
-
- unmapped_body = []
- for item in body:
- unmapped_body.append(self.unmap_model(item))
-
- self.print_colored_output(unmapped_body)
-
- selection = self.get_input(values=['y', 'n'], text='Continue?')
-
- if selection == 'y':
-
- api_caller = self.get_api_caller(endpoint)
-
- print("Please wait patching...\n")
-
- if my_op_mode == 'scim':
- body = {'schemas': ['urn:ietf:params:scim:api:messages:2.0:PatchOp'], 'Operations': body}
- elif endpoint.info['operationId'] == 'patch-user-by-inum':
- patch_data = {'jsonPatchString': json.dumps([{'op': patch_op, 'path': '/dn', 'value': 'inum={},ou=people,o=jans'.format(url_param_val)}]), 'customAttributes':unmapped_body}
- body = patch_data
- try:
- if url_param_val:
- param_mapping = self.guess_param_mapping(url_param['name'])
- payload = {param_mapping: url_param_val, 'body': body}
- api_response = api_caller(**payload)
- else:
- api_response = api_caller(body=body)
- except Exception as e:
- api_response = None
- self.print_exception(e)
-
- if api_response:
- api_response_unmapped = self.unmap_model(api_response)
- self.print_colored_output(api_response_unmapped)
-
- selection = self.get_input(['b'])
- if selection == 'b':
- self.display_menu(endpoint.parent)
-
- def process_put(self, endpoint):
-
- schema = self.get_scheme_for_endpoint(endpoint)
-
- initialised = False
- cur_model = None
- go_back = False
- key_name = None
- parent_model = None
-
- if endpoint.info.get('x-cli-getdata') != '_file':
- if 'x-cli-getdata' in endpoint.info and endpoint.info['x-cli-getdata'] != None:
- for m in endpoint.parent:
- if m.info['operationId'] == endpoint.info['x-cli-getdata']:
- while True:
- try:
- cur_model = self.process_get(m, return_value=True)
- break
- except ValueError as e:
- print(self.colored_text("Server returned no data", error_color))
- retry = self.get_input(values=['y', 'n'], text='Retry?')
- if retry == 'n':
- self.display_menu(endpoint.parent)
- break
- initialised = True
- get_endpoint = m
- break
-
- else:
- if endpoint.info['operationId'] == 'put-properties-fido2':
- for m in endpoint.parent:
- if m.method == 'get':
- break
- cur_model = self.process_get(m, return_value=True)
- initialised = True
- get_endpoint = m
-
- else:
- for m in endpoint.parent:
- if m.method == 'get' and m.path.endswith('}'):
- while True:
- while True:
- try:
- key_name_desc = self.get_endpiont_url_param(m)
- if key_name_desc and 'name' in key_name_desc:
- key_name = key_name_desc['name']
- cur_model = self.process_get(m, return_value=True)
- break
- except ValueError as e:
- print(self.colored_text("Server returned no data", error_color))
- retry = self.get_input(values=['y', 'n'], text='Retry?')
- if retry == 'n':
- self.display_menu(endpoint.parent)
- break
-
- if not cur_model is False:
- break
-
- initialised = True
- get_endpoint = m
- break
-
- if not cur_model:
- for m in endpoint.parent:
- if m.method == 'get' and not m.path.endswith('}'):
- cur_model = self.process_get(m, return_value=True)
- get_endpoint = m
-
-
- if not cur_model:
- cur_model = getattr(swagger_client.models, schema['__schema_name__'])
-
- end_point_param = self.get_endpiont_url_param(endpoint)
-
-
- if cur_model:
-
- if endpoint.info.get('x-cli-getdata') == '_file':
-
- schema_desc = schema.get('description') or schema['__schema_name__']
- text = 'Enter filename to load data for «{}»: '.format(schema_desc)
- data_fn = input(self.colored_text(text, 244))
- if data_fn == 'b':
- go_back = True
- elif data_fn == 'q':
- sys.exit()
- else:
- data_org = self.get_json_from_file(data_fn)
-
- data = {}
- for k in data_org:
- if k in cur_model.attribute_map:
- mapped_key = cur_model.attribute_map[k]
- data[mapped_key] = data_org[k]
- else:
- data[k] = data_org[k]
-
- api_caller = self.get_api_caller(endpoint)
-
- print("Please wait while posting data ...\n")
-
- try:
- api_response = api_caller(body=data)
- except Exception as e:
- api_response = None
- self.print_exception(e)
-
- if api_response:
- api_response_unmapped = self.unmap_model(api_response)
- self.print_colored_output(api_response_unmapped)
-
- selection = self.get_input(values=['q', 'x', 'b'])
- if selection == 'b':
- self.display_menu(endpoint.parent)
-
-
- else:
-
- end_point_param_val = None
- if end_point_param:
- end_point_param_val = getattr(cur_model, end_point_param['name'], None) or self.get_model_key_map(cur_model, end_point_param['name'])
-
- attr_name_list = []
- for attr_name in cur_model.attribute_map:
- if attr_name != 'dn':
- attr_name_list.append(cur_model.attribute_map[attr_name])
-
- attr_name_list.sort()
- item_numbers = []
-
- def print_fields():
- print("Fields:")
- for i, attr_name in enumerate(attr_name_list):
- print(str(i + 1).rjust(2), attr_name)
- item_numbers.append(str(i + 1))
-
- print_fields()
- changed_items = []
- selection_list = ['q', 'x', 'b', 'v', 's', 'l'] + item_numbers
- help_text = 'q: quit, v: view, s: save, l: list fields #: update field'
-
- while True:
- selection = self.get_input(values=selection_list, help_text=help_text)
- if selection == 'v':
- self.pretty_print(self.unmap_model(cur_model))
- elif selection == 'l':
- print_fields()
- elif selection in item_numbers:
- item = attr_name_list[int(selection) - 1]
- item_unmapped = self.get_model_key_map(cur_model, item)
- if schema['properties'].get('keys', {}).get('properties'):
- schema = schema['properties']['keys']
-
- schema_item = schema['properties'][item]
- schema_item['__name__'] = item
- self.get_input_for_schema_(schema, cur_model, initialised=initialised, getitem=schema_item)
- changed_items.append(item)
-
- if selection == 'b':
- self.display_menu(endpoint.parent)
- break
- elif selection == 's':
- print('Changes:')
- for ci in changed_items:
- model_key = self.get_model_key_map(cur_model, ci)
- str_val = str(getattr(cur_model, model_key))
- print(self.colored_text(ci, bold_color) + ':', self.colored_text(str_val, success_color))
-
- selection = self.get_input(values=['y', 'n'], text='Continue?')
-
- if selection == 'y':
- schema_must = self.get_scheme_for_endpoint(endpoint)
- if schema_must['__schema_name__'] != cur_model.__class__.__name__:
- for e in endpoint.parent.children:
- if e.method == 'get':
- parent_model = self.process_get(e, return_value=True)
- break
-
-
- if parent_model and key_name and hasattr(parent_model, 'keys'):
- for i, wkey in enumerate(parent_model.keys):
- if getattr(wkey, key_name) == getattr(cur_model, key_name):
- parent_model.keys[i] = cur_model
- cur_model = parent_model
- break
-
- print("Please wait while posting data ...\n")
- api_caller = self.get_api_caller(endpoint)
- put_pname = self.get_url_param(endpoint.path)
-
- try:
- if put_pname:
- args_ = {'body': cur_model, put_pname: end_point_param_val}
- api_response = api_caller(**args_)
- else:
- api_response = api_caller(body=cur_model)
- except Exception as e:
- api_response = None
- self.print_exception(e)
-
- if api_response:
- api_response_unmapped = self.unmap_model(api_response)
- self.print_colored_output(api_response_unmapped)
- go_back = True
- break
-
- if go_back:
- selection = self.get_input(values=['q', 'x', 'b'])
- if selection == 'b':
- self.display_menu(endpoint.parent)
- else:
- self.get_input_for_schema_(schema, cur_model, initialised=initialised)
-
- def display_menu(self, menu):
- clear()
- self.current_menu = menu
-
- name_list = [menu.name]
- par = menu
- while True:
- par = par.parent
- if not par:
- break
- name_list.insert(0, par.name)
-
- if len(name_list) > 1:
- del name_list[0]
-
- self.print_underlined(': '.join(name_list))
-
- selection_values = ['q', 'x', 'b']
-
- menu_numbering = {}
-
- c = 0
- for i, item in enumerate(menu):
- if item.info.get('x-cli-ignore') or (item.parent.name == 'Main Menu' and not item.children):
- continue
-
- print(c + 1, item)
- selection_values.append(str(c + 1))
- menu_numbering[c + 1] = i
- c += 1
-
- selection = self.get_input(selection_values)
-
- if selection == 'b' and not menu.parent:
- print("Quiting...")
- sys.exit()
- elif selection == 'b':
- self.display_menu(menu.parent)
- elif int(selection) in menu_numbering and menu.get_child(menu_numbering[int(selection)]).children:
- self.display_menu(menu.get_child(menu_numbering[int(selection)]))
- else:
- m = menu.get_child(menu_numbering[int(selection)])
- getattr(self, 'process_' + m.method)(m)
-
- def parse_command_args(self, args):
- args_dict = {}
-
- if args:
- for arg in args.split(','):
- neq = arg.find(':')
- if neq > 1:
- arg_name = arg[:neq].strip()
- arg_val = arg[neq + 1:].strip()
- if arg_name and arg_val:
- args_dict[arg_name] = arg_val
-
- return args_dict
-
- def parse_args(self, args, path):
- param_names = []
- if not 'parameters' in path:
- return {}
- for param in path['parameters']:
- param_names.append(param['name'])
-
- args_dict = self.parse_command_args(args)
-
- for arg_name in args_dict:
- if not arg_name in param_names:
- self.exit_with_error("valid endpoint args are: {}".format(', '.join(param_names)))
-
- return args_dict
-
- def help_for(self, op_name):
- paths = self.get_tag_from_api_name(op_name + 'Api')
-
- schema_path = None
-
- for path in paths:
- if 'tags' in path:
- print('Operation ID:', path['operationId'])
- print(' Description:', path['description'])
- if path.get('__urlsuffix__'):
- print(' url-suffix:', path['__urlsuffix__'])
- if 'parameters' in path:
- param_names = []
- for param in path['parameters']:
- desc = param.get('description', 'No description is provided for this parameter')
- param_type = param.get('schema', {}).get('type')
- if param_type:
- desc += ' [{}]'.format(param_type)
- param_names.append((param['name'], desc))
- if param_names:
- print(' Parameters:')
- for param in param_names:
- print(' {}: {}'.format(param[0], param[1]))
-
- if 'requestBody' in path:
- for apptype in path['requestBody'].get('content', {}):
- if 'schema' in path['requestBody']['content'][apptype]:
- if path['requestBody']['content'][apptype]['schema'].get('type') == 'array':
- schema_path = path['requestBody']['content'][apptype]['schema']['items']['$ref']
- print(' Schema: Array of {}'.format(schema_path[1:]))
- else:
- spparent = path['requestBody']['content'][apptype]['schema']
- schema_path = spparent.get('$ref')
- if schema_path:
- print(' Schema: {}'.format(schema_path[1:]))
- else:
- print(' Data type: {}'.format(spparent.get('type')))
-
-
- if schema_path:
- print()
- print("To get sample schema type {0} --schema , for example {0} --schema {1}".format(sys.argv[0],
- schema_path[
- 1:]))
-
- def render_json_entry(self, val):
- if isinstance(val, str) and val.startswith('_file '):
- file_path = val[6:].strip()
- if os.path.exists(file_path):
- with open(file_path) as f:
- val = f.read()
- else:
- raise ValueError("File '{}' not found".format(file_path))
- return val
-
- def get_json_from_file(self, data_fn):
-
- if not os.path.exists(data_fn):
- self.exit_with_error("Can't find file {}".format(data_fn))
-
- try:
- with open(data_fn) as f:
- data = json.load(f)
- except:
- self.exit_with_error("Error parsing json file {}".format(data_fn))
-
- if isinstance(data, list):
- for entry in data:
- if isinstance(entry, dict):
- for k in entry:
- entry[k] = self.render_json_entry(entry[k])
-
- if isinstance(data, dict):
- for k in data:
- data[k] = self.render_json_entry(data[k])
-
- return data
-
- def get_api_instance(self, client):
- api_instance = client(swagger_client.ApiClient(self.swagger_configuration))
- if args.key_password:
- api_instance.api_client.rest_client.pool_manager.connection_pool_kw['key_password'] = args.key_password
- return api_instance
-
- def get_path_api_caller_for_path(self, path):
-
- dummy_enpoint = Menu(name='', info=path)
- security = self.get_scope_for_endpoint(dummy_enpoint)
- if security.strip():
- self.get_access_token(security)
- class_name = self.get_api_class_name(path['tags'][0])
- client = getattr(swagger_client, class_name)
- api_instance = self.get_api_instance(client)
- api_caller = getattr(api_instance, path['operationId'].replace('-', '_'))
-
- return api_caller
-
- def process_command_get(self, path, suffix_param, endpoint_params, data_fn, data=None):
- api_caller = self.get_path_api_caller_for_path(path)
- api_response = None
- encoded_param = urlencode(endpoint_params)
-
- if encoded_param:
- sys.stderr.write("Calling with params {}\n".format(encoded_param))
-
- try:
- if path.get('__urlsuffix__'):
- api_response = api_caller(suffix_param[path['__urlsuffix__']], **endpoint_params)
- else:
- api_response = api_caller(**endpoint_params)
- except Exception as e:
- if hasattr(e, 'reason'):
- sys.stderr.write(e.reason)
- if hasattr(e, 'body'):
- sys.stderr.write(e.body)
- sys.stderr.write('\n')
- sys.exit()
-
- api_response_unmapped = []
- if isinstance(api_response, list):
- for model in api_response:
- data_dict = self.unmap_model(model)
- api_response_unmapped.append(data_dict)
- else:
- data_dict = self.unmap_model(api_response)
- api_response_unmapped = data_dict
-
- print(json.dumps(api_response_unmapped, indent=2))
-
- def get_sub_model(self, field):
- sub_model_name_str = field.get('title') or field.get('description')
- sub_model_name = self.get_name_from_string(sub_model_name_str)
- if hasattr(swagger_client.models, sub_model_name):
- return getattr(swagger_client.models, sub_model_name)
-
- def exit_with_error(self, error_text):
- error_text += '\n'
- sys.stderr.write(self.colored_text(error_text, error_color))
- print()
- sys.exit()
-
- def process_command_post(self, path, suffix_param, endpoint_params, data_fn, data):
- api_caller = self.get_path_api_caller_for_path(path)
-
- endpoint = Menu(name='', info=path)
- schema = self.get_scheme_for_endpoint(endpoint)
- model_name = schema['__schema_name__']
- model = getattr(swagger_client.models, model_name)
-
- if not data:
-
- if data_fn.endswith('jwt'):
- with open(data_fn) as reader:
- data_org = jwt.decode(reader.read(),
- options={"verify_signature": False, "verify_exp": False, "verify_aud": False})
- else:
- try:
- data_org = self.get_json_from_file(data_fn)
- except ValueError as ve:
- self.exit_with_error(str(ve))
-
- data = {}
-
- for k in data_org:
- if k in model.attribute_map:
- mapped_key = model.attribute_map[k]
- data[mapped_key] = data_org[k]
- else:
- data[k] = data_org[k]
-
- try:
- body = myapi._ApiClient__deserialize_model(data, model)
- except Exception as e:
- self.exit_with_error(str(e))
-
- try:
- if suffix_param:
- api_response = api_caller(body=body, **suffix_param)
- else:
- api_response = api_caller(body=body)
- except Exception as e:
- self.print_exception(e)
- sys.exit()
-
- unmapped_response = self.unmap_model(api_response)
- sys.stderr.write("Server Response:\n")
- print(json.dumps(unmapped_response, indent=2))
-
- def process_command_put(self, path, suffix_param, endpoint_params, data_fn, data=None):
- self.process_command_post(path, suffix_param, endpoint_params, data_fn, data=None)
-
- def process_command_patch(self, path, suffix_param, endpoint_params, data_fn, data=None):
-
- if not data:
- try:
- data = self.get_json_from_file(data_fn)
- except ValueError as ve:
- self.exit_with_error(str(ve))
-
- if not isinstance(data, list):
- self.exit_with_error("{} must be array of /components/schemas/PatchRequest".format(data_fn))
-
- op_modes = ('add', 'remove', 'replace', 'move', 'copy', 'test')
-
- for item in data:
- if not item['op'] in op_modes:
- print("op must be one of {}".format(', '.join(op_modes)))
- sys.exit()
- if not item['path'].startswith('/'):
- item['path'] = '/' + item['path']
-
- api_caller = self.get_path_api_caller_for_path(path)
-
- try:
- if suffix_param:
- api_response = api_caller(suffix_param[path['__urlsuffix__']], body=data)
- else:
- api_response = api_caller(body=data)
- except Exception as e:
- self.print_exception(e)
- sys.exit()
-
- unmapped_response = self.unmap_model(api_response)
- sys.stderr.write("Server Response:\n")
- print(json.dumps(unmapped_response, indent=2))
-
- def process_command_delete(self, path, suffix_param, endpoint_params, data_fn, data=None):
-
- api_caller = self.get_path_api_caller_for_path(path)
- api_response = None
-
- try:
- api_response = api_caller(suffix_param[path['__urlsuffix__']], **endpoint_params)
- except Exception as e:
- self.print_exception(e)
- sys.exit()
-
- if api_response:
- unmapped_response = self.unmap_model(api_response)
- sys.stderr.write("Server Response:\n")
- print(json.dumps(unmapped_response, indent=2))
-
- def process_command_by_id(self, operation_id, url_suffix, endpoint_args, data_fn, data=None):
- path = self.get_path_by_id(operation_id)
-
- if not path:
- self.exit_with_error("No such Operation ID")
-
- suffix_param = self.parse_command_args(url_suffix)
- endpoint_params = self.parse_command_args(endpoint_args)
-
- if path.get('__urlsuffix__') and not path['__urlsuffix__'] in suffix_param:
- self.exit_with_error("This operation requires a value for url-suffix {}".format(path['__urlsuffix__']))
-
- endpoint = Menu('', info=path)
- schema = self.get_scheme_for_endpoint(endpoint)
-
- if not data:
- op_path = self.get_path_by_id(operation_id)
- if op_path['__method__'] == 'patch' and not data_fn:
- pop, pdata = '', ''
- if args.patch_add:
- pop = 'add'
- pdata = args.patch_add
- elif args.patch_remove:
- pop = 'remove'
- pdata = args.patch_remove
- elif args.patch_replace:
- pop = 'replace'
- pdata = args.patch_replace
-
- if pop:
- if pop != 'remove' and pdata.count(':') != 1:
- self.exit_with_error("Please provide --patch-data as colon delimited key:value pair")
-
- if pop != 'remove':
- ppath, pval = pdata.split(':')
- data = [{'op': pop, 'path': '/'+ ppath.lstrip('/'), 'value': pval}]
- else:
- data = [{'op': pop, 'path': '/'+ pdata.lstrip('/')}]
-
- if (schema and not data_fn) and not data:
- self.exit_with_error("Please provide schema with --data argument")
-
- caller_function = getattr(self, 'process_command_' + path['__method__'])
- caller_function(path, suffix_param, endpoint_params, data_fn, data=data)
-
- def make_schema_val(self, stype):
- if stype == 'object':
- return {}
- elif stype == 'list':
- return []
- elif stype == 'bool':
- return random.choice((True, False))
- else:
- return None
-
- def is_native_type(self, model, prop):
- stype = getattr(model, prop)
- if stype.startswith('list['):
- stype = re.match(r'list\[(.*)\]', stype).group(1)
- elif stype.startswith('dict('):
- stype = re.match(r'dict\(([^,]*), (.*)\)', stype).group(2)
-
- if stype in swagger_client.ApiClient.NATIVE_TYPES_MAPPING:
- return True
-
- def get_schema_from_model(self, model, schema):
-
- for key in model.attribute_map:
- stype = model.swagger_types[key]
- mapped_key = model.attribute_map[key]
- if stype in swagger_client.ApiClient.NATIVE_TYPES_MAPPING:
- schema[mapped_key] = self.make_schema_val(stype)
- else:
- if stype.startswith('list['):
- sub_cls = re.match(r'list\[(.*)\]', stype).group(1)
- sub_type = 'list'
- elif stype.startswith('dict('):
- sub_cls = re.match(r'dict\(([^,]*), (.*)\)', stype).group(2)
- sub_type = 'dict'
- else:
- sub_cls = stype
- sub_type = None
-
- if sub_cls in swagger_client.ApiClient.NATIVE_TYPES_MAPPING:
- schema[mapped_key] = self.make_schema_val(sub_type)
- else:
- sub_dict = {}
- sub_model = getattr(swagger_client.models, sub_cls)
- sub_schema = self.get_schema_from_model(sub_model, sub_dict)
- schema[mapped_key] = sub_dict
-
- def get_swagger_model_attr(self, model, attr):
- stype = model.swagger_types[attr]
- if stype in swagger_client.ApiClient.NATIVE_TYPES_MAPPING:
- return getattr(model, attr)
-
- def fill_defaults(self, schema, schema_={}):
-
- for k in schema:
- if isinstance(schema[k], dict):
- sub_schema_ = None
- if '$ref' in schema_['properties'][k]:
- sub_schema_ = self.cfg_yml['components']['schemas'][schema_['properties'][k]['$ref'].split('/')[-1]]
- elif schema_['properties'][k].get('items', {}).get('$ref'):
- sub_schema_ = self.cfg_yml['components']['schemas'][
- schema_['properties'][k]['items']['$ref'].split('/')[-1]]
- elif 'properties' in schema_['properties'][k]:
- sub_schema_ = schema_['properties'][k]
- if sub_schema_:
- self.fill_defaults(schema[k], sub_schema_)
-
- else:
- if 'enum' in schema_['properties'][k]:
- val = random.choice(schema_['properties'][k]['enum'])
- if schema_['properties'][k]['type'] == 'array':
- val = [val]
- schema[k] = val
- elif 'default' in schema_['properties'][k]:
- schema[k] = schema_['properties'][k]['default']
- elif 'example' in schema_['properties'][k]:
- schema[k] = schema_['properties'][k]['example']
- elif k in schema_.get('required', []):
- schema[k] = schema_['properties'][k]['type']
-
- def get_sample_schema(self, ref):
- schema_ = self.get_schema_from_reference('#' + args.schema)
- m = getattr(swagger_client.models, schema_['__schema_name__'])
- schema = {}
- self.get_schema_from_model(m, schema)
- self.fill_defaults(schema, schema_)
-
- print(json.dumps(schema, indent=2))
-
- def runApp(self):
- clear()
- self.display_menu(self.menu)
-
-
-def main():
-
- cli_object = JCA_CLI(host, client_id, client_secret, access_token, test_client)
- error_log_file = os.path.join(log_dir, 'error.log')
- try:
- if not access_token:
- cli_object.check_connection()
- if not (args.operation_id or args.info or args.schema):
- # reset previous color
- print('\033[0m', end='')
- cli_object.runApp()
- else:
- print()
- if args.info:
- cli_object.help_for(args.info)
- elif args.schema:
- cli_object.get_sample_schema(args.schema)
- elif args.operation_id:
- cli_object.process_command_by_id(args.operation_id, args.url_suffix, args.endpoint_args, args.data)
- print()
- except Exception as e:
- if os.environ.get('errstdout'):
- print(traceback.print_exc())
- print(u"\u001b[38;5;{}mAn Unhandled error raised: {}\u001b[0m".format(error_color, e))
- with open(error_log_file, 'a') as w:
- traceback.print_exc(file=w)
- print("Error is logged to {}".format(error_log_file))
-
-
-if __name__ == "__main__":
- main()
diff --git a/jans-cli/cli/pylib/__init__.py b/jans-cli/cli/pylib/__init__.py
deleted file mode 100644
index e69de29bb2d..00000000000
diff --git a/jans-cli/cli/pylib/tabulate/LICENSE b/jans-cli/cli/pylib/tabulate/LICENSE
deleted file mode 100644
index 81241eca637..00000000000
--- a/jans-cli/cli/pylib/tabulate/LICENSE
+++ /dev/null
@@ -1,20 +0,0 @@
-Copyright (c) 2011-2020 Sergey Astanin and contributors
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/jans-cli/cli/pylib/tabulate/README b/jans-cli/cli/pylib/tabulate/README
deleted file mode 100644
index 42061c01a1c..00000000000
--- a/jans-cli/cli/pylib/tabulate/README
+++ /dev/null
@@ -1 +0,0 @@
-README.md
\ No newline at end of file
diff --git a/jans-cli/cli/pylib/tabulate/README.md b/jans-cli/cli/pylib/tabulate/README.md
deleted file mode 100644
index ce06dadc834..00000000000
--- a/jans-cli/cli/pylib/tabulate/README.md
+++ /dev/null
@@ -1,747 +0,0 @@
-python-tabulate
-===============
-
-Pretty-print tabular data in Python, a library and a command-line
-utility.
-
-The main use cases of the library are:
-
-- printing small tables without hassle: just one function call,
- formatting is guided by the data itself
-- authoring tabular data for lightweight plain-text markup: multiple
- output formats suitable for further editing or transformation
-- readable presentation of mixed textual and numeric data: smart
- column alignment, configurable number formatting, alignment by a
- decimal point
-
-Installation
-------------
-
-To install the Python library and the command line utility, run:
-
- pip install tabulate
-
-The command line utility will be installed as `tabulate` to `bin` on
-Linux (e.g. `/usr/bin`); or as `tabulate.exe` to `Scripts` in your
-Python installation on Windows (e.g.
-`C:\Python27\Scripts\tabulate.exe`).
-
-You may consider installing the library only for the current user:
-
- pip install tabulate --user
-
-In this case the command line utility will be installed to
-`~/.local/bin/tabulate` on Linux and to
-`%APPDATA%\Python\Scripts\tabulate.exe` on Windows.
-
-To install just the library on Unix-like operating systems:
-
- TABULATE_INSTALL=lib-only pip install tabulate
-
-On Windows:
-
- set TABULATE_INSTALL=lib-only
- pip install tabulate
-
-Build status
-------------
-
-[![Build status](https://circleci.com/gh/astanin/python-tabulate.svg?style=svg)](https://circleci.com/gh/astanin/python-tabulate/tree/master) [![Build status](https://ci.appveyor.com/api/projects/status/8745yksvvol7h3d7/branch/master?svg=true)](https://ci.appveyor.com/project/astanin/python-tabulate/branch/master)
-
-Library usage
--------------
-
-The module provides just one function, `tabulate`, which takes a list of
-lists or another tabular data type as the first argument, and outputs a
-nicely formatted plain-text table:
-
- >>> from tabulate import tabulate
-
- >>> table = [["Sun",696000,1989100000],["Earth",6371,5973.6],
- ... ["Moon",1737,73.5],["Mars",3390,641.85]]
- >>> print(tabulate(table))
- ----- ------ -------------
- Sun 696000 1.9891e+09
- Earth 6371 5973.6
- Moon 1737 73.5
- Mars 3390 641.85
- ----- ------ -------------
-
-The following tabular data types are supported:
-
-- list of lists or another iterable of iterables
-- list or another iterable of dicts (keys as columns)
-- dict of iterables (keys as columns)
-- two-dimensional NumPy array
-- NumPy record arrays (names as columns)
-- pandas.DataFrame
-
-Examples in this file use Python2. Tabulate supports Python3 too.
-
-### Headers
-
-The second optional argument named `headers` defines a list of column
-headers to be used:
-
- >>> print(tabulate(table, headers=["Planet","R (km)", "mass (x 10^29 kg)"]))
- Planet R (km) mass (x 10^29 kg)
- -------- -------- -------------------
- Sun 696000 1.9891e+09
- Earth 6371 5973.6
- Moon 1737 73.5
- Mars 3390 641.85
-
-If `headers="firstrow"`, then the first row of data is used:
-
- >>> print(tabulate([["Name","Age"],["Alice",24],["Bob",19]],
- ... headers="firstrow"))
- Name Age
- ------ -----
- Alice 24
- Bob 19
-
-If `headers="keys"`, then the keys of a dictionary/dataframe, or column
-indices are used. It also works for NumPy record arrays and lists of
-dictionaries or named tuples:
-
- >>> print(tabulate({"Name": ["Alice", "Bob"],
- ... "Age": [24, 19]}, headers="keys"))
- Age Name
- ----- ------
- 24 Alice
- 19 Bob
-
-### Row Indices
-
-By default, only pandas.DataFrame tables have an additional column
-called row index. To add a similar column to any other type of table,
-pass `showindex="always"` or `showindex=True` argument to `tabulate()`.
-To suppress row indices for all types of data, pass `showindex="never"`
-or `showindex=False`. To add a custom row index column, pass
-`showindex=rowIDs`, where `rowIDs` is some iterable:
-
- >>> print(tabulate([["F",24],["M",19]], showindex="always"))
- - - --
- 0 F 24
- 1 M 19
- - - --
-
-### Table format
-
-There is more than one way to format a table in plain text. The third
-optional argument named `tablefmt` defines how the table is formatted.
-
-Supported table formats are:
-
-- "plain"
-- "simple"
-- "github"
-- "grid"
-- "fancy\_grid"
-- "pipe"
-- "orgtbl"
-- "jira"
-- "presto"
-- "pretty"
-- "psql"
-- "rst"
-- "mediawiki"
-- "moinmoin"
-- "youtrack"
-- "html"
-- "unsafehtml"
-- "latex"
-- "latex\_raw"
-- "latex\_booktabs"
-- "textile"
-
-`plain` tables do not use any pseudo-graphics to draw lines:
-
- >>> table = [["spam",42],["eggs",451],["bacon",0]]
- >>> headers = ["item", "qty"]
- >>> print(tabulate(table, headers, tablefmt="plain"))
- item qty
- spam 42
- eggs 451
- bacon 0
-
-`simple` is the default format (the default may change in future
-versions). It corresponds to `simple_tables` in [Pandoc Markdown
-extensions](http://johnmacfarlane.net/pandoc/README.html#tables):
-
- >>> print(tabulate(table, headers, tablefmt="simple"))
- item qty
- ------ -----
- spam 42
- eggs 451
- bacon 0
-
-`github` follows the conventions of Github flavored Markdown. It
-corresponds to the `pipe` format without alignment colons:
-
- >>> print(tabulate(table, headers, tablefmt="github"))
- | item | qty |
- |--------|-------|
- | spam | 42 |
- | eggs | 451 |
- | bacon | 0 |
-
-`grid` is like tables formatted by Emacs'
-[table.el](http://table.sourceforge.net/) package. It corresponds to
-`grid_tables` in Pandoc Markdown extensions:
-
- >>> print(tabulate(table, headers, tablefmt="grid"))
- +--------+-------+
- | item | qty |
- +========+=======+
- | spam | 42 |
- +--------+-------+
- | eggs | 451 |
- +--------+-------+
- | bacon | 0 |
- +--------+-------+
-
-`fancy_grid` draws a grid using box-drawing characters:
-
- >>> print(tabulate(table, headers, tablefmt="fancy_grid"))
- ╒════════╤═══════╕
- │ item │ qty │
- ╞════════╪═══════╡
- │ spam │ 42 │
- ├────────┼───────┤
- │ eggs │ 451 │
- ├────────┼───────┤
- │ bacon │ 0 │
- ╘════════╧═══════╛
-
-`presto` is like tables formatted by Presto cli:
-
- >>> print(tabulate(table, headers, tablefmt="presto"))
- item | qty
- --------+-------
- spam | 42
- eggs | 451
- bacon | 0
-
-`pretty` attempts to be close to the format emitted by the PrettyTables
-library:
-
- >>> print(tabulate(table, headers, tablefmt="pretty"))
- +-------+-----+
- | item | qty |
- +-------+-----+
- | spam | 42 |
- | eggs | 451 |
- | bacon | 0 |
- +-------+-----+
-
-`psql` is like tables formatted by Postgres' psql cli:
-
- >>> print(tabulate(table, headers, tablefmt="psql"))
- +--------+-------+
- | item | qty |
- |--------+-------|
- | spam | 42 |
- | eggs | 451 |
- | bacon | 0 |
- +--------+-------+
-
-`pipe` follows the conventions of [PHP Markdown
-Extra](http://michelf.ca/projects/php-markdown/extra/#table) extension.
-It corresponds to `pipe_tables` in Pandoc. This format uses colons to
-indicate column alignment:
-
- >>> print(tabulate(table, headers, tablefmt="pipe"))
- | item | qty |
- |:-------|------:|
- | spam | 42 |
- | eggs | 451 |
- | bacon | 0 |
-
-`orgtbl` follows the conventions of Emacs
-[org-mode](http://orgmode.org/manual/Tables.html), and is editable also
-in the minor orgtbl-mode. Hence its name:
-
- >>> print(tabulate(table, headers, tablefmt="orgtbl"))
- | item | qty |
- |--------+-------|
- | spam | 42 |
- | eggs | 451 |
- | bacon | 0 |
-
-`jira` follows the conventions of Atlassian Jira markup language:
-
- >>> print(tabulate(table, headers, tablefmt="jira"))
- || item || qty ||
- | spam | 42 |
- | eggs | 451 |
- | bacon | 0 |
-
-`rst` formats data like a simple table of the
-[reStructuredText](http://docutils.sourceforge.net/docs/user/rst/quickref.html#tables)
-format:
-
- >>> print(tabulate(table, headers, tablefmt="rst"))
- ====== =====
- item qty
- ====== =====
- spam 42
- eggs 451
- bacon 0
- ====== =====
-
-`mediawiki` format produces a table markup used in
-[Wikipedia](http://www.mediawiki.org/wiki/Help:Tables) and on other
-MediaWiki-based sites:
-
- >>> print(tabulate(table, headers, tablefmt="mediawiki"))
- {| class="wikitable" style="text-align: left;"
- |+
- |-
- ! item !! align="right"| qty
- |-
- | spam || align="right"| 42
- |-
- | eggs || align="right"| 451
- |-
- | bacon || align="right"| 0
- |}
-
-`moinmoin` format produces a table markup used in
-[MoinMoin](https://moinmo.in/) wikis:
-
- >>> print(tabulate(table, headers, tablefmt="moinmoin"))
- || ''' item ''' || ''' quantity ''' ||
- || spam || 41.999 ||
- || eggs || 451 ||
- || bacon || ||
-
-`youtrack` format produces a table markup used in Youtrack tickets:
-
- >>> print(tabulate(table, headers, tablefmt="youtrack"))
- || item || quantity ||
- | spam | 41.999 |
- | eggs | 451 |
- | bacon | |
-
-`textile` format produces a table markup used in
-[Textile](http://redcloth.org/hobix.com/textile/) format:
-
- >>> print(tabulate(table, headers, tablefmt="textile"))
- |_. item |_. qty |
- |<. spam |>. 42 |
- |<. eggs |>. 451 |
- |<. bacon |>. 0 |
-
-`html` produces standard HTML markup as an html.escape'd str
-with a ._repr_html_ method so that Jupyter Lab and Notebook display the HTML
-and a .str property so that the raw HTML remains accessible.
-`unsafehtml` table format can be used if an unescaped HTML is required:
-
- >>> print(tabulate(table, headers, tablefmt="html"))
-
-
- item | qty |
- spam | 42 |
- eggs | 451 |
- bacon | 0 |
-
-
-
-`latex` format creates a `tabular` environment for LaTeX markup,
-replacing special characters like `_` or `\` to their LaTeX
-correspondents:
-
- >>> print(tabulate(table, headers, tablefmt="latex"))
- \begin{tabular}{lr}
- \hline
- item & qty \\
- \hline
- spam & 42 \\
- eggs & 451 \\
- bacon & 0 \\
- \hline
- \end{tabular}
-
-`latex_raw` behaves like `latex` but does not escape LaTeX commands and
-special characters.
-
-`latex_booktabs` creates a `tabular` environment for LaTeX markup using
-spacing and style from the `booktabs` package.
-
-### Column alignment
-
-`tabulate` is smart about column alignment. It detects columns which
-contain only numbers, and aligns them by a decimal point (or flushes
-them to the right if they appear to be integers). Text columns are
-flushed to the left.
-
-You can override the default alignment with `numalign` and `stralign`
-named arguments. Possible column alignments are: `right`, `center`,
-`left`, `decimal` (only for numbers), and `None` (to disable alignment).
-
-Aligning by a decimal point works best when you need to compare numbers
-at a glance:
-
- >>> print(tabulate([[1.2345],[123.45],[12.345],[12345],[1234.5]]))
- ----------
- 1.2345
- 123.45
- 12.345
- 12345
- 1234.5
- ----------
-
-Compare this with a more common right alignment:
-
- >>> print(tabulate([[1.2345],[123.45],[12.345],[12345],[1234.5]], numalign="right"))
- ------
- 1.2345
- 123.45
- 12.345
- 12345
- 1234.5
- ------
-
-For `tabulate`, anything which can be parsed as a number is a number.
-Even numbers represented as strings are aligned properly. This feature
-comes in handy when reading a mixed table of text and numbers from a
-file:
-
- >>> import csv ; from StringIO import StringIO
- >>> table = list(csv.reader(StringIO("spam, 42\neggs, 451\n")))
- >>> table
- [['spam', ' 42'], ['eggs', ' 451']]
- >>> print(tabulate(table))
- ---- ----
- spam 42
- eggs 451
- ---- ----
-
-
-To disable this feature use `disable_numparse=True`.
-
- >>> print(tabulate.tabulate([["Ver1", "18.0"], ["Ver2","19.2"]], tablefmt="simple", disable_numparse=True))
- ---- ----
- Ver1 18.0
- Ver2 19.2
- ---- ----
-
-
-### Custom column alignment
-
-`tabulate` allows a custom column alignment to override the above. The
-`colalign` argument can be a list or a tuple of `stralign` named
-arguments. Possible column alignments are: `right`, `center`, `left`,
-`decimal` (only for numbers), and `None` (to disable alignment).
-Omitting an alignment uses the default. For example:
-
- >>> print(tabulate([["one", "two"], ["three", "four"]], colalign=("right",))
- ----- ----
- one two
- three four
- ----- ----
-
-### Number formatting
-
-`tabulate` allows to define custom number formatting applied to all
-columns of decimal numbers. Use `floatfmt` named argument:
-
- >>> print(tabulate([["pi",3.141593],["e",2.718282]], floatfmt=".4f"))
- -- ------
- pi 3.1416
- e 2.7183
- -- ------
-
-`floatfmt` argument can be a list or a tuple of format strings, one per
-column, in which case every column may have different number formatting:
-
- >>> print(tabulate([[0.12345, 0.12345, 0.12345]], floatfmt=(".1f", ".3f")))
- --- ----- -------
- 0.1 0.123 0.12345
- --- ----- -------
-
-### Text formatting
-
-By default, `tabulate` removes leading and trailing whitespace from text
-columns. To disable whitespace removal, set the global module-level flag
-`PRESERVE_WHITESPACE`:
-
- import tabulate
- tabulate.PRESERVE_WHITESPACE = True
-
-### Wide (fullwidth CJK) symbols
-
-To properly align tables which contain wide characters (typically
-fullwidth glyphs from Chinese, Japanese or Korean languages), the user
-should install `wcwidth` library. To install it together with
-`tabulate`:
-
- pip install tabulate[widechars]
-
-Wide character support is enabled automatically if `wcwidth` library is
-already installed. To disable wide characters support without
-uninstalling `wcwidth`, set the global module-level flag
-`WIDE_CHARS_MODE`:
-
- import tabulate
- tabulate.WIDE_CHARS_MODE = False
-
-### Multiline cells
-
-Most table formats support multiline cell text (text containing newline
-characters). The newline characters are honored as line break
-characters.
-
-Multiline cells are supported for data rows and for header rows.
-
-Further automatic line breaks are not inserted. Of course, some output
-formats such as latex or html handle automatic formatting of the cell
-content on their own, but for those that don't, the newline characters
-in the input cell text are the only means to break a line in cell text.
-
-Note that some output formats (e.g. simple, or plain) do not represent
-row delimiters, so that the representation of multiline cells in such
-formats may be ambiguous to the reader.
-
-The following examples of formatted output use the following table with
-a multiline cell, and headers with a multiline cell:
-
- >>> table = [["eggs",451],["more\nspam",42]]
- >>> headers = ["item\nname", "qty"]
-
-`plain` tables:
-
- >>> print(tabulate(table, headers, tablefmt="plain"))
- item qty
- name
- eggs 451
- more 42
- spam
-
-`simple` tables:
-
- >>> print(tabulate(table, headers, tablefmt="simple"))
- item qty
- name
- ------ -----
- eggs 451
- more 42
- spam
-
-`grid` tables:
-
- >>> print(tabulate(table, headers, tablefmt="grid"))
- +--------+-------+
- | item | qty |
- | name | |
- +========+=======+
- | eggs | 451 |
- +--------+-------+
- | more | 42 |
- | spam | |
- +--------+-------+
-
-`fancy_grid` tables:
-
- >>> print(tabulate(table, headers, tablefmt="fancy_grid"))
- ╒════════╤═══════╕
- │ item │ qty │
- │ name │ │
- ╞════════╪═══════╡
- │ eggs │ 451 │
- ├────────┼───────┤
- │ more │ 42 │
- │ spam │ │
- ╘════════╧═══════╛
-
-`pipe` tables:
-
- >>> print(tabulate(table, headers, tablefmt="pipe"))
- | item | qty |
- | name | |
- |:-------|------:|
- | eggs | 451 |
- | more | 42 |
- | spam | |
-
-`orgtbl` tables:
-
- >>> print(tabulate(table, headers, tablefmt="orgtbl"))
- | item | qty |
- | name | |
- |--------+-------|
- | eggs | 451 |
- | more | 42 |
- | spam | |
-
-`jira` tables:
-
- >>> print(tabulate(table, headers, tablefmt="jira"))
- | item | qty |
- | name | |
- |:-------|------:|
- | eggs | 451 |
- | more | 42 |
- | spam | |
-
-`presto` tables:
-
- >>> print(tabulate(table, headers, tablefmt="presto"))
- item | qty
- name |
- --------+-------
- eggs | 451
- more | 42
- spam |
-
-`pretty` tables:
-
- >>> print(tabulate(table, headers, tablefmt="pretty"))
- +------+-----+
- | item | qty |
- | name | |
- +------+-----+
- | eggs | 451 |
- | more | 42 |
- | spam | |
- +------+-----+
-
-`psql` tables:
-
- >>> print(tabulate(table, headers, tablefmt="psql"))
- +--------+-------+
- | item | qty |
- | name | |
- |--------+-------|
- | eggs | 451 |
- | more | 42 |
- | spam | |
- +--------+-------+
-
-`rst` tables:
-
- >>> print(tabulate(table, headers, tablefmt="rst"))
- ====== =====
- item qty
- name
- ====== =====
- eggs 451
- more 42
- spam
- ====== =====
-
-Multiline cells are not well supported for the other table formats.
-
-Usage of the command line utility
----------------------------------
-
- Usage: tabulate [options] [FILE ...]
-
- FILE a filename of the file with tabular data;
- if "-" or missing, read data from stdin.
-
- Options:
-
- -h, --help show this message
- -1, --header use the first row of data as a table header
- -o FILE, --output FILE print table to FILE (default: stdout)
- -s REGEXP, --sep REGEXP use a custom column separator (default: whitespace)
- -F FPFMT, --float FPFMT floating point number format (default: g)
- -f FMT, --format FMT set output table format; supported formats:
- plain, simple, github, grid, fancy_grid, pipe,
- orgtbl, rst, mediawiki, html, latex, latex_raw,
- latex_booktabs, tsv
- (default: simple)
-
-Performance considerations
---------------------------
-
-Such features as decimal point alignment and trying to parse everything
-as a number imply that `tabulate`:
-
-- has to "guess" how to print a particular tabular data type
-- needs to keep the entire table in-memory
-- has to "transpose" the table twice
-- does much more work than it may appear
-
-It may not be suitable for serializing really big tables (but who's
-going to do that, anyway?) or printing tables in performance sensitive
-applications. `tabulate` is about two orders of magnitude slower than
-simply joining lists of values with a tab, coma or other separator.
-
-In the same time `tabulate` is comparable to other table
-pretty-printers. Given a 10x10 table (a list of lists) of mixed text and
-numeric data, `tabulate` appears to be slower than `asciitable`, and
-faster than `PrettyTable` and `texttable` The following mini-benchmark
-was run in Python 3.8.1 in Windows 10 x64:
-
- =========================== ========== ===========
- Table formatter time, μs rel. time
- =========================== ========== ===========
- csv to StringIO 12.4 1.0
- join with tabs and newlines 15.7 1.3
- asciitable (0.8.0) 208.3 16.7
- tabulate (0.8.7) 492.1 39.5
- PrettyTable (0.7.2) 945.5 76.0
- texttable (1.6.2) 1239.5 99.6
- =========================== ========== ===========
-
-
-Version history
----------------
-
-The full version history can be found at the [changelog](https://github.com/astanin/python-tabulate/blob/master/CHANGELOG).
-
-How to contribute
------------------
-
-Contributions should include tests and an explanation for the changes
-they propose. Documentation (examples, docstrings, README.md) should be
-updated accordingly.
-
-This project uses [nose](https://nose.readthedocs.org/) testing
-framework and [tox](https://tox.readthedocs.io/) to automate testing in
-different environments. Add tests to one of the files in the `test/`
-folder.
-
-To run tests on all supported Python versions, make sure all Python
-interpreters, `nose` and `tox` are installed, then run `tox` in the root
-of the project source tree.
-
-On Linux `tox` expects to find executables like `python2.6`,
-`python2.7`, `python3.4` etc. On Windows it looks for
-`C:\Python26\python.exe`, `C:\Python27\python.exe` and
-`C:\Python34\python.exe` respectively.
-
-To test only some Python environements, use `-e` option. For example, to
-test only against Python 2.7 and Python 3.6, run:
-
- tox -e py27,py36
-
-in the root of the project source tree.
-
-To enable NumPy and Pandas tests, run:
-
- tox -e py27-extra,py36-extra
-
-(this may take a long time the first time, because NumPy and Pandas will
-have to be installed in the new virtual environments)
-
-See `tox.ini` file to learn how to use `nosetests` directly to test
-individual Python versions.
-
-Contributors
-------------
-
-Sergey Astanin, Pau Tallada Crespí, Erwin Marsi, Mik Kocikowski, Bill
-Ryder, Zach Dwiel, Frederik Rietdijk, Philipp Bogensberger, Greg
-(anonymous), Stefan Tatschner, Emiel van Miltenburg, Brandon Bennett,
-Amjith Ramanujam, Jan Schulz, Simon Percivall, Javier Santacruz
-López-Cepero, Sam Denton, Alexey Ziyangirov, acaird, Cesar Sanchez,
-naught101, John Vandenberg, Zack Dever, Christian Clauss, Benjamin
-Maier, Andy MacKinlay, Thomas Roten, Jue Wang, Joe King, Samuel Phan,
-Nick Satterly, Daniel Robbins, Dmitry B, Lars Butler, Andreas Maier,
-Dick Marinus, Sébastien Celles, Yago González, Andrew Gaul, Wim Glenn,
-Jean Michel Rouly, Tim Gates, John Vandenberg, Sorin Sbarnea,
-Wes Turner, Andrew Tija, Marco Gorelli, Sean McGinnis, danja100.
diff --git a/jans-cli/cli/pylib/tabulate/__init__.py b/jans-cli/cli/pylib/tabulate/__init__.py
deleted file mode 100644
index e69de29bb2d..00000000000
diff --git a/jans-cli/cli/pylib/tabulate/tabulate.py b/jans-cli/cli/pylib/tabulate/tabulate.py
deleted file mode 100644
index 2bbb913c62d..00000000000
--- a/jans-cli/cli/pylib/tabulate/tabulate.py
+++ /dev/null
@@ -1,1792 +0,0 @@
-# -*- coding: utf-8 -*-
-
-"""Pretty-print tabular data."""
-
-from __future__ import print_function
-from __future__ import unicode_literals
-from collections import namedtuple
-from platform import python_version_tuple
-import re
-import math
-import sys
-
-
-if sys.version_info.major >= 3 and sys.version_info.minor >= 3:
- from collections.abc import Iterable
-else:
- from collections import Iterable
-
-
-if python_version_tuple()[0] < "3":
- from itertools import izip_longest
- from functools import partial
-
- _none_type = type(None)
- _bool_type = bool
- _int_type = int
- _long_type = long # noqa
- _float_type = float
- _text_type = unicode # noqa
- _binary_type = str
-
- def _is_file(f):
- return hasattr(f, "read")
-
-
-else:
- from itertools import zip_longest as izip_longest
- from functools import reduce, partial
-
- _none_type = type(None)
- _bool_type = bool
- _int_type = int
- _long_type = int
- _float_type = float
- _text_type = str
- _binary_type = bytes
- basestring = str
-
- import io
-
- def _is_file(f):
- return isinstance(f, io.IOBase)
-
-
-try:
- import wcwidth # optional wide-character (CJK) support
-except ImportError:
- wcwidth = None
-
-try:
- from html import escape as htmlescape
-except ImportError:
- from cgi import escape as htmlescape
-
-
-__all__ = ["tabulate", "tabulate_formats", "simple_separated_format"]
-__version__ = "0.8.8"
-
-
-# minimum extra space in headers
-MIN_PADDING = 2
-
-# Whether or not to preserve leading/trailing whitespace in data.
-PRESERVE_WHITESPACE = False
-
-_DEFAULT_FLOATFMT = "g"
-_DEFAULT_MISSINGVAL = ""
-
-
-# if True, enable wide-character (CJK) support
-WIDE_CHARS_MODE = wcwidth is not None
-
-
-Line = namedtuple("Line", ["begin", "hline", "sep", "end"])
-
-
-DataRow = namedtuple("DataRow", ["begin", "sep", "end"])
-
-
-# A table structure is suppposed to be:
-#
-# --- lineabove ---------
-# headerrow
-# --- linebelowheader ---
-# datarow
-# --- linebewteenrows ---
-# ... (more datarows) ...
-# --- linebewteenrows ---
-# last datarow
-# --- linebelow ---------
-#
-# TableFormat's line* elements can be
-#
-# - either None, if the element is not used,
-# - or a Line tuple,
-# - or a function: [col_widths], [col_alignments] -> string.
-#
-# TableFormat's *row elements can be
-#
-# - either None, if the element is not used,
-# - or a DataRow tuple,
-# - or a function: [cell_values], [col_widths], [col_alignments] -> string.
-#
-# padding (an integer) is the amount of white space around data values.
-#
-# with_header_hide:
-#
-# - either None, to display all table elements unconditionally,
-# - or a list of elements not to be displayed if the table has column headers.
-#
-TableFormat = namedtuple(
- "TableFormat",
- [
- "lineabove",
- "linebelowheader",
- "linebetweenrows",
- "linebelow",
- "headerrow",
- "datarow",
- "padding",
- "with_header_hide",
- ],
-)
-
-
-def _pipe_segment_with_colons(align, colwidth):
- """Return a segment of a horizontal line with optional colons which
- indicate column's alignment (as in `pipe` output format)."""
- w = colwidth
- if align in ["right", "decimal"]:
- return ("-" * (w - 1)) + ":"
- elif align == "center":
- return ":" + ("-" * (w - 2)) + ":"
- elif align == "left":
- return ":" + ("-" * (w - 1))
- else:
- return "-" * w
-
-
-def _pipe_line_with_colons(colwidths, colaligns):
- """Return a horizontal line with optional colons to indicate column's
- alignment (as in `pipe` output format)."""
- if not colaligns: # e.g. printing an empty data frame (github issue #15)
- colaligns = [""] * len(colwidths)
- segments = [_pipe_segment_with_colons(a, w) for a, w in zip(colaligns, colwidths)]
- return "|" + "|".join(segments) + "|"
-
-
-def _mediawiki_row_with_attrs(separator, cell_values, colwidths, colaligns):
- alignment = {
- "left": "",
- "right": 'align="right"| ',
- "center": 'align="center"| ',
- "decimal": 'align="right"| ',
- }
- # hard-coded padding _around_ align attribute and value together
- # rather than padding parameter which affects only the value
- values_with_attrs = [
- " " + alignment.get(a, "") + c + " " for c, a in zip(cell_values, colaligns)
- ]
- colsep = separator * 2
- return (separator + colsep.join(values_with_attrs)).rstrip()
-
-
-def _textile_row_with_attrs(cell_values, colwidths, colaligns):
- cell_values[0] += " "
- alignment = {"left": "<.", "right": ">.", "center": "=.", "decimal": ">."}
- values = (alignment.get(a, "") + v for a, v in zip(colaligns, cell_values))
- return "|" + "|".join(values) + "|"
-
-
-def _html_begin_table_without_header(colwidths_ignore, colaligns_ignore):
- # this table header will be suppressed if there is a header row
- return "\n"
-
-
-def _html_row_with_attrs(celltag, unsafe, cell_values, colwidths, colaligns):
- alignment = {
- "left": "",
- "right": ' style="text-align: right;"',
- "center": ' style="text-align: center;"',
- "decimal": ' style="text-align: right;"',
- }
- if unsafe:
- values_with_attrs = [
- "<{0}{1}>{2}{0}>".format(celltag, alignment.get(a, ""), c)
- for c, a in zip(cell_values, colaligns)
- ]
- else:
- values_with_attrs = [
- "<{0}{1}>{2}{0}>".format(celltag, alignment.get(a, ""), htmlescape(c))
- for c, a in zip(cell_values, colaligns)
- ]
- rowhtml = "{}
".format("".join(values_with_attrs).rstrip())
- if celltag == "th": # it's a header row, create a new table header
- rowhtml = "\n\n{}\n\n".format(rowhtml)
- return rowhtml
-
-
-def _moin_row_with_attrs(celltag, cell_values, colwidths, colaligns, header=""):
- alignment = {
- "left": "",
- "right": '