diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index b4a08f08e..47d48060e 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -54,36 +54,3 @@ jobs: with: name: artifacts path: packages/dashboard-e2e/artifacts - reporting-e2e: - needs: build-docker-images - name: Reporting e2e - runs-on: ubuntu-latest - container: - image: ghcr.io/${{ github.repository }}/e2e - credentials: - username: ${{ github.repository_owner }} - password: ${{ secrets.GITHUB_TOKEN }} - options: --privileged --ipc=host - defaults: - run: - shell: bash - working-directory: packages/reporting-e2e - steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v2 - with: - node-version: '14' - cache: 'npm' - - name: bootstrap - run: | - npm install -g npm@latest - scripts/bootstrap.sh reporting-e2e - working-directory: . - - name: test - run: npm test - - name: upload artifacts - uses: actions/upload-artifact@v2 - if: always() - with: - name: artifacts - path: packages/reporting-e2e/artifacts diff --git a/.github/workflows/reporting-e2e.yml b/.github/workflows/reporting-e2e.yml deleted file mode 100644 index 6be4b57fe..000000000 --- a/.github/workflows/reporting-e2e.yml +++ /dev/null @@ -1,41 +0,0 @@ -name: Reporting End-to-End -on: - pull_request: - push: - branches: - - main -env: - CI: true -jobs: - reporting-e2e: - name: Reporting e2e - runs-on: ubuntu-latest - container: - image: ghcr.io/${{ github.repository }}/e2e - credentials: - username: ${{ github.repository_owner }} - password: ${{ secrets.GITHUB_TOKEN }} - options: --privileged --ipc=host - defaults: - run: - shell: bash - working-directory: packages/reporting-e2e - steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v2 - with: - node-version: '14' - cache: 'npm' - - name: bootstrap - run: | - npm install -g npm@latest - scripts/bootstrap.sh reporting-e2e - working-directory: . - - name: test - run: npm test - - name: upload artifacts - uses: actions/upload-artifact@v2 - if: always() - with: - name: artifacts - path: packages/reporting-e2e/artifacts diff --git a/.github/workflows/reporting-server.yml b/.github/workflows/reporting-server.yml deleted file mode 100644 index 3ed14949d..000000000 --- a/.github/workflows/reporting-server.yml +++ /dev/null @@ -1,46 +0,0 @@ -name: reporting-server -on: - pull_request: - paths: - - '.github/workflows/reporting-server.yml' - - 'packages/reporting-server/**' - push: - branches: - - main -env: - CI: true -jobs: - tests: - name: Tests - runs-on: ubuntu-latest - container: - image: ghcr.io/${{ github.repository }}/e2e - credentials: - username: ${{ github.repository_owner }} - password: ${{ secrets.GITHUB_TOKEN }} - defaults: - run: - shell: bash - working-directory: packages/reporting-server - steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v2 - with: - node-version: '14' - cache: 'npm' - - name: bootstrap - run: | - . /rmf_demos_ws/install/setup.bash - npm install -g npm@latest - scripts/bootstrap.sh reporting-server - working-directory: . - - name: tests - run: | - . /rmf_demos_ws/install/setup.bash - npm run lint - npm run test:cov - python3 -m pipenv run python -m coverage xml - - name: Upload coverage to Codecov - uses: codecov/codecov-action@v1 - with: - flags: reporting-server diff --git a/.github/workflows/reporting.yml b/.github/workflows/reporting.yml deleted file mode 100644 index 3aa3ec6e6..000000000 --- a/.github/workflows/reporting.yml +++ /dev/null @@ -1,47 +0,0 @@ -name: reporting -on: - pull_request: - paths: - - '.github/workflows/reporting.yml' - - 'packages/reporting/**' - - 'packages/react-components/**' - - 'packages/rmf-auth/**' - - 'packages/rmf-models/**' - push: - branches: - - main -env: - CI: true -jobs: - unit-tests: - name: Unit Tests - runs-on: ubuntu-latest - container: - image: ghcr.io/${{ github.repository }}/e2e - credentials: - username: ${{ github.repository_owner }} - password: ${{ secrets.GITHUB_TOKEN }} - defaults: - run: - shell: bash - working-directory: packages/reporting - steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v2 - with: - node-version: '14' - cache: 'npm' - - name: bootstrap - run: | - . /rmf_demos_ws/install/setup.bash - npm install -g npm@latest - scripts/bootstrap.sh reporting - working-directory: . - - name: build - run: npm run build:storybook && npm run build - - name: unit test - run: npm run test:coverage - - name: Upload coverage to Codecov - uses: codecov/codecov-action@v1 - with: - flags: reporting diff --git a/Pipfile b/Pipfile index 774c35762..ecf512073 100644 --- a/Pipfile +++ b/Pipfile @@ -15,8 +15,6 @@ api-server = {editable = true, path = "./packages/api-server"} requests = "~=2.25" asyncpg = "~=0.24.0" websocket-client = "~=1.2.3" -# reporting-server -reporting-server = {editable = true, path = "./packages/reporting-server"} # ros-translator ros-translator = {editable = true, path = "./packages/ros-translator"} diff --git a/Pipfile.lock b/Pipfile.lock index 3d8ba43c2..b69b9954f 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "f9cbd766b56afdd8ff5cda7fe2e4e9c3f896dc3a878e7dd807f8debe919905d1" + "sha256": "46ea379ca21c6c642e6387b01933657b8d12a9abebf07c977989b2b3c23c9696" }, "pipfile-spec": 6, "requires": { @@ -29,7 +29,7 @@ "sha256:6c49dc6d3405929b1d08eeccc72306d3677503cc5e5e43771efc1e00232e8231", "sha256:f0e6acc24bc4864149267ac82fb46dfb3be4455f99fe21df82609cc6e6baee51" ], - "markers": "python_version >= '3.6'", + "markers": "python_full_version >= '3.6.0'", "version": "==0.17.0" }, "api-server": { @@ -68,7 +68,7 @@ "sha256:3ac67daa353ecf853a1df9d3e924f005e729227a60a8dbada31a4c31aba7f654", "sha256:42c84ffbe6f8de898af6073b4be9ea7ccedcd78d3474aa844c54e49d5a079f6f" ], - "markers": "python_version >= '3.6'", + "markers": "python_full_version >= '3.6.0'", "version": "==0.21.4" }, "black": { @@ -143,11 +143,11 @@ }, "charset-normalizer": { "hashes": [ - "sha256:1eecaa09422db5be9e29d7fc65664e6c33bd06f9ced7838578ba40d58bdf3721", - "sha256:b0b883e8e874edfdece9c28f314e3dd5badf067342e42fb162203335ae61aa2c" + "sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597", + "sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df" ], "markers": "python_version >= '3'", - "version": "==2.0.9" + "version": "==2.0.12" }, "click": { "hashes": [ @@ -245,16 +245,16 @@ "sha256:6ea2286e439c4ced7cce2b2862c25859601bf327a515c12dd6e431ef5d49d12f", "sha256:d3e3c0ac35110efb22ee3ed28201cf32f9d11a9a0e52d7dd676cad25f5219523" ], - "markers": "python_version >= '3.6'", + "markers": "python_full_version >= '3.6.0'", "version": "==0.65.3" }, "h11": { "hashes": [ - "sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6", - "sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042" + "sha256:70813c1135087a248a4d38cc0e1a0181ffab2188141a93eaf567940c3957ff06", + "sha256:8ddd78563b633ca55346c8cd41ec0af27d3c79931828beffb46ce70a379e7442" ], - "markers": "python_version >= '3.6'", - "version": "==0.12.0" + "markers": "python_full_version >= '3.6.0'", + "version": "==0.13.0" }, "httptools": { "hashes": [ @@ -347,83 +347,54 @@ "sha256:fccdf7c2c5821a8cbd0a9440a456f5050492f2270bd54e94360cac663398739b", "sha256:fd45683c3caddf83abbb1249b653a266e7069a09f486daa8863fb0e7496a9fdb" ], - "markers": "python_version >= '3.6'", + "markers": "python_full_version >= '3.6.0'", "version": "==1.7.1" }, "markupsafe": { "hashes": [ - "sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298", - "sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64", - "sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b", - "sha256:04635854b943835a6ea959e948d19dcd311762c5c0c6e1f0e16ee57022669194", - "sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567", - "sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff", - "sha256:0d4b31cc67ab36e3392bbf3862cfbadac3db12bdd8b02a2731f509ed5b829724", - "sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74", - "sha256:168cd0a3642de83558a5153c8bd34f175a9a6e7f6dc6384b9655d2697312a646", - "sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35", - "sha256:1f2ade76b9903f39aa442b4aadd2177decb66525062db244b35d71d0ee8599b6", - "sha256:20dca64a3ef2d6e4d5d615a3fd418ad3bde77a47ec8a23d984a12b5b4c74491a", - "sha256:2a7d351cbd8cfeb19ca00de495e224dea7e7d919659c2841bbb7f420ad03e2d6", - "sha256:2d7d807855b419fc2ed3e631034685db6079889a1f01d5d9dac950f764da3dad", - "sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26", - "sha256:36bc903cbb393720fad60fc28c10de6acf10dc6cc883f3e24ee4012371399a38", - "sha256:37205cac2a79194e3750b0af2a5720d95f786a55ce7df90c3af697bfa100eaac", - "sha256:3c112550557578c26af18a1ccc9e090bfe03832ae994343cfdacd287db6a6ae7", - "sha256:3dd007d54ee88b46be476e293f48c85048603f5f516008bee124ddd891398ed6", - "sha256:4296f2b1ce8c86a6aea78613c34bb1a672ea0e3de9c6ba08a960efe0b0a09047", - "sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75", - "sha256:49e3ceeabbfb9d66c3aef5af3a60cc43b85c33df25ce03d0031a608b0a8b2e3f", - "sha256:4dc8f9fb58f7364b63fd9f85013b780ef83c11857ae79f2feda41e270468dd9b", - "sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135", - "sha256:53edb4da6925ad13c07b6d26c2a852bd81e364f95301c66e930ab2aef5b5ddd8", - "sha256:5855f8438a7d1d458206a2466bf82b0f104a3724bf96a1c781ab731e4201731a", - "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a", - "sha256:5b6d930f030f8ed98e3e6c98ffa0652bdb82601e7a016ec2ab5d7ff23baa78d1", - "sha256:5bb28c636d87e840583ee3adeb78172efc47c8b26127267f54a9c0ec251d41a9", - "sha256:60bf42e36abfaf9aff1f50f52644b336d4f0a3fd6d8a60ca0d054ac9f713a864", - "sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914", - "sha256:6300b8454aa6930a24b9618fbb54b5a68135092bc666f7b06901f897fa5c2fee", - "sha256:63f3268ba69ace99cab4e3e3b5840b03340efed0948ab8f78d2fd87ee5442a4f", - "sha256:6557b31b5e2c9ddf0de32a691f2312a32f77cd7681d8af66c2692efdbef84c18", - "sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8", - "sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2", - "sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d", - "sha256:6fcf051089389abe060c9cd7caa212c707e58153afa2c649f00346ce6d260f1b", - "sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b", - "sha256:89c687013cb1cd489a0f0ac24febe8c7a666e6e221b783e53ac50ebf68e45d86", - "sha256:8d206346619592c6200148b01a2142798c989edcb9c896f9ac9722a99d4e77e6", - "sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f", - "sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb", - "sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833", - "sha256:99df47edb6bda1249d3e80fdabb1dab8c08ef3975f69aed437cb69d0a5de1e28", - "sha256:9f02365d4e99430a12647f09b6cc8bab61a6564363f313126f775eb4f6ef798e", - "sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415", - "sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902", - "sha256:aca6377c0cb8a8253e493c6b451565ac77e98c2951c45f913e0b52facdcff83f", - "sha256:add36cb2dbb8b736611303cd3bfcee00afd96471b09cda130da3581cbdc56a6d", - "sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9", - "sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d", - "sha256:baa1a4e8f868845af802979fcdbf0bb11f94f1cb7ced4c4b8a351bb60d108145", - "sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066", - "sha256:bf5d821ffabf0ef3533c39c518f3357b171a1651c1ff6827325e4489b0e46c3c", - "sha256:c47adbc92fc1bb2b3274c4b3a43ae0e4573d9fbff4f54cd484555edbf030baf1", - "sha256:cdfba22ea2f0029c9261a4bd07e830a8da012291fbe44dc794e488b6c9bb353a", - "sha256:d6c7ebd4e944c85e2c3421e612a7057a2f48d478d79e61800d81468a8d842207", - "sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f", - "sha256:d8446c54dc28c01e5a2dbac5a25f071f6653e6e40f3a8818e8b45d790fe6ef53", - "sha256:deb993cacb280823246a026e3b2d81c493c53de6acfd5e6bfe31ab3402bb37dd", - "sha256:e0f138900af21926a02425cf736db95be9f4af72ba1bb21453432a07f6082134", - "sha256:e9936f0b261d4df76ad22f8fee3ae83b60d7c3e871292cd42f40b81b70afae85", - "sha256:f0567c4dc99f264f49fe27da5f735f414c4e7e7dd850cfd8e69f0862d7c74ea9", - "sha256:f5653a225f31e113b152e56f154ccbe59eeb1c7487b39b9d9f9cdb58e6c79dc5", - "sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94", - "sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509", - "sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51", - "sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872" - ], - "markers": "python_version >= '3.6'", - "version": "==2.0.1" + "sha256:023af8c54fe63530545f70dd2a2a7eed18d07a9a77b94e8bf1e2ff7f252db9a3", + "sha256:09c86c9643cceb1d87ca08cdc30160d1b7ab49a8a21564868921959bd16441b8", + "sha256:142119fb14a1ef6d758912b25c4e803c3ff66920635c44078666fe7cc3f8f759", + "sha256:1d1fb9b2eec3c9714dd936860850300b51dbaa37404209c8d4cb66547884b7ed", + "sha256:204730fd5fe2fe3b1e9ccadb2bd18ba8712b111dcabce185af0b3b5285a7c989", + "sha256:24c3be29abb6b34052fd26fc7a8e0a49b1ee9d282e3665e8ad09a0a68faee5b3", + "sha256:290b02bab3c9e216da57c1d11d2ba73a9f73a614bbdcc027d299a60cdfabb11a", + "sha256:3028252424c72b2602a323f70fbf50aa80a5d3aa616ea6add4ba21ae9cc9da4c", + "sha256:30c653fde75a6e5eb814d2a0a89378f83d1d3f502ab710904ee585c38888816c", + "sha256:3cace1837bc84e63b3fd2dfce37f08f8c18aeb81ef5cf6bb9b51f625cb4e6cd8", + "sha256:4056f752015dfa9828dce3140dbadd543b555afb3252507348c493def166d454", + "sha256:454ffc1cbb75227d15667c09f164a0099159da0c1f3d2636aa648f12675491ad", + "sha256:598b65d74615c021423bd45c2bc5e9b59539c875a9bdb7e5f2a6b92dfcfc268d", + "sha256:599941da468f2cf22bf90a84f6e2a65524e87be2fce844f96f2dd9a6c9d1e635", + "sha256:5ddea4c352a488b5e1069069f2f501006b1a4362cb906bee9a193ef1245a7a61", + "sha256:62c0285e91414f5c8f621a17b69fc0088394ccdaa961ef469e833dbff64bd5ea", + "sha256:679cbb78914ab212c49c67ba2c7396dc599a8479de51b9a87b174700abd9ea49", + "sha256:6e104c0c2b4cd765b4e83909cde7ec61a1e313f8a75775897db321450e928cce", + "sha256:736895a020e31b428b3382a7887bfea96102c529530299f426bf2e636aacec9e", + "sha256:75bb36f134883fdbe13d8e63b8675f5f12b80bb6627f7714c7d6c5becf22719f", + "sha256:7d2f5d97fcbd004c03df8d8fe2b973fe2b14e7bfeb2cfa012eaa8759ce9a762f", + "sha256:80beaf63ddfbc64a0452b841d8036ca0611e049650e20afcb882f5d3c266d65f", + "sha256:84ad5e29bf8bab3ad70fd707d3c05524862bddc54dc040982b0dbcff36481de7", + "sha256:8da5924cb1f9064589767b0f3fc39d03e3d0fb5aa29e0cb21d43106519bd624a", + "sha256:961eb86e5be7d0973789f30ebcf6caab60b844203f4396ece27310295a6082c7", + "sha256:96de1932237abe0a13ba68b63e94113678c379dca45afa040a17b6e1ad7ed076", + "sha256:a0a0abef2ca47b33fb615b491ce31b055ef2430de52c5b3fb19a4042dbc5cadb", + "sha256:b2a5a856019d2833c56a3dcac1b80fe795c95f401818ea963594b345929dffa7", + "sha256:b8811d48078d1cf2a6863dafb896e68406c5f513048451cd2ded0473133473c7", + "sha256:c532d5ab79be0199fa2658e24a02fce8542df196e60665dd322409a03db6a52c", + "sha256:d3b64c65328cb4cd252c94f83e66e3d7acf8891e60ebf588d7b493a55a1dbf26", + "sha256:d4e702eea4a2903441f2735799d217f4ac1b55f7d8ad96ab7d4e25417cb0827c", + "sha256:d5653619b3eb5cbd35bfba3c12d575db2a74d15e0e1c08bf1db788069d410ce8", + "sha256:d66624f04de4af8bbf1c7f21cc06649c1c69a7f84109179add573ce35e46d448", + "sha256:e67ec74fada3841b8c5f4c4f197bea916025cb9aa3fe5abf7d52b655d042f956", + "sha256:e6f7f3f41faffaea6596da86ecc2389672fa949bd035251eab26dc6697451d05", + "sha256:f02cf7221d5cd915d7fa58ab64f7ee6dd0f6cddbb48683debf5d04ae9b1c2cc1", + "sha256:f0eddfcabd6936558ec020130f932d479930581171368fd728efcfb6ef0dd357", + "sha256:fabbe18087c3d33c5824cb145ffca52eccd053061df1d79d4b66dafa5ad2a5ea", + "sha256:fc3150f85e2dbcf99e65238c842d1cfe69d3e7649b19864c1cc043213d9cd730" + ], + "markers": "python_version >= '3.7'", + "version": "==2.1.0" }, "mccabe": { "hashes": [ @@ -448,17 +419,11 @@ }, "platformdirs": { "hashes": [ - "sha256:1d7385c7db91728b83efd0ca99a5afb296cab9d0ed8313a45ed8ba17967ecfca", - "sha256:440633ddfebcc36264232365d7840a970e75e1018d15b4327d11f91909045fda" + "sha256:7535e70dfa32e84d4b34996ea99c5e432fa29a708d0f4e394bbcb2a8faa4f16d", + "sha256:bcae7cab893c2d310a711b70b24efb93334febe65f8de776ee320b517471e227" ], "markers": "python_version >= '3.7'", - "version": "==2.4.1" - }, - "py-dateutil": { - "hashes": [ - "sha256:7efa2ca17159c590408cb624de9aa10d360f14097cb70dd7559e632f2cf4b048" - ], - "version": "==2.2" + "version": "==2.5.1" }, "pycparser": { "hashes": [ @@ -517,7 +482,7 @@ "sha256:b888b4d56f06f6dcd777210c334e69c737be74755d3e5e9ee3fe67dc18a0ee41", "sha256:e0c4bb8d9f0af0c7f5b1ec4c5036309617d03d56932877f2f7a0beeb5318322f" ], - "markers": "python_version >= '3.6'", + "markers": "python_full_version >= '3.6.0'", "version": "==2.3.0" }, "pylint": { @@ -532,11 +497,11 @@ }, "pypika-tortoise": { "hashes": [ - "sha256:76d1bb45d9cd92ec18037be312d3ee8a26945f787ce981367cfed1ac6e1a11c9", - "sha256:90a61d3d8b9c19cecd1bdd3ca3515114cb7c3a657a6cad9e9e48cfe6db8cc1e0" + "sha256:28fb2715a94ff2f3bc1c4ef6cc46c385c244c27d100aac760231bf612361d5ba", + "sha256:ecdf2d6e0aeb0e15880d9e2ead41362ec7320f37fb25a3a71664c2e1105ad218" ], "markers": "python_version >= '3.7' and python_version < '4.0'", - "version": "==0.1.2" + "version": "==0.1.3" }, "python-dotenv": { "hashes": [ @@ -547,19 +512,19 @@ }, "python-engineio": { "hashes": [ - "sha256:ad06a975f7e14cb3bb7137cbf70fd883804484d29acd58004d1db1e2a7fc0ad3", - "sha256:fed35eeacfa21f53f1fc05ef0cadd65a50780364da3a2be7650eb92f928fdb11" + "sha256:6e1d26977ffefe3b7da1b5df7a8750aedc7686da8201cd90daf36693db122489", + "sha256:85986067cb9f7695347954d4e03491f7d45152c5428c07109a9707e04e8942cb" ], - "markers": "python_version >= '3.6'", - "version": "==4.3.0" + "markers": "python_full_version >= '3.6.0'", + "version": "==4.3.1" }, "python-socketio": { "hashes": [ - "sha256:ca28a0ff0ca5dd05ec5ba4ee2572fe06b96d6f0bc7df384d8b50fbbc06986134", - "sha256:ce972ea1b82aa1811fa10d30cf0d5c251b9a1558c3d66829b6fe70854bcccf0b" + "sha256:5a1b173a4c3471bed363c502a93b36a02e2a29847645c5557237b3359d49caf0", + "sha256:6213f7dfbb87e554472d11151f96bae9059854aa88e4a1e0fa6e97cca1c3fcf4" ], - "markers": "python_version >= '3.6'", - "version": "==5.5.0" + "markers": "python_full_version >= '3.6.0'", + "version": "==5.5.2" }, "pytz": { "hashes": [ @@ -606,17 +571,13 @@ ], "version": "==6.0" }, - "reporting-server": { - "editable": true, - "path": "./packages/reporting-server" - }, "requests": { "hashes": [ - "sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24", - "sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7" + "sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61", + "sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d" ], "index": "pypi", - "version": "==2.26.0" + "version": "==2.27.1" }, "ros-translator": { "editable": true, @@ -627,31 +588,23 @@ "sha256:922c5f4edb3aa1beaa47bf61d65d5380011ff6adcd527f26377d05cb73ed8ec8", "sha256:b657ca2b45aa485da2f7dcfd09fac2e554f7ac51ff3c2f8f2ff962ecd963d91c" ], - "markers": "python_version >= '3.6'", + "markers": "python_full_version >= '3.6.0'", "version": "==3.2.0" }, "setuptools": { "hashes": [ - "sha256:5c89b1a14a67ac5f0956f1cb0aeb7d1d3f4c8ba4e4e1ab7bf1af4933f9a2f0fe", - "sha256:675fcebecb43c32eb930481abf907619137547f4336206e4d673180242e1a278" + "sha256:2347b2b432c891a863acadca2da9ac101eae6169b1d3dfee2ec605ecd50dbfe5", + "sha256:e4f30b9f84e5ab3decf945113119649fec09c1fc3507c6ebffec75646c56e62b" ], "markers": "python_version >= '3.7'", - "version": "==60.2.0" - }, - "six": { - "hashes": [ - "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", - "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.16.0" + "version": "==60.9.3" }, "starlette": { "hashes": [ "sha256:3c8e48e52736b3161e34c9f0e8153b4f32ec5d8995a3ee1d59410d92f75162ed", "sha256:7d49f4a27f8742262ef1470608c59ddbc66baf37c148e938c7038e6bc7a998aa" ], - "markers": "python_version >= '3.6'", + "markers": "python_full_version >= '3.6.0'", "version": "==0.14.2" }, "toml": { @@ -667,7 +620,7 @@ "sha256:05b6166bff487dc068d322585c7ea4ef78deed501cc124060e0f238e89a9231f", "sha256:e3069e4be3ead9668e21cb9b074cd948f7b3113fd9c8bba083f48247aab8b11c" ], - "markers": "python_version >= '3.6'", + "markers": "python_full_version >= '3.6.0'", "version": "==1.2.3" }, "tortoise-orm": { @@ -680,19 +633,19 @@ }, "typing-extensions": { "hashes": [ - "sha256:4ca091dea149f945ec56afb48dae714f21e8692ef22a395223bcd328961b6a0e", - "sha256:7f001e5ac290a0c0401508864c7ec868be4e701886d5b573a9528ed3973d9d3b" + "sha256:1a9462dcc3347a79b1f1c0271fbe79e844580bb598bafa1ed208b94da3cdcd42", + "sha256:21c85e0fe4b9a155d0799430b0ad741cdce7e359660ccbd8b530613e8df88ce2" ], - "markers": "python_version >= '3.6'", - "version": "==4.0.1" + "markers": "python_full_version >= '3.6.0'", + "version": "==4.1.1" }, "urllib3": { "hashes": [ - "sha256:4987c65554f7a2dbf30c18fd48778ef124af6fab771a377103da0585e2336ece", - "sha256:c4fdf4019605b6e5423637e01bc9fe4daef873709a7973e195ceba0a62bbc844" + "sha256:000ca7f471a233c2251c6c7023ee85305721bfdf18621ebff4fd17a8653427ed", + "sha256:0e7c33d9a63e7ddfcb86780aac87befc2fbddf46c58dbb487e0855f7ceec283c" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4.0'", - "version": "==1.26.7" + "version": "==1.26.8" }, "uvicorn": { "extras": [ diff --git a/README.md b/README.md index c8ef336e4..bb0c1121e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![Nightly](https://github.com/open-rmf/rmf-web/actions/workflows/nightly.yml/badge.svg)](https://github.com/open-rmf/rmf-web/actions/workflows/nightly.yml) [![Dashboard End-to-End](https://github.com/open-rmf/rmf-web/actions/workflows/dashboard-e2e.yml/badge.svg)](https://github.com/open-rmf/rmf-web/actions/workflows/dashboard-e2e.yml) [![Reporting End-to-End](https://github.com/open-rmf/rmf-web/actions/workflows/reporting-e2e.yml/badge.svg)](https://github.com/open-rmf/rmf-web/actions/workflows/reporting-e2e.yml) [![react-components](https://github.com/open-rmf/rmf-web/workflows/react-components/badge.svg)](https://github.com/open-rmf/rmf-web/actions?query=workflow%3Areact-components+branch%3Amain) [![dashboard](https://github.com/open-rmf/rmf-web/workflows/dashboard/badge.svg)](https://github.com/open-rmf/rmf-web/actions?query=workflow%3Adashboard+branch%3Amain) [![api-server](https://github.com/open-rmf/rmf-web/workflows/api-server/badge.svg)](https://github.com/open-rmf/rmf-web/actions?query=workflow%3Aapi-server+branch%3Amain) [![reporting](https://github.com/open-rmf/rmf-web/actions/workflows/reporting.yml/badge.svg)](https://github.com/open-rmf/rmf-web/actions/workflows/reporting.yml) [![reporting-server](https://github.com/open-rmf/rmf-web/actions/workflows/reporting-server.yml/badge.svg)](https://github.com/open-rmf/rmf-web/actions/workflows/reporting-server.yml) [![rmf-auth](https://github.com/open-rmf/rmf-web/actions/workflows/rmf-auth.yml/badge.svg)](https://github.com/open-rmf/rmf-web/actions/workflows/rmf-auth.yml) [![ros-translator](https://github.com/open-rmf/rmf-web/actions/workflows/ros-translator.yml/badge.svg)](https://github.com/open-rmf/rmf-web/actions/workflows/ros-translator.yml) [![api-client](https://github.com/open-rmf/rmf-web/actions/workflows/api-client.yml/badge.svg)](https://github.com/open-rmf/rmf-web/actions/workflows/api-client.yml) [![codecov](https://codecov.io/gh/open-rmf/rmf-web/branch/main/graph/badge.svg)](https://codecov.io/gh/open-rmf/rmf-web) +[![Nightly](https://github.com/open-rmf/rmf-web/actions/workflows/nightly.yml/badge.svg)](https://github.com/open-rmf/rmf-web/actions/workflows/nightly.yml) [![Dashboard End-to-End](https://github.com/open-rmf/rmf-web/actions/workflows/dashboard-e2e.yml/badge.svg)](https://github.com/open-rmf/rmf-web/actions/workflows/dashboard-e2e.yml) [![react-components](https://github.com/open-rmf/rmf-web/workflows/react-components/badge.svg)](https://github.com/open-rmf/rmf-web/actions?query=workflow%3Areact-components+branch%3Amain) [![dashboard](https://github.com/open-rmf/rmf-web/workflows/dashboard/badge.svg)](https://github.com/open-rmf/rmf-web/actions?query=workflow%3Adashboard+branch%3Amain) [![api-server](https://github.com/open-rmf/rmf-web/workflows/api-server/badge.svg)](https://github.com/open-rmf/rmf-web/actions?query=workflow%3Aapi-server+branch%3Amain) [![rmf-auth](https://github.com/open-rmf/rmf-web/actions/workflows/rmf-auth.yml/badge.svg)](https://github.com/open-rmf/rmf-web/actions/workflows/rmf-auth.yml) [![ros-translator](https://github.com/open-rmf/rmf-web/actions/workflows/ros-translator.yml/badge.svg)](https://github.com/open-rmf/rmf-web/actions/workflows/ros-translator.yml) [![api-client](https://github.com/open-rmf/rmf-web/actions/workflows/api-client.yml/badge.svg)](https://github.com/open-rmf/rmf-web/actions/workflows/api-client.yml) [![codecov](https://codecov.io/gh/open-rmf/rmf-web/branch/main/graph/badge.svg)](https://codecov.io/gh/open-rmf/rmf-web) # Building the Dashboard diff --git a/codecov.yml b/codecov.yml index c4e0f38f2..04421b984 100644 --- a/codecov.yml +++ b/codecov.yml @@ -15,14 +15,6 @@ flags: paths: - packages/api-server carryforward: true - reporting: - paths: - - packages/reporting - carryforward: true - reporting-server: - paths: - - packages/reporting-server - carryforward: true rmf-auth: paths: - packages/rmf-auth diff --git a/example-deployment/README.md b/example-deployment/README.md index dcb17088d..d9955a99d 100644 --- a/example-deployment/README.md +++ b/example-deployment/README.md @@ -22,8 +22,6 @@ others: * keycloak * nginx * postgres -* minio -* fluentd **NOTE: If you are using this example as a starting point for your own deployment, you must make sure that you have the proper license to use the softwares used in this example.** @@ -146,7 +144,7 @@ When keycloak is ready, test it out by going to https://example.com/auth. The su For this example, we will 1. Create a `rmf-web` realm. -1. Create a `dashboard` and `reporting` client. +1. Create a `dashboard` client. 1. Add https://example.com to the list of allowed origins. 1. Create an example user with user=example password=example. @@ -226,53 +224,6 @@ docker build -t rmf-web/dashboard:example -f docker/dashboard.Dockerfile ws/rmf- docker save rmf-web/dashboard:example | bash -c 'eval $(.bin/minikube docker-env) && docker load' ``` -## [MinIO](https://github.com/minio/minio) -MinIO is a High-Performance Object Storage released under Apache License v2.0. MinIO has several uses but in our case, we will use MinIO to store logs. - -This requires internet connection, see [Deploying in an airgapped network](#deploying-in-an-airgapped-network) if you are in an airgap network. - -We will be using the official minio image, so there is no need to build or publish anything. - -## FluentD - -Fluentd is an open source data collector for unified logging layer. Fluentd allows you to unify data collection and consumption for a better use and understanding of data. - -We will be using the official fluentd image, so there is no need to build or publish anything. - -### Fluentd Configmap - -The fluentd config used in this example can be found at `k8s/example-full/fluentd-configmap.yaml`. - -We have 4 files in our `fluentd-configmap.yaml` : -* `fluent.conf`: Our main config which includes all configurations we want to run. -* `pods-fluent.conf`: `tail` config that sources all pod logs on the `kubernetes` host in the cluster. -* `minio-fluent.conf`: `match` config to capture all logs and send them to MinIO. Every chunck of logs should have 5mb. -Capture all logs and send them to MinIO. Every chunck of logs should have 5mb. -* `minio-fluent-dev.conf`: `match` config to capture all logs and send them to MinIO. Every chunck of logs should have 2kb for development purposes. - -## reporting-server - -build and publish the image - -```bash -docker build -t rmf-web/reporting-server:example -f docker/reporting-server.Dockerfile ws/rmf-web --build-arg BUILDER_TAG=example -docker save rmf-web/reporting-server:example | bash -c 'eval $(.bin/minikube docker-env) && docker load' -``` - -## reporting - -build and publish the image - -```bash -docker build -t rmf-web/reporting:example -f docker/reporting.Dockerfile ws/rmf-web --build-arg BUILDER_TAG=example -``` - -## CronJobs - -Cronjobs are jobs that run periodically on a given schedule. You can configure the schedule following this [cron schedule syntax](https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/#cron-schedule-syntax) - -The jobs we will run in this example can be found at `k8s/example-full/cronjobs.yaml`, it clean old logs from the database once a day at 0004 (timezone depends on the configuration of the kubernetes cluster). - ## Test the deployment If not done so already, launch the office demo @@ -307,21 +258,17 @@ Sit back and relax, everything will be done for you! There are certain parts that requires an internet connection to fetch the docker images and source codes, in order to deploy from within an airgapped network, you can use obtain the images first, then push them directly to minikube. For example -when you have internet, run this to get the keycloak, MinIO and Fluentd images +when you have internet, run this to get the keycloak image ```bash docker pull quay.io/keycloak/keycloak:12.0.4 -docker pull minio/minio:RELEASE.2021-03-10T05-11-33Z -docker pull fluent/fluentd-kubernetes-daemonset:v1.12.2-debian-s3-1.0 ``` now, connect to the airgapped network and push the images to minikube ```bash docker save quay.io/keycloak/keycloak:12.0.4 | bash -c 'eval $(.bin/minikube docker-env) && docker load' -docker save minio/minio:RELEASE.2021-03-10T05-11-33Z | bash -c 'eval $(.bin/minikube docker-env) && docker load' -docker save fluent/fluentd-kubernetes-daemonset:v1.12.2-debian-s3-1.0 | bash -c 'eval $(.bin/minikube docker-env) && docker load' ``` -now you can deploy keycloak, MinIO and Fluentd without require access to the internet +now you can deploy keycloak without require access to the internet If connection to the internet from the same PC is not possible, you can use `docker save` to save the image into a tarball, then transfer it to the minikube PC through whatever method possible (thumbdrives, cds etc) and use `docker load` to load the image. Then you can use `.bin/minikube load` to push it into minikube. diff --git a/example-deployment/deploy.sh b/example-deployment/deploy.sh index 9c612aae0..eeaca7f35 100755 --- a/example-deployment/deploy.sh +++ b/example-deployment/deploy.sh @@ -44,19 +44,6 @@ docker build -t rmf-web/dashboard:$rmf_web_ver -f docker/dashboard.Dockerfile "$ echo '📤 publishing dashboard image...' docker save rmf-web/dashboard:$rmf_web_ver | bash -c 'eval $(.bin/minikube docker-env) && docker load' -docker build -t rmf-web/reporting-server:$rmf_web_ver -f docker/reporting-server.Dockerfile "$rmf_web_ws" --build-arg BUILDER_TAG=$rmf_web_ver -echo '📤 publishing reporting-server image...' -docker save rmf-web/reporting-server:$rmf_web_ver | bash -c 'eval $(.bin/minikube docker-env) && docker load' - -docker build -t rmf-web/reporting:$rmf_web_ver -f docker/reporting.Dockerfile "$rmf_web_ws" \ - --build-arg BUILDER_TAG=$rmf_web_ver \ - --build-arg PUBLIC_URL='/reporting' \ - --build-arg REACT_APP_REPORTING_SERVER='https://example.com/logserver/api/v1' \ - --build-arg REACT_APP_AUTH_PROVIDER='keycloak' \ - --build-arg REACT_APP_KEYCLOAK_CONFIG='{ "realm": "rmf-web", "clientId": "reporting", "url": "https://example.com/auth" }' -echo '📤 publishing reporting image...' -docker save rmf-web/reporting:$rmf_web_ver | bash -c 'eval $(.bin/minikube docker-env) && docker load' - ### deploy phase # need to deploy keycloak separately because we need to register the apps prior to generating @@ -79,6 +66,4 @@ echo '✅ successfully setup keycloak' # deploy all other components export RMF_SERVER_TAG=$rmf_web_ver export DASHBOARD_TAG=$rmf_web_ver -export REPORTING_SERVER_TAG=$rmf_web_ver -export REPORTING_TAG=$rmf_web_ver ./kustomize-env.sh k8s/example-full | kubectl apply -f - diff --git a/example-deployment/docker/reporting-server.Dockerfile b/example-deployment/docker/reporting-server.Dockerfile deleted file mode 100644 index 1608cc7db..000000000 --- a/example-deployment/docker/reporting-server.Dockerfile +++ /dev/null @@ -1,22 +0,0 @@ -ARG BUILDER_TAG -FROM rmf-web/builder:$BUILDER_TAG - -COPY . /root/rmf-web -RUN cd /root/rmf-web && \ - lerna run prepare --include-dependencies --scope=reporting-server && \ - cd packages/reporting-server && \ - npm run prepack - -FROM python:3.9 - -SHELL ["bash", "-c"] - -COPY --from=0 /root/rmf-web/packages/reporting-server/dist/ /root/reporting-server - -RUN cd /root/reporting-server && \ - pip3 install $(ls -1 | grep '.*.whl')[postgres] - -RUN echo -e '#!/bin/bash\n\ - exec reporting_server "$@"\n\ - ' > /docker-entry-point.sh && chmod +x /docker-entry-point.sh -ENTRYPOINT ["/docker-entry-point.sh"] diff --git a/example-deployment/docker/reporting.Dockerfile b/example-deployment/docker/reporting.Dockerfile deleted file mode 100644 index a083b44c9..000000000 --- a/example-deployment/docker/reporting.Dockerfile +++ /dev/null @@ -1,43 +0,0 @@ -ARG BUILDER_TAG -FROM rmf-web/builder:$BUILDER_TAG - -COPY . /root/rmf-web -RUN cd /root/rmf-web && \ - lerna run prepare --include-dependencies --scope=reporting - -ARG PUBLIC_URL -ARG REACT_APP_REPORTING_SERVER -ARG REACT_APP_AUTH_PROVIDER -ARG REACT_APP_KEYCLOAK_CONFIG - -RUN cd /root/rmf-web/packages/reporting && \ - PUBLIC_URL='/reporting' \ - REACT_APP_REPORTING_SERVER='https://example.com/logserver/api/v1' \ - REACT_APP_AUTH_PROVIDER='keycloak' \ - REACT_APP_KEYCLOAK_CONFIG='{ "realm": "rmf-web", "clientId": "reporting", "url": "https://example.com/auth" }' \ - npm run build - -### - -FROM nginx:stable -COPY --from=0 /root/rmf-web/packages/reporting/build /usr/share/nginx/html/reporting -SHELL ["bash", "-c"] -RUN echo -e 'server {\n\ - listen 80;\n\ - server_name localhost;\n\ -\n\ - location / {\n\ - root /usr/share/nginx/html;\n\ - index index.html index.htm;\n\ - try_files $uri /reporting/index.html;\n\ - }\n\ -\n\ - location /reporting/static/ {\n\ - expires 30d;\n\ - }\n\ -\n\ - error_page 500 502 503 504 /50x.html;\n\ - location = /50x.html {\n\ - root /usr/share/nginx/html;\n\ - }\n\ -}\n' > /etc/nginx/conf.d/default.conf diff --git a/example-deployment/k8s/base/reporting-server/fluentd.yaml b/example-deployment/k8s/base/reporting-server/fluentd.yaml deleted file mode 100644 index 57d2c56a6..000000000 --- a/example-deployment/k8s/base/reporting-server/fluentd.yaml +++ /dev/null @@ -1,99 +0,0 @@ -apiVersion: v1 -kind: ServiceAccount -metadata: - name: fluentd ---- -# to set permission and groups -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: fluentd - namespace: default -rules: -- apiGroups: - - "" - resources: - - pods - - namespaces - verbs: - - get - - list - - watch ---- -kind: RoleBinding -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: fluentd -roleRef: - kind: Role - name: fluentd - apiGroup: rbac.authorization.k8s.io -subjects: -- kind: ServiceAccount - name: fluentd - namespace: default - ---- -apiVersion: apps/v1 -kind: DaemonSet -metadata: - name: fluentd - namespace: default - labels: - k8s-app: fluentd-logging - version: v1 -spec: - selector: - matchLabels: - k8s-app: fluentd-logging - version: v1 - template: - metadata: - labels: - k8s-app: fluentd-logging - version: v1 - spec: - serviceAccount: fluentd - serviceAccountName: fluentd - tolerations: - - key: node-role.kubernetes.io/master - effect: NoSchedule - containers: - - name: fluentd - image: fluent/fluentd-kubernetes-daemonset:v1.12.2-debian-s3-1.0 - resources: - limits: - memory: 200Mi - requests: - cpu: 100m - memory: 200Mi - volumeMounts: - - name: fluentd-config - mountPath: /fluentd/etc - - name: varlog - mountPath: /var/log - - name: varlibdockercontainers - mountPath: /var/lib/docker/containers - readOnly: true - env: - # FIXME: This should be on k8s secrets. - # MinIO access key and secret key - - name: MINIO_ACCESS_KEY - value: "minio" - - name: MINIO_SECRET_KEY - value: "minio123" - - name: FLUENTD_USER - value: "fluentd" - - name: FLUENTD_PASSWORD - value: "fluentd123" - terminationGracePeriodSeconds: 30 - volumes: - - name: fluentd-config - configMap: - name: fluentd-config - - name: varlog - hostPath: - path: /var/log - - name: varlibdockercontainers - hostPath: - path: /var/lib/docker/containers \ No newline at end of file diff --git a/example-deployment/k8s/base/reporting-server/kustomization.yaml b/example-deployment/k8s/base/reporting-server/kustomization.yaml deleted file mode 100644 index 2d49fdb51..000000000 --- a/example-deployment/k8s/base/reporting-server/kustomization.yaml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization - -resources: - - reporting-server.yaml - - minio.yaml - - fluentd.yaml diff --git a/example-deployment/k8s/base/reporting-server/minio.yaml b/example-deployment/k8s/base/reporting-server/minio.yaml deleted file mode 100644 index 5434aa3cd..000000000 --- a/example-deployment/k8s/base/reporting-server/minio.yaml +++ /dev/null @@ -1,121 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - # This name uniquely identifies the service - name: minio-service -spec: - type: LoadBalancer - ports: - - port: 9000 - targetPort: 9000 - protocol: TCP - selector: - # Looks for labels `app:minio` in the namespace and applies the spec - app: minio - ---- - -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - # This name uniquely identifies the PVC. This is used in deployment. - name: minio-pv-claim -spec: - # Read more about access modes here: http://kubernetes.io/docs/user-guide/persistent-volumes/#access-modes - accessModes: - # The volume is mounted as read-write by a single node - - ReadWriteOnce - resources: - # This is the request for storage. Should be available in the cluster. - requests: - storage: 10Gi - ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - # This name uniquely identifies the Deployment - name: minio -spec: - selector: - matchLabels: - app: minio # has to match .spec.template.metadata.labels - strategy: - # Specifies the strategy used to replace old Pods by new ones - # Refer: https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#strategy - type: Recreate - template: - metadata: - labels: - # This label is used as a selector in Service definition - app: minio - spec: - # Volumes used by this deployment - volumes: - - name: data - # This volume is based on PVC - persistentVolumeClaim: - # Name of the PVC created earlier - claimName: minio-pv-claim - containers: - - name: minio - # Volume mounts for this container - volumeMounts: - # Volume 'data' is mounted to path '/data' - - name: data - mountPath: "/data" - # Pulls the lastest Minio image from Docker Hub - image: minio/minio:RELEASE.2021-03-10T05-11-33Z - args: - - server - - /data - env: - # FIXME: This should be on k8s secrets. - # MinIO access key and secret key - - name: MINIO_ACCESS_KEY - value: "minio" - - name: MINIO_SECRET_KEY - value: "minio123" - ports: - - containerPort: 9000 - # Readiness probe detects situations when MinIO server instance - # is not ready to accept traffic. Kubernetes doesn't forward - # traffic to the pod while readiness checks fail. - readinessProbe: - httpGet: - path: /minio/health/ready - port: 9000 - initialDelaySeconds: 120 - periodSeconds: 20 - # Liveness probe detects situations where MinIO server instance - # is not working properly and needs restart. Kubernetes automatically - # restarts the pods if liveness checks fail. - livenessProbe: - httpGet: - path: /minio/health/live - port: 9000 - initialDelaySeconds: 120 - periodSeconds: 20 ---- - -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: minio-ingress - labels: - app: minio -spec: - tls: - - hosts: - - example.com - rules: - - host: example.com - http: - paths: - - path: /minio - pathType: Prefix - backend: - service: - name: minio-service - port: - number: 9000 diff --git a/example-deployment/k8s/base/reporting-server/reporting-server.yaml b/example-deployment/k8s/base/reporting-server/reporting-server.yaml deleted file mode 100644 index d05d0b6c2..000000000 --- a/example-deployment/k8s/base/reporting-server/reporting-server.yaml +++ /dev/null @@ -1,154 +0,0 @@ -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: reporting-server-db - labels: - app: reporting-server - tier: db -spec: - accessModes: - - ReadWriteOnce - volumeMode: Filesystem - resources: - requests: - storage: 1Gi ---- -apiVersion: v1 -kind: Service -metadata: - name: reporting-server-db - labels: - app: reporting-server - tier: db -spec: - selector: - app: reporting-server - tier: db - ports: - - port: 5432 ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: reporting-server-db - labels: - app: reporting-server - tier: db -spec: - replicas: 1 - selector: - matchLabels: - app: reporting-server - tier: db - template: - metadata: - labels: - app: reporting-server - tier: db - spec: - containers: - - name: postgresql - image: postgres:13 - env: - - name: POSTGRES_USER - value: reporting-server - - name: POSTGRES_PASSWORD - value: reporting-server - volumeMounts: - - mountPath: /var/lib/postgresql/data - name: reporting-server-db - volumes: - - name: reporting-server-db - persistentVolumeClaim: - claimName: reporting-server-db ---- -apiVersion: v1 -kind: Service -metadata: - name: reporting-server - labels: - app: reporting-server - tier: app -spec: - type: LoadBalancer - selector: - app: reporting-server - tier: app - ports: - - protocol: TCP - port: 8002 - targetPort: 8002 - name: report - - protocol: TCP - port: 8003 - targetPort: 8003 - name: fluentd ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: reporting-server - labels: - app: reporting-server - tier: app -spec: - selector: - matchLabels: - app: reporting-server - tier: app - template: - metadata: - labels: - app: reporting-server - tier: app - spec: - containers: - - name: reporting-server - image: rmf-web/reporting-server - ports: - - containerPort: 8002 - name: report - - containerPort: 8003 - name: fluentd - env: - - name: RMF_REPORT_REST_SERVER_CONFIG - value: /reporting-server-config/reporting_server_config.py - volumeMounts: - - mountPath: /jwt-configmap - name: jwt-pub-key - - mountPath: /reporting-server-config - name: reporting-server-config - hostNetwork: true - dnsPolicy: ClusterFirstWithHostNet - volumes: - - name: jwt-pub-key - configMap: - name: jwt-pub-key - - name: reporting-server-config - configMap: - name: reporting-server-config ---- -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: reporting-server - labels: - app: reporting-server - tier: app - annotations: - nginx.ingress.kubernetes.io/rewrite-target: /$2 -spec: - tls: - - hosts: - - example.com - rules: - - host: example.com - http: - paths: - - path: /logserver/api/v1(/|$)(.*) - pathType: Prefix - backend: - service: - name: reporting-server - port: - number: 8002 diff --git a/example-deployment/k8s/base/reporting/kustomization.yaml b/example-deployment/k8s/base/reporting/kustomization.yaml deleted file mode 100644 index 6f858aa37..000000000 --- a/example-deployment/k8s/base/reporting/kustomization.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization - -resources: - - reporting.yaml diff --git a/example-deployment/k8s/base/reporting/reporting.yaml b/example-deployment/k8s/base/reporting/reporting.yaml deleted file mode 100644 index 89cf1a8a6..000000000 --- a/example-deployment/k8s/base/reporting/reporting.yaml +++ /dev/null @@ -1,56 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: reporting - labels: - app: reporting -spec: - type: LoadBalancer - selector: - app: reporting - ports: - - protocol: TCP - port: 80 ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: reporting - labels: - app: reporting -spec: - selector: - matchLabels: - app: reporting - template: - metadata: - labels: - app: reporting - spec: - containers: - - name: reporting - image: rmf-web/reporting - ports: - - containerPort: 80 ---- -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: reporting - labels: - app: reporting -spec: - tls: - - hosts: - - example.com - rules: - - host: example.com - http: - paths: - - path: /reporting - pathType: Prefix - backend: - service: - name: reporting - port: - number: 80 diff --git a/example-deployment/k8s/example-full/cronjobs.yaml b/example-deployment/k8s/example-full/cronjobs.yaml deleted file mode 100644 index 2df47a579..000000000 --- a/example-deployment/k8s/example-full/cronjobs.yaml +++ /dev/null @@ -1,35 +0,0 @@ -apiVersion: batch/v1beta1 -kind: CronJob -metadata: - name: reporting-server-clean-logs -spec: - # Once a day - schedule: "4 0 * * *" - jobTemplate: - spec: - template: - spec: - restartPolicy: Never - containers: - - name: reporting-server-clean-logs - image: rmf-web/reporting-server - command: - - /bin/sh - - -c - - reporting_server_clean_logs - env: - - name: RMF_REPORT_REST_SERVER_CONFIG - value: /reporting-server-config/reporting_server_config.py - - volumeMounts: - - mountPath: /jwt-configmap - name: jwt-pub-key - - mountPath: /reporting-server-config - name: reporting-server-config - volumes: - - name: jwt-pub-key - configMap: - name: jwt-pub-key - - name: reporting-server-config - configMap: - name: reporting-server-config \ No newline at end of file diff --git a/example-deployment/k8s/example-full/fluentd-configmap.yaml b/example-deployment/k8s/example-full/fluentd-configmap.yaml deleted file mode 100644 index c3c114063..000000000 --- a/example-deployment/k8s/example-full/fluentd-configmap.yaml +++ /dev/null @@ -1,167 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: fluentd-config -data: - fluent.conf: |- - ################################################################ - # This source gets all logs from local docker host - @include pods-fluent.conf - @include reporting-server.conf - @include reporting-server-all-logs.conf - @include keycloak-logs.conf - # Use this configuration for production - @include minio-fluent.conf - # @include minio-fluent-dev.conf - # This is for running fluentd in the cloud - pods-fluent.conf: |- - - @type tail - read_from_head true - tag rmf.all.* - path /var/log/containers/*.log - pos_file /var/log/fluent/fluentd-rmf-all.log.pos - exclude_path ["/var/log/containers/fluent*"] - format json - time_format %Y-%m-%dT%H:%M:%S.%NZ - - - - @type kubernetes_metadata - - - - @type tail - read_from_head true - tag rmf.server.* - path /var/log/containers/rmf-server*.log - pos_file /var/log/fluent/fluentd-rmf-server.log.pos - exclude_path ["/var/log/containers/fluent*"] - - @type kubernetes - @type "#{ENV['FLUENT_CONTAINER_TAIL_PARSER_TYPE'] || 'json'}" - time_format %Y-%m-%dT%H:%M:%S.%NZ - - - - - @type tail - read_from_head true - tag keycloak.* - path /var/log/containers/keycloak*.log - pos_file /var/log/fluent/fluentd-keycloak.log.pos - exclude_path ["/var/log/containers/keycloak-db*"] - - @type kubernetes - @type "#{ENV['FLUENT_CONTAINER_TAIL_PARSER_TYPE'] || 'json'}" - time_format %Y-%m-%dT%H:%M:%S.%NZ - - - - - @type tail - read_from_head true - tag minio.s3.* - path /var/log/containers/*.log - pos_file /var/log/fluent/fluentd-all-containers.log.pos - exclude_path ["/var/log/containers/fluent*"] - - @type kubernetes - @type "#{ENV['FLUENT_CONTAINER_TAIL_PARSER_TYPE'] || 'json'}" - time_format %Y-%m-%dT%H:%M:%S.%NZ - - - - minio-fluent-dev.conf: |- - - @type s3 - aws_key_id "#{ENV['MINIO_ACCESS_KEY']}" # The access key for Minio - aws_sec_key "#{ENV['MINIO_SECRET_KEY']}" # The secret key for Minio - s3_bucket test # The bucket to store the log data - s3_endpoint http://minio-service:9000/ # The endpoint URL - # s3_region us-east-1 # See the region settings of your Minio server - path logs/ # This prefix is added to each file - force_path_style true # This prevents AWS SDK from breaking endpoint URL - time_slice_format %Y%m%d%H%M # This timestamp is added to each file name - - @type file - path /var/log/fluent/s3 - timekey 1m # Flush the accumulated chunks every 1 min - timekey_wait 1s # Wait for 1 seconds before flushing - timekey_use_utc true # Use this option if you prefer UTC timestamps - chunk_limit_size 2k # The maximum size of each chunk - - - - minio-fluent.conf: |- - - @type s3 - aws_key_id "#{ENV['MINIO_ACCESS_KEY']}" # The access key for Minio - aws_sec_key "#{ENV['MINIO_SECRET_KEY']}" # The secret key for Minio - s3_bucket test # The bucket to store the log data - s3_endpoint http://minio-service:9000/ # The endpoint URL - # s3_region us-east-1 # See the region settings of your Minio server - path logs/ # This prefix is added to each file - force_path_style true # This prevents AWS SDK from breaking endpoint URL - time_slice_format %Y%m%d%H%M # This timestamp is added to each file name - - @type file - path /var/log/fluent/s3 - timekey 60m # Flush the accumulated chunks every hour - timekey_wait 1m # Wait for 60 seconds before flushing - timekey_use_utc true # Use this option if you prefer UTC timestamps - chunk_limit_size 5mb # The maximum size of each chunk - - - - reporting-server.conf: |- - - @type http - endpoint http://reporting-server:8003/log/rmfserver/ - http_method post - open_timeout 3 - # content_type - - @type json - - json_array true - - flush_interval 10s - - - method basic - username "#{ENV['FLUENTD_USER']}" - password "#{ENV['FLUENTD_PASSWORD']}" - - - - reporting-server-all-logs.conf: |- - - @type http - endpoint http://reporting-server:8003/log/all/ - http_method post - open_timeout 3 - - @type json - - json_array true - - flush_interval 10s - - - - keycloak-logs.conf: |- - - @type http - endpoint http://reporting-server:8003/log/keycloak/ - http_method post - open_timeout 3 - - @type json - - json_array true - - flush_interval 10s - - - \ No newline at end of file diff --git a/example-deployment/k8s/example-full/kustomization.yaml b/example-deployment/k8s/example-full/kustomization.yaml index 20c439688..78d698388 100644 --- a/example-deployment/k8s/example-full/kustomization.yaml +++ b/example-deployment/k8s/example-full/kustomization.yaml @@ -6,8 +6,6 @@ commonLabels: bases: - ../example-lite - - ../base/reporting-server - - ../base/reporting resources: - cronjobs.yaml @@ -21,19 +19,8 @@ images: newTag: ${RMF_SERVER_TAG} - name: rmf-web/dashboard newTag: ${DASHBOARD_TAG} - - name: rmf-web/reporting-server - newTag: ${REPORTING_SERVER_TAG} - - name: rmf-web/reporting - newTag: ${REPORTING_TAG} configMapGenerator: - name: jwt-pub-key files: - keycloak/jwt-pub-key.pub - - name: reporting-server-config - files: - - reporting_server_config.py - - name: rmf-server-config - files: - - rmf_server_config.py - behavior: replace diff --git a/example-deployment/k8s/example-full/reporting_server_config.py b/example-deployment/k8s/example-full/reporting_server_config.py deleted file mode 100644 index ee5e0aedc..000000000 --- a/example-deployment/k8s/example-full/reporting_server_config.py +++ /dev/null @@ -1,19 +0,0 @@ -from copy import deepcopy - -from rest_server.default_config import config as default_config - -config = deepcopy(default_config) -config["host"] = "0.0.0.0" -config["port"] = 8002 -config["port_fluentd"] = 8003 -config[ - "db_url" -] = "postgres://reporting-server:reporting-server@reporting-server-db/reporting-server" -config["public_url"] = "https://example.com/logserver/api/v1" -config["log_level"] = "INFO" -config["jwt_public_key"] = "/jwt-configmap/jwt-pub-key.pub" -config[ - "oidc_url" -] = "https://example.com/auth/realms/rmf-web/.well-known/openid-configuration" -config["aud"] = "reporting" -config["iss"] = "https://example.com/auth/realms/rmf-web" diff --git a/example-deployment/keycloak-tools/bootstrap-keycloak.js b/example-deployment/keycloak-tools/bootstrap-keycloak.js index f5aa75f09..f4b6c4023 100644 --- a/example-deployment/keycloak-tools/bootstrap-keycloak.js +++ b/example-deployment/keycloak-tools/bootstrap-keycloak.js @@ -77,21 +77,6 @@ async function createAudienceClientScope(headers, name, description, audience) { }, ); - await tryRequest( - `${baseUrl}/admin/realms/rmf-web/clients`, - { - method: 'POST', - headers: authHeaders(token), - }, - { - clientId: 'reporting', - rootUrl: 'https://example.com', - redirectUris: ['https://example.com/*'], - webOrigins: ['https://example.com'], - publicClient: true, - }, - ); - { // create admin user await tryRequest( @@ -182,14 +167,7 @@ async function createAudienceClientScope(headers, name, description, audience) { 'dashboard', ); - await createAudienceClientScope( - authHeaders(token), - 'reporting', - 'reporting scope', - 'reporting', - ); - - // get existing clients (dashboard and reporting) + // get existing clients const clientsRaw = await request(`${baseUrl}/admin/realms/rmf-web/clients`, { method: 'GET', headers: authHeaders(token), @@ -201,11 +179,7 @@ async function createAudienceClientScope(headers, name, description, audience) { return item.clientId === 'dashboard'; })[0].id; - const reportingId = clientArray.filter(function (item) { - return item.clientId === 'reporting'; - })[0].id; - - // get existing clients (dashboard and reporting) + // get existing clients const clientsScopeRaw = await request(`${baseUrl}/admin/realms/rmf-web/client-scopes`, { method: 'GET', headers: authHeaders(token), @@ -216,12 +190,7 @@ async function createAudienceClientScope(headers, name, description, audience) { return item.name == 'dashboard'; })[0].id; - const clientScopeReportingId = clientScopesArray.filter(function (item) { - return item.name == 'reporting'; - })[0].id; - await setClientScopeToClient(authHeaders(token), dashboardId, clientScopeDashboardId); - await setClientScopeToClient(authHeaders(token), reportingId, clientScopeReportingId); } catch (e) { process.exitCode = 1; } diff --git a/package-lock.json b/package-lock.json index 90414e597..54b3da144 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,9 +11,6 @@ "packages/dashboard", "packages/dashboard-e2e", "packages/react-components", - "packages/reporting", - "packages/reporting-e2e", - "packages/reporting-server", "packages/rmf-auth", "packages/rmf-models", "packages/ros-translator", @@ -17363,12 +17360,6 @@ "node": ">=4" } }, - "node_modules/cssfontparser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/cssfontparser/-/cssfontparser-1.2.1.tgz", - "integrity": "sha1-9AIvyPlwDGgCnVQghK+69CWj8+M=", - "dev": true - }, "node_modules/cssnano": { "version": "4.1.11", "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-4.1.11.tgz", @@ -24169,16 +24160,6 @@ "node": ">= 10.14.2" } }, - "node_modules/jest-canvas-mock": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/jest-canvas-mock/-/jest-canvas-mock-2.3.1.tgz", - "integrity": "sha512-5FnSZPrX3Q2ZfsbYNE3wqKR3+XorN8qFzDzB5o0golWgt6EOX1+emBnpOc9IAQ+NXFj8Nzm3h7ZdE/9H0ylBcg==", - "dev": true, - "dependencies": { - "cssfontparser": "^1.2.1", - "moo-color": "^1.0.2" - } - }, "node_modules/jest-changed-files": { "version": "26.6.2", "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-26.6.2.tgz", @@ -27558,15 +27539,6 @@ "node": ">=10" } }, - "node_modules/moo-color": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/moo-color/-/moo-color-1.0.2.tgz", - "integrity": "sha512-5iXz5n9LWQzx/C2WesGFfpE6RLamzdHwsn3KpfzShwbfIqs7stnoEpaNErf/7+3mbxwZ4s8Foq7I0tPxw7BWHg==", - "dev": true, - "dependencies": { - "color-name": "^1.1.4" - } - }, "node_modules/move-concurrently": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", @@ -33848,18 +33820,6 @@ "node": ">=0.10" } }, - "node_modules/reporting": { - "resolved": "packages/reporting", - "link": true - }, - "node_modules/reporting-e2e": { - "resolved": "packages/reporting-e2e", - "link": true - }, - "node_modules/reporting-server": { - "resolved": "packages/reporting-server", - "link": true - }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -42266,60 +42226,6 @@ "react-router-dom": "^5.2.0" } }, - "packages/reporting": { - "version": "0.0.1", - "license": "Apache-2.0", - "devDependencies": { - "@fontsource/roboto": "^4.3.0", - "@storybook/addon-actions": "^6.3.7", - "@storybook/addon-essentials": "^6.3.7", - "@storybook/addon-links": "^6.3.7", - "@storybook/react": "^6.3.7", - "@testing-library/jest-dom": "^5.11.4", - "@testing-library/react": "^11.2.3", - "@testing-library/react-hooks": "^5.0.3", - "@testing-library/user-event": "^12.1.9", - "@types/debug": "^4.1.5", - "@types/jest": "^26.0.13", - "@types/react": "^17.0.19", - "@types/react-dom": "^17.0.9", - "@types/react-router": "^5.1.7", - "@types/react-router-dom": "^5.1.7", - "axios": "^0.21.1", - "clsx": "^1.1.1", - "jest-canvas-mock": "^2.3.1", - "react": "^17.0.2", - "react-components": "file:../react-components", - "react-router": "^5.2.0", - "react-router-dom": "^5.2.0", - "react-scripts": "^4.0.3", - "reporting-server": "file:../reporting-server", - "rmf-auth": "file:../rmf-auth", - "ts-node": "^9.1.1", - "typescript": "~4.4.4" - } - }, - "packages/reporting-e2e": { - "devDependencies": { - "@types/mocha": "^9.0.0", - "@wdio/cli": "7.11.1", - "@wdio/local-runner": "7.11.1", - "@wdio/mocha-framework": "7.11.1", - "@wdio/spec-reporter": "7.10.1", - "concurrently": "^5.3.0", - "node-fetch": "^2.6.1", - "reporting": "file:../reporting", - "serve": "^11.3.2", - "ts-node": "^9.1.1", - "typescript": "~4.4.4" - } - }, - "packages/reporting-server": { - "version": "0.0.1", - "devDependencies": { - "pipenv-install": "file:../../pipenv-install" - } - }, "packages/rmf-auth": { "version": "0.0.1", "license": "Apache-2.0", @@ -55776,12 +55682,6 @@ "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", "dev": true }, - "cssfontparser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/cssfontparser/-/cssfontparser-1.2.1.tgz", - "integrity": "sha1-9AIvyPlwDGgCnVQghK+69CWj8+M=", - "dev": true - }, "cssnano": { "version": "4.1.11", "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-4.1.11.tgz", @@ -61092,16 +60992,6 @@ "jest-cli": "^26.6.3" } }, - "jest-canvas-mock": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/jest-canvas-mock/-/jest-canvas-mock-2.3.1.tgz", - "integrity": "sha512-5FnSZPrX3Q2ZfsbYNE3wqKR3+XorN8qFzDzB5o0golWgt6EOX1+emBnpOc9IAQ+NXFj8Nzm3h7ZdE/9H0ylBcg==", - "dev": true, - "requires": { - "cssfontparser": "^1.2.1", - "moo-color": "^1.0.2" - } - }, "jest-changed-files": { "version": "26.6.2", "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-26.6.2.tgz", @@ -63839,15 +63729,6 @@ } } }, - "moo-color": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/moo-color/-/moo-color-1.0.2.tgz", - "integrity": "sha512-5iXz5n9LWQzx/C2WesGFfpE6RLamzdHwsn3KpfzShwbfIqs7stnoEpaNErf/7+3mbxwZ4s8Foq7I0tPxw7BWHg==", - "dev": true, - "requires": { - "color-name": "^1.1.4" - } - }, "move-concurrently": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", @@ -68906,60 +68787,6 @@ "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", "dev": true }, - "reporting": { - "version": "file:packages/reporting", - "requires": { - "@fontsource/roboto": "^4.3.0", - "@storybook/addon-actions": "^6.3.7", - "@storybook/addon-essentials": "^6.3.7", - "@storybook/addon-links": "^6.3.7", - "@storybook/react": "^6.3.7", - "@testing-library/jest-dom": "^5.11.4", - "@testing-library/react": "^11.2.3", - "@testing-library/react-hooks": "^5.0.3", - "@testing-library/user-event": "^12.1.9", - "@types/debug": "^4.1.5", - "@types/jest": "^26.0.13", - "@types/react": "^17.0.19", - "@types/react-dom": "^17.0.9", - "@types/react-router": "^5.1.7", - "@types/react-router-dom": "^5.1.7", - "axios": "^0.21.1", - "clsx": "^1.1.1", - "jest-canvas-mock": "^2.3.1", - "react": "^17.0.2", - "react-components": "file:../react-components", - "react-router": "^5.2.0", - "react-router-dom": "^5.2.0", - "react-scripts": "^4.0.3", - "reporting-server": "file:../reporting-server", - "rmf-auth": "file:../rmf-auth", - "ts-node": "^9.1.1", - "typescript": "~4.4.4" - } - }, - "reporting-e2e": { - "version": "file:packages/reporting-e2e", - "requires": { - "@types/mocha": "^9.0.0", - "@wdio/cli": "7.11.1", - "@wdio/local-runner": "7.11.1", - "@wdio/mocha-framework": "7.11.1", - "@wdio/spec-reporter": "7.10.1", - "concurrently": "^5.3.0", - "node-fetch": "^2.6.1", - "reporting": "file:../reporting", - "serve": "^11.3.2", - "ts-node": "^9.1.1", - "typescript": "~4.4.4" - } - }, - "reporting-server": { - "version": "file:packages/reporting-server", - "requires": { - "pipenv-install": "file:../../pipenv-install" - } - }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -69394,7 +69221,7 @@ "@types/react-router": "^5.1.7", "@types/react-router-dom": "^5.1.7", "@types/reactour": "^1.17.1", - "ajv": "*", + "ajv": "^8.10.0", "api-client": "file:../api-client", "api-server": "file:../api-server", "axios": "^0.21.1", diff --git a/package.json b/package.json index eb186afa4..b1aa69799 100644 --- a/package.json +++ b/package.json @@ -7,9 +7,6 @@ "packages/dashboard", "packages/dashboard-e2e", "packages/react-components", - "packages/reporting", - "packages/reporting-e2e", - "packages/reporting-server", "packages/rmf-auth", "packages/rmf-models", "packages/ros-translator", diff --git a/packages/react-components/lib/index.ts b/packages/react-components/lib/index.ts index bdc90588f..320742b77 100644 --- a/packages/react-components/lib/index.ts +++ b/packages/react-components/lib/index.ts @@ -19,7 +19,6 @@ export * from './logo-button'; export * from './map'; export * from './navigation-bar'; export * from './place'; -export * from './reports'; export * from './robots'; export * from './simple-filter'; export * from './simple-info'; diff --git a/packages/react-components/lib/reports/default-date-form.spec.tsx b/packages/react-components/lib/reports/default-date-form.spec.tsx deleted file mode 100644 index f30a4d8e6..000000000 --- a/packages/react-components/lib/reports/default-date-form.spec.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { render } from '@testing-library/react'; -import React from 'react'; -import { TestLocalizationProvider } from '../test/locale'; -import { DefaultDatesForm } from './default-dates-form'; -import { reportConfigProps } from './utils.spec'; - -describe('Default date form', () => { - it('places correctly initial values', () => { - const date = new Date(); - const newConfig = { ...reportConfigProps, fromLogDate: date, toLogDate: date }; - const root = render(, { wrapper: TestLocalizationProvider }); - const fromInput = root.getByLabelText('From'); - expect(fromInput.getAttribute('data-unix')).toBe(date.valueOf().toString()); - const toInput = root.getByLabelText('To'); - expect(toInput.getAttribute('data-unix')).toBe(date.valueOf().toString()); - }); -}); diff --git a/packages/react-components/lib/reports/default-dates-form.tsx b/packages/react-components/lib/reports/default-dates-form.tsx deleted file mode 100644 index 31661bb66..000000000 --- a/packages/react-components/lib/reports/default-dates-form.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import DateTimePicker from '@mui/lab/DateTimePicker'; -import { styled, TextField } from '@mui/material'; -import Button from '@mui/material/Button'; -import React from 'react'; -import { LogQueryPayload } from '.'; - -interface DefaultDatesFormProps { - search?: (payload: LogQueryPayload) => void; - fromLogDate?: Date; - toLogDate?: Date; - onSelectFromDate?: (date: unknown) => void; - onSelectToDate?: (date: unknown) => void; -} - -const classes = { - searchForm: 'traj-time-control-root', - searchButton: 'traj-time-control-container', -}; -const StyledDiv = styled('div')(() => ({ - [`& .${classes.searchForm}`]: { - display: 'flex', - justifyContent: 'space-evenly', - }, - [`& .${classes.searchButton}`]: { - width: '100%', - }, -})); - -export const DefaultDatesForm = (props: DefaultDatesFormProps): JSX.Element | null => { - const { search, fromLogDate, toLogDate, onSelectToDate, onSelectFromDate } = props; - const searchQuery = () => { - search && search({ toLogDate, fromLogDate }); - }; - - return onSelectFromDate && onSelectToDate ? ( - -
- ( - - )} - /> - ( - - )} - /> -
- -

- -
- ) : null; -}; diff --git a/packages/react-components/lib/reports/default-report-interface.tsx b/packages/react-components/lib/reports/default-report-interface.tsx deleted file mode 100644 index 1f0395004..000000000 --- a/packages/react-components/lib/reports/default-report-interface.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { styled } from '@mui/material'; - -export const defaultReportClasses = { - table: 'default-report-table', -}; -export const DefaultReportContainer = styled('div')(() => ({ - [`& .${defaultReportClasses.table}`]: { - overflowY: 'scroll', - paddingTop: '20px', - }, -})); - -export interface DefaultReportQueryPayload { - toLogDate?: unknown | null; - fromLogDate?: unknown | null; - offset?: number | null; - limit?: number | null; -} - -export interface DefaultLogTableProps { - tableSize?: number; - addMoreRows?(): void; -} diff --git a/packages/react-components/lib/reports/dispenser-state/dispenser-state-report-table.spec.tsx b/packages/react-components/lib/reports/dispenser-state/dispenser-state-report-table.spec.tsx deleted file mode 100644 index fb022b112..000000000 --- a/packages/react-components/lib/reports/dispenser-state/dispenser-state-report-table.spec.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import { cleanup, render, RenderResult, screen } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import { format } from 'date-fns'; -import React from 'react'; -import { getDispenserLogs } from '../utils.spec'; -import { DispenserStateReportTable } from './dispenser-state-report-table'; - -const timestamp = new Date('Mon Jan 1 00:00:02 UTC 2001'); - -describe('Dispenser table test', () => { - let root: RenderResult; - let mockAddMoreRows: ReturnType; - - beforeEach(() => { - mockAddMoreRows = jasmine.createSpy(); - root = render( - , - ); - }); - - afterEach(cleanup); - - it('formats dates correctly', async () => { - const tableFirstDateElement = (await root.getAllByTestId('dispenser-table-date'))[0]; - expect(tableFirstDateElement.innerHTML).toBe(format(timestamp, 'MMM dd yyyy hh:mm aaa')); - }); - - it('shows the correct number of rows', () => { - const allRows = root.container.querySelectorAll('.MuiDataGrid-row').length; - expect(allRows).toBe(100); - }); - - it('shows titles correctly', () => { - expect(screen.queryByText('Guid')).toBeTruthy(); - expect(screen.queryByText('State')).toBeTruthy(); - expect(screen.queryByText('Timestamp')).toBeTruthy(); - }); - - it('executes the addMoreRows', () => { - const nextPageButton = screen.queryByLabelText('Go to next page'); - nextPageButton && userEvent.click(nextPageButton); - expect(mockAddMoreRows).toHaveBeenCalled(); - }); -}); diff --git a/packages/react-components/lib/reports/dispenser-state/dispenser-state-report-table.tsx b/packages/react-components/lib/reports/dispenser-state/dispenser-state-report-table.tsx deleted file mode 100644 index b81d59926..000000000 --- a/packages/react-components/lib/reports/dispenser-state/dispenser-state-report-table.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import React from 'react'; -import { DataGrid, GridRenderCellParams } from '@mui/x-data-grid'; -import { Typography } from '@mui/material'; -import { DefaultLogTableProps } from '../default-report-interface'; -import { format } from 'date-fns'; - -export type DispenserStateRowsType = { - created: string; //date - guid: string; - state: string; -}[]; - -export interface DispenserStateReportTable extends DefaultLogTableProps { - rows: DispenserStateRowsType | []; -} - -export const DispenserStateReportTable = (props: DispenserStateReportTable): React.ReactElement => { - const { rows, addMoreRows } = props; - - return ( -
- r.guid} - columns={[ - { - headerName: 'Guid', - field: 'guid', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.guid}; - }, - }, - { - headerName: 'State', - field: 'state', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.state}; - }, - }, - { - headerName: 'Timestamp', - field: 'created', - type: 'datetime', - filterable: false, - align: 'center', - renderCell: (rowData: GridRenderCellParams) => { - return ( - - {format(new Date(rowData.value as number), 'MMM dd yyyy hh:mm aaa')} - - ); - }, - }, - ]} - rows={rows} - pageSize={100} - rowsPerPageOptions={[50, 100]} - onPageChange={() => { - if (addMoreRows) { - addMoreRows(); - } - }} - disableColumnMenu={true} - /> -
- ); -}; diff --git a/packages/react-components/lib/reports/dispenser-state/dispenser-state-report.spec.tsx b/packages/react-components/lib/reports/dispenser-state/dispenser-state-report.spec.tsx deleted file mode 100644 index 695f346af..000000000 --- a/packages/react-components/lib/reports/dispenser-state/dispenser-state-report.spec.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { render, screen, waitFor } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import React from 'react'; -import { getDispenserLogs, reportConfigProps } from '../utils.spec'; -import { DispenserStateReport } from './dispenser-state-report'; -import { TestLocalizationProvider } from '../../test/locale'; - -const getLogsPromise = async () => getDispenserLogs(); - -it('smoke test', async () => { - await waitFor(() => { - render(); - }); -}); - -it('doesn`t shows the table when logs list is empty', async () => { - await waitFor(() => { - render( []} />); - }); - - expect(screen.queryByText('Dispenser State')).toBeFalsy(); -}); - -it('calls the retrieve log function when the button is clicked', () => { - const getLogsPromiseMock = jasmine.createSpy(); - const getLogsPromise = async () => { - getLogsPromiseMock(); - return getDispenserLogs(); - }; - render(, { - wrapper: TestLocalizationProvider, - }); - expect(screen.getByRole('button', { name: /Retrieve Logs/i })).toBeTruthy(); - userEvent.click(screen.getByRole('button', { name: /Retrieve Logs/i })); - expect(getLogsPromiseMock).toHaveBeenCalled(); -}); diff --git a/packages/react-components/lib/reports/dispenser-state/dispenser-state-report.tsx b/packages/react-components/lib/reports/dispenser-state/dispenser-state-report.tsx deleted file mode 100644 index 28af636c3..000000000 --- a/packages/react-components/lib/reports/dispenser-state/dispenser-state-report.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import React from 'react'; -import { - DefaultReportQueryPayload, - defaultReportClasses, - DefaultReportContainer, -} from '../default-report-interface'; -import { DefaultDatesForm } from '../default-dates-form'; -import { DispenserStateReportTable, DispenserStateRowsType } from './dispenser-state-report-table'; -import { ReportConfigProps } from '../utils'; - -export interface DispenserStateReportProps extends ReportConfigProps { - getLogs: (data: DefaultReportQueryPayload) => Promise; -} - -export const DispenserStateReport = (props: DispenserStateReportProps): React.ReactElement => { - const { getLogs, ...otherProps } = props; - const [logs, setLogs] = React.useState([]); - const [lastSearchParams, setLastSearchParams] = React.useState({}); - - const searchLogs = async (payload: DefaultReportQueryPayload) => { - setLastSearchParams(payload); - setLogs(await getLogs(payload)); - }; - - const getMoreLogs = async () => { - setLogs(logs.concat(await getLogs({ ...lastSearchParams, offset: logs.length }))); - }; - - return ( - - -
- {logs.length !== 0 && ( - - )} -
-
- ); -}; - -export default DispenserStateReport; diff --git a/packages/react-components/lib/reports/dispenser-state/index.ts b/packages/react-components/lib/reports/dispenser-state/index.ts deleted file mode 100644 index f3522dc5f..000000000 --- a/packages/react-components/lib/reports/dispenser-state/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './dispenser-state-report'; -export * from './dispenser-state-report-table'; diff --git a/packages/react-components/lib/reports/door-state/door-state-report-table.spec.tsx b/packages/react-components/lib/reports/door-state/door-state-report-table.spec.tsx deleted file mode 100644 index ea0dafc60..000000000 --- a/packages/react-components/lib/reports/door-state/door-state-report-table.spec.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import { cleanup, render, RenderResult, screen } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import { format } from 'date-fns'; -import React from 'react'; -import { getDoorLogs } from '../utils.spec'; -import { DoorStateReportTable } from './door-state-report-table'; - -const timestamp = new Date('Mon Jan 1 00:00:02 UTC 2001'); - -describe('Door table test', () => { - let root: RenderResult; - let mockAddMoreRows: ReturnType; - - beforeEach(() => { - mockAddMoreRows = jasmine.createSpy(); - root = render(); - }); - - afterEach(cleanup); - - it('formats dates correctly', async () => { - const tableFirstDateElement = (await root.getAllByTestId('door-table-date'))[0]; - expect(tableFirstDateElement.innerHTML).toBe(format(timestamp, 'MMM dd yyyy hh:mm aaa')); - }); - - it('shows the correct number of rows', () => { - const allRows = root.container.querySelectorAll('.MuiDataGrid-row').length; - expect(allRows).toBe(100); - }); - - it('shows titles correctly', () => { - expect(screen.queryByText('Name')).toBeTruthy(); - expect(screen.queryByText('State')).toBeTruthy(); - expect(screen.queryByText('Timestamp')).toBeTruthy(); - }); - - it('executes the addMoreRows', () => { - const nextPageButton = screen.queryByTitle('Go to next page'); - nextPageButton && userEvent.click(nextPageButton); - expect(mockAddMoreRows).toHaveBeenCalled(); - }); -}); diff --git a/packages/react-components/lib/reports/door-state/door-state-report-table.tsx b/packages/react-components/lib/reports/door-state/door-state-report-table.tsx deleted file mode 100644 index efc677813..000000000 --- a/packages/react-components/lib/reports/door-state/door-state-report-table.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import React from 'react'; -import { DataGrid, GridRenderCellParams } from '@mui/x-data-grid'; -import { Typography } from '@mui/material'; -import { DefaultLogTableProps } from '../default-report-interface'; -import { format } from 'date-fns'; - -export type DoorStateRowsType = { - created: string; //date - door: { id: number; name: string }; - state: string; -}[]; - -export interface DoorStateReportTable extends DefaultLogTableProps { - rows: DoorStateRowsType | []; -} - -export const DoorStateReportTable = (props: DoorStateReportTable): React.ReactElement => { - const { rows, addMoreRows } = props; - return ( -
- r.door.id} - columns={[ - { - headerName: 'Name', - field: 'name', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.door.name}; - }, - }, - { - headerName: 'State', - field: 'state', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.state}; - }, - }, - { - headerName: 'Timestamp', - field: 'created', - type: 'datetime', - filterable: false, - align: 'center', - renderCell: (rowData: GridRenderCellParams) => { - return ( - - {format(new Date(rowData.value as number), 'MMM dd yyyy hh:mm aaa')} - - ); - }, - }, - ]} - rows={rows} - pageSize={100} - rowsPerPageOptions={[50, 100]} - onPageChange={() => { - if (addMoreRows) { - addMoreRows(); - } - }} - disableColumnMenu={true} - /> -
- ); -}; diff --git a/packages/react-components/lib/reports/door-state/door-state-report.spec.tsx b/packages/react-components/lib/reports/door-state/door-state-report.spec.tsx deleted file mode 100644 index f3b63cbda..000000000 --- a/packages/react-components/lib/reports/door-state/door-state-report.spec.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { render, screen, waitFor } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import React from 'react'; -import { TestLocalizationProvider } from '../../test/locale'; -import { getDoorLogs, reportConfigProps } from '../utils.spec'; -import { DoorStateReport } from './door-state-report'; - -const getLogsPromise = async () => getDoorLogs(); - -it('smoke test', async () => { - await waitFor(() => { - render(); - }); -}); - -it('doesn`t shows the table when logs list is empty', async () => { - await waitFor(() => { - render( []} />); - }); - - expect(screen.queryByText('Door State')).toBeFalsy(); -}); - -it('calls the retrieve log function when the button is clicked', () => { - const getLogsPromiseMock = jasmine.createSpy(); - const getLogsPromise = async () => { - getLogsPromiseMock(); - return getDoorLogs(); - }; - render(, { - wrapper: TestLocalizationProvider, - }); - expect(screen.getByRole('button', { name: /Retrieve Logs/i })).toBeTruthy(); - userEvent.click(screen.getByRole('button', { name: /Retrieve Logs/i })); - expect(getLogsPromiseMock).toHaveBeenCalled(); -}); diff --git a/packages/react-components/lib/reports/door-state/door-state-report.tsx b/packages/react-components/lib/reports/door-state/door-state-report.tsx deleted file mode 100644 index 7c7d837e0..000000000 --- a/packages/react-components/lib/reports/door-state/door-state-report.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import React from 'react'; -import { - DefaultReportQueryPayload, - defaultReportClasses, - DefaultReportContainer, -} from '../default-report-interface'; -import { DefaultDatesForm } from '../default-dates-form'; -import { DoorStateReportTable, DoorStateRowsType } from './door-state-report-table'; -import { ReportConfigProps } from '../utils'; - -export interface DoorStateReportProps extends ReportConfigProps { - getLogs: (data: DefaultReportQueryPayload) => Promise; -} - -export const DoorStateReport = (props: DoorStateReportProps): React.ReactElement => { - const { getLogs, ...otherProps } = props; - const [logs, setLogs] = React.useState([]); - const [lastSearchParams, setLastSearchParams] = React.useState({}); - - const searchLogs = async (payload: DefaultReportQueryPayload) => { - setLastSearchParams(payload); - setLogs(await getLogs(payload)); - }; - - const getMoreLogs = async () => { - setLogs(logs.concat(await getLogs({ ...lastSearchParams, offset: logs.length }))); - }; - - return ( - - -
- {logs.length !== 0 && ( - - )} -
-
- ); -}; - -export default DoorStateReport; diff --git a/packages/react-components/lib/reports/door-state/index.ts b/packages/react-components/lib/reports/door-state/index.ts deleted file mode 100644 index 7b4b3a528..000000000 --- a/packages/react-components/lib/reports/door-state/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './door-state-report'; -export * from './door-state-report-table'; diff --git a/packages/react-components/lib/reports/fleet-state/fleet-state-report-table.spec.tsx b/packages/react-components/lib/reports/fleet-state/fleet-state-report-table.spec.tsx deleted file mode 100644 index cf7256061..000000000 --- a/packages/react-components/lib/reports/fleet-state/fleet-state-report-table.spec.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import { cleanup, render, RenderResult, screen } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import { format } from 'date-fns'; -import React from 'react'; -import { getFleetLogs } from '../utils.spec'; -import { FleetStateReportTable } from './fleet-state-report-table'; - -const timestamp = new Date('Mon Jan 1 00:00:02 UTC 2001'); - -describe('Fleet table test', () => { - let root: RenderResult; - let mockAddMoreRows: ReturnType; - - beforeEach(() => { - mockAddMoreRows = jasmine.createSpy(); - root = render(); - }); - - afterEach(cleanup); - - it('formats dates correctly', async () => { - const tableFirstDateElement = (await root.getAllByTestId('fleet-table-date'))[0]; - expect(tableFirstDateElement.innerHTML).toBe(format(timestamp, 'MMM dd yyyy hh:mm aaa')); - }); - - it('shows the correct number of rows', () => { - const allRows = root.container.querySelectorAll('.MuiDataGrid-row').length; - expect(allRows).toBe(100); - }); - - it('shows titles correctly', () => { - expect(screen.queryByText('Fleet')).toBeTruthy(); - expect(screen.queryByText('Robot')).toBeTruthy(); - expect(screen.queryByText('Battery')).toBeTruthy(); - expect(screen.queryByText('Mode')).toBeTruthy(); - expect(screen.queryByText('Model')).toBeTruthy(); - expect(screen.queryByText('TaskID')).toBeTruthy(); - expect(screen.queryByText('Timestamp')).toBeTruthy(); - }); - - it('executes the addMoreRows', () => { - const nextPageButton = screen.queryByTitle('Go to next page'); - nextPageButton && userEvent.click(nextPageButton); - expect(mockAddMoreRows).toHaveBeenCalled(); - }); -}); diff --git a/packages/react-components/lib/reports/fleet-state/fleet-state-report-table.tsx b/packages/react-components/lib/reports/fleet-state/fleet-state-report-table.tsx deleted file mode 100644 index c19a332b2..000000000 --- a/packages/react-components/lib/reports/fleet-state/fleet-state-report-table.tsx +++ /dev/null @@ -1,106 +0,0 @@ -import React from 'react'; -import { DataGrid, GridRenderCellParams } from '@mui/x-data-grid'; -import { Typography } from '@mui/material'; -import { DefaultLogTableProps } from '../default-report-interface'; -import { format } from 'date-fns'; - -export type FleetStateRowsType = { - created: string; //date - fleet: { id: number; name: string }; - robot: { id: number; name: string; model?: string }; - robot_battery_percent: string; - robot_location: string; - robot_mode: string; - robot_seq: number; - robot_task_id: string; -}[]; - -export interface FleetStateReportTable extends DefaultLogTableProps { - rows: FleetStateRowsType | []; -} - -export const FleetStateReportTable = (props: FleetStateReportTable): React.ReactElement => { - const { rows, addMoreRows } = props; - - return ( -
- r.robot.id} - columns={[ - { - headerName: 'Fleet', - field: 'fleet_name', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.fleet.name}; - }, - }, - { - headerName: 'Robot', - field: 'robot_name', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.robot.name}; - }, - }, - { - headerName: 'Battery', - field: 'robot_battery_percent', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.robot_battery_percent}; - }, - }, - { - headerName: 'Mode', - field: 'robot_mode', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.robot_mode}; - }, - }, - { - headerName: 'Model', - field: 'robot_model', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.robot.model}; - }, - }, - { - headerName: 'TaskID', - field: 'robot_task_id', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.robot_task_id}; - }, - }, - { - headerName: 'Timestamp', - field: 'created', - type: 'datetime', - filterable: false, - align: 'center', - renderCell: (rowData: GridRenderCellParams) => { - return ( - - {format(new Date(rowData.value as number), 'MMM dd yyyy hh:mm aaa')} - - ); - }, - }, - ]} - rows={rows} - pageSize={100} - rowsPerPageOptions={[50, 100]} - onPageChange={() => { - if (addMoreRows) { - addMoreRows(); - } - }} - disableColumnMenu={true} - /> -
- ); -}; diff --git a/packages/react-components/lib/reports/fleet-state/fleet-state-report.spec.tsx b/packages/react-components/lib/reports/fleet-state/fleet-state-report.spec.tsx deleted file mode 100644 index 7acdd96c6..000000000 --- a/packages/react-components/lib/reports/fleet-state/fleet-state-report.spec.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { render, screen, waitFor } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import React from 'react'; -import { TestLocalizationProvider } from '../../test/locale'; -import { getFleetLogs, reportConfigProps } from '../utils.spec'; -import { FleetStateReport } from './fleet-state-report'; - -const getLogsPromise = async () => getFleetLogs(); - -it('smoke test', async () => { - await waitFor(() => { - render(); - }); -}); - -it('doesn`t shows the table when logs list is empty', async () => { - await waitFor(() => { - render( []} />); - }); - - expect(screen.queryByText('Fleet State')).toBeFalsy(); -}); - -it('calls the retrieve log function when the button is clicked', async () => { - const getLogsPromiseMock = jasmine.createSpy(); - const getLogsPromise = async () => { - getLogsPromiseMock(); - return getFleetLogs(); - }; - render(, { - wrapper: TestLocalizationProvider, - }); - expect(screen.getByRole('button', { name: /Retrieve Logs/i })).toBeTruthy(); - userEvent.click(screen.getByRole('button', { name: /Retrieve Logs/i })); - expect(getLogsPromiseMock).toHaveBeenCalled(); -}); diff --git a/packages/react-components/lib/reports/fleet-state/fleet-state-report.tsx b/packages/react-components/lib/reports/fleet-state/fleet-state-report.tsx deleted file mode 100644 index ebdbbc503..000000000 --- a/packages/react-components/lib/reports/fleet-state/fleet-state-report.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import React from 'react'; -import { - DefaultReportQueryPayload, - DefaultReportContainer, - defaultReportClasses, -} from '../default-report-interface'; -import { DefaultDatesForm } from '../default-dates-form'; -import { FleetStateReportTable, FleetStateRowsType } from './fleet-state-report-table'; -import { ReportConfigProps } from '../utils'; - -export interface FleetStateReportProps extends ReportConfigProps { - getLogs: (data: DefaultReportQueryPayload) => Promise; -} - -export const FleetStateReport = (props: FleetStateReportProps): React.ReactElement => { - const { getLogs, ...otherProps } = props; - const [logs, setLogs] = React.useState([]); - const [lastSearchParams, setLastSearchParams] = React.useState({}); - - const searchLogs = async (payload: DefaultReportQueryPayload) => { - setLastSearchParams(payload); - setLogs(await getLogs(payload)); - }; - - const getMoreLogs = async () => { - setLogs(logs.concat(await getLogs({ ...lastSearchParams, offset: logs.length }))); - }; - - return ( - - -
- {logs.length !== 0 && ( - - )} -
-
- ); -}; - -export default FleetStateReport; diff --git a/packages/react-components/lib/reports/fleet-state/index.ts b/packages/react-components/lib/reports/fleet-state/index.ts deleted file mode 100644 index 02939fe93..000000000 --- a/packages/react-components/lib/reports/fleet-state/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './fleet-state-report'; -export * from './fleet-state-report-table'; diff --git a/packages/react-components/lib/reports/health/health-report-table.spec.tsx b/packages/react-components/lib/reports/health/health-report-table.spec.tsx deleted file mode 100644 index e429a4606..000000000 --- a/packages/react-components/lib/reports/health/health-report-table.spec.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import { cleanup, render, RenderResult, screen } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import { format } from 'date-fns'; -import React from 'react'; -import { getHealthLogs } from '../utils.spec'; -import { HealthReportTable } from './health-report-table'; - -const timestamp = new Date('Mon Jan 1 00:00:02 UTC 2001'); - -describe('Health table test', () => { - let root: RenderResult; - let mockAddMoreRows: ReturnType; - - beforeEach(() => { - mockAddMoreRows = jasmine.createSpy(); - root = render(); - }); - - afterEach(cleanup); - - it('formats dates correctly', async () => { - const tableFirstDateElement = (await root.getAllByTestId('health-table-date'))[0]; - expect(tableFirstDateElement.innerHTML).toBe(format(timestamp, 'MMM dd yyyy hh:mm aaa')); - }); - - it('shows the correct number of rows', () => { - const allRows = root.container.querySelectorAll('.MuiDataGrid-row').length; - expect(allRows).toBe(100); - }); - - it('shows titles correctly', () => { - expect(screen.queryByText('Device')).toBeTruthy(); - expect(screen.queryByText('Actor')).toBeTruthy(); - expect(screen.queryByText('Health Status')).toBeTruthy(); - expect(screen.queryByText('Health Message')).toBeTruthy(); - expect(screen.queryByText('Timestamp')).toBeTruthy(); - }); - - it('executes the addMoreRows', () => { - const nextPageButton = screen.queryByTitle('Go to next page'); - nextPageButton && userEvent.click(nextPageButton); - expect(mockAddMoreRows).toHaveBeenCalled(); - }); -}); diff --git a/packages/react-components/lib/reports/health/health-report-table.tsx b/packages/react-components/lib/reports/health/health-report-table.tsx deleted file mode 100644 index a44ec10fe..000000000 --- a/packages/react-components/lib/reports/health/health-report-table.tsx +++ /dev/null @@ -1,86 +0,0 @@ -import React from 'react'; -import { DataGrid, GridRenderCellParams } from '@mui/x-data-grid'; -import { Typography } from '@mui/material'; -import { DefaultLogTableProps } from '../default-report-interface'; -import { format } from 'date-fns'; - -export type HealthRowsType = { - created: string; //date - device: { id?: number; type: string; actor: string }; - health_status: string; - health_message: string; -}[]; - -export interface HealthReportTable extends DefaultLogTableProps { - rows: HealthRowsType | []; -} - -export const HealthReportTable = (props: HealthReportTable): React.ReactElement => { - const { rows, addMoreRows } = props; - - return ( -
- r.device.id} - columns={[ - { - headerName: 'Device', - field: 'device', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.device.type}; - }, - }, - { - headerName: 'Actor', - field: 'actor_id', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.device.actor}; - }, - }, - { - headerName: 'Health Status', - field: 'health_status', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.health_status}; - }, - }, - { - headerName: 'Health Message', - field: 'health_message', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.health_message}; - }, - }, - { - headerName: 'Timestamp', - field: 'created', - type: 'datetime', - filterable: false, - align: 'center', - renderCell: (rowData: GridRenderCellParams) => { - return ( - - {format(new Date(rowData.value as number), 'MMM dd yyyy hh:mm aaa')} - - ); - }, - }, - ]} - rows={rows} - pageSize={100} - rowsPerPageOptions={[50, 100]} - onPageChange={() => { - if (addMoreRows) { - addMoreRows(); - } - }} - disableColumnMenu={true} - /> -
- ); -}; diff --git a/packages/react-components/lib/reports/health/health-report.spec.tsx b/packages/react-components/lib/reports/health/health-report.spec.tsx deleted file mode 100644 index 8d9ac27c2..000000000 --- a/packages/react-components/lib/reports/health/health-report.spec.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { render, screen, waitFor } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import React from 'react'; -import { TestLocalizationProvider } from '../../test/locale'; -import { getHealthLogs, reportConfigProps } from '../utils.spec'; -import { HealthReport } from './health-report'; - -const getLogsPromise = async () => getHealthLogs(); - -it('smoke test', async () => { - await waitFor(() => { - render(); - }); -}); - -it('doesn`t shows the table when logs list is empty', async () => { - await waitFor(() => { - render( await []} />); - }); - - expect(screen.queryByText('Health')).toBeFalsy(); -}); - -it('calls the retrieve log function when the button is clicked', async () => { - const getLogsPromiseMock = jasmine.createSpy(); - const getLogsPromise = async () => { - getLogsPromiseMock(); - return getHealthLogs(); - }; - render(, { - wrapper: TestLocalizationProvider, - }); - expect(screen.getByRole('button', { name: /Retrieve Logs/i })).toBeTruthy(); - userEvent.click(screen.getByRole('button', { name: /Retrieve Logs/i })); - expect(getLogsPromiseMock).toHaveBeenCalled(); -}); diff --git a/packages/react-components/lib/reports/health/health-report.tsx b/packages/react-components/lib/reports/health/health-report.tsx deleted file mode 100644 index 3ea2142f0..000000000 --- a/packages/react-components/lib/reports/health/health-report.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import React from 'react'; -import { - DefaultReportQueryPayload, - defaultReportClasses, - DefaultReportContainer, -} from '../default-report-interface'; -import { DefaultDatesForm } from '../default-dates-form'; -import { HealthReportTable, HealthRowsType } from './health-report-table'; -import { ReportConfigProps } from '../utils'; - -export interface HealthReportProps extends ReportConfigProps { - getLogs: (data: DefaultReportQueryPayload) => Promise; -} - -export const HealthReport = (props: HealthReportProps): React.ReactElement => { - const { getLogs, ...otherProps } = props; - const [logs, setLogs] = React.useState([]); - const [lastSearchParams, setLastSearchParams] = React.useState({}); - - const searchLogs = async (payload: DefaultReportQueryPayload) => { - setLastSearchParams(payload); - setLogs(await getLogs(payload)); - }; - - const getMoreLogs = async () => { - setLogs(logs.concat(await getLogs({ ...lastSearchParams, offset: logs.length }))); - }; - - return ( - - -
- {logs.length !== 0 && ( - - )} -
-
- ); -}; - -export default HealthReport; diff --git a/packages/react-components/lib/reports/health/index.ts b/packages/react-components/lib/reports/health/index.ts deleted file mode 100644 index bfbe0ebbe..000000000 --- a/packages/react-components/lib/reports/health/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './health-report'; -export * from './health-report-table'; diff --git a/packages/react-components/lib/reports/index.ts b/packages/react-components/lib/reports/index.ts deleted file mode 100644 index 752b658bd..000000000 --- a/packages/react-components/lib/reports/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -export * from './log-management'; -export * from './multi-level-menu'; -export * from './default-dates-form'; -export * from './default-report-interface'; -export * from './dispenser-state'; -export * from './door-state'; -export * from './fleet-state'; -export * from './health'; -export * from './ingestor-state'; -export * from './lift-state'; -export * from './user-report'; -export * from './task-summary'; -export * from './utils'; diff --git a/packages/react-components/lib/reports/ingestor-state/index.ts b/packages/react-components/lib/reports/ingestor-state/index.ts deleted file mode 100644 index a899427b2..000000000 --- a/packages/react-components/lib/reports/ingestor-state/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './ingestor-state-report'; -export * from './ingestor-state-report-table'; diff --git a/packages/react-components/lib/reports/ingestor-state/ingestor-state-report-table.spec.tsx b/packages/react-components/lib/reports/ingestor-state/ingestor-state-report-table.spec.tsx deleted file mode 100644 index b9d5cc5fd..000000000 --- a/packages/react-components/lib/reports/ingestor-state/ingestor-state-report-table.spec.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import { cleanup, render, RenderResult, screen } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import { format } from 'date-fns'; -import React from 'react'; -import { getIngestorLogs } from '../utils.spec'; -import { IngestorStateReportTable } from './ingestor-state-report-table'; - -const timestamp = new Date('Mon Jan 1 00:00:02 UTC 2001'); - -describe('Ingestor table test', () => { - let root: RenderResult; - let mockAddMoreRows: ReturnType; - - beforeEach(() => { - mockAddMoreRows = jasmine.createSpy(); - root = render( - , - ); - }); - - afterEach(cleanup); - - it('formats dates correctly', async () => { - const tableFirstDateElement = (await root.getAllByTestId('ingestor-table-date'))[0]; - expect(tableFirstDateElement.innerHTML).toBe(format(timestamp, 'MMM dd yyyy hh:mm aaa')); - }); - - it('shows the correct number of rows', () => { - const allRows = root.container.querySelectorAll('.MuiDataGrid-row').length; - expect(allRows).toBe(100); - }); - - it('shows titles correctly', () => { - expect(screen.queryByText('Guid')).toBeTruthy(); - expect(screen.queryByText('State')).toBeTruthy(); - expect(screen.queryByText('Timestamp')).toBeTruthy(); - }); - - it('executes the addMoreRows', () => { - const nextPageButton = screen.queryByTitle('Go to next page'); - nextPageButton && userEvent.click(nextPageButton); - expect(mockAddMoreRows).toHaveBeenCalled(); - }); -}); diff --git a/packages/react-components/lib/reports/ingestor-state/ingestor-state-report-table.tsx b/packages/react-components/lib/reports/ingestor-state/ingestor-state-report-table.tsx deleted file mode 100644 index 565b40582..000000000 --- a/packages/react-components/lib/reports/ingestor-state/ingestor-state-report-table.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import React from 'react'; -import { DataGrid, GridRenderCellParams } from '@mui/x-data-grid'; -import { Typography } from '@mui/material'; -import { DefaultLogTableProps } from '../default-report-interface'; -import { format } from 'date-fns'; - -export type IngestorStateRowsType = { - created: string; //date - guid: string; - state: string; -}[]; - -export interface IngestorStateReportTable extends DefaultLogTableProps { - rows: IngestorStateRowsType | []; -} - -export const IngestorStateReportTable = (props: IngestorStateReportTable): React.ReactElement => { - const { rows, addMoreRows } = props; - - return ( -
- r.guid} - columns={[ - { - headerName: 'Guid', - field: 'guid', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.guid}; - }, - }, - { - headerName: 'State', - field: 'state', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.state}; - }, - }, - { - headerName: 'Timestamp', - field: 'created', - type: 'datetime', - filterable: false, - align: 'center', - renderCell: (rowData: GridRenderCellParams) => { - return ( - - {format(new Date(rowData.value as number), 'MMM dd yyyy hh:mm aaa')} - - ); - }, - }, - ]} - rows={rows} - pageSize={100} - rowsPerPageOptions={[50, 100]} - onPageChange={() => { - if (addMoreRows) { - addMoreRows(); - } - }} - disableColumnMenu={true} - /> -
- ); -}; diff --git a/packages/react-components/lib/reports/ingestor-state/ingestor-state-report.spec.tsx b/packages/react-components/lib/reports/ingestor-state/ingestor-state-report.spec.tsx deleted file mode 100644 index f46ea6926..000000000 --- a/packages/react-components/lib/reports/ingestor-state/ingestor-state-report.spec.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { render, screen, waitFor } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import React from 'react'; -import { TestLocalizationProvider } from '../../test/locale'; -import { getIngestorLogs, reportConfigProps } from '../utils.spec'; -import { IngestorStateReport } from './ingestor-state-report'; - -const getLogsPromise = async () => getIngestorLogs(); - -it('smoke test', async () => { - await waitFor(() => { - render(); - }); -}); - -it('doesn`t shows the table when logs list is empty', async () => { - await waitFor(() => { - render( []} />); - }); - - expect(screen.queryByText('Ingestor State')).toBeFalsy(); -}); - -it('calls the retrieve log function when the button is clicked', async () => { - const getLogsPromiseMock = jasmine.createSpy(); - const getLogsPromise = async () => { - getLogsPromiseMock(); - return getIngestorLogs(); - }; - render(, { - wrapper: TestLocalizationProvider, - }); - expect(screen.getByRole('button', { name: /Retrieve Logs/i })).toBeTruthy(); - userEvent.click(screen.getByRole('button', { name: /Retrieve Logs/i })); - expect(getLogsPromiseMock).toHaveBeenCalled(); -}); diff --git a/packages/react-components/lib/reports/ingestor-state/ingestor-state-report.tsx b/packages/react-components/lib/reports/ingestor-state/ingestor-state-report.tsx deleted file mode 100644 index a85c0192e..000000000 --- a/packages/react-components/lib/reports/ingestor-state/ingestor-state-report.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import React from 'react'; -import { - DefaultReportQueryPayload, - defaultReportClasses, - DefaultReportContainer, -} from '../default-report-interface'; -import { DefaultDatesForm } from '../default-dates-form'; -import { IngestorStateReportTable, IngestorStateRowsType } from './ingestor-state-report-table'; -import { ReportConfigProps } from '../utils'; - -export interface IngestorStateReportProps extends ReportConfigProps { - getLogs: (data: DefaultReportQueryPayload) => Promise; -} - -export const IngestorStateReport = (props: IngestorStateReportProps): React.ReactElement => { - const { getLogs, ...otherProps } = props; - const [logs, setLogs] = React.useState([]); - const [lastSearchParams, setLastSearchParams] = React.useState({}); - - const searchLogs = async (payload: DefaultReportQueryPayload) => { - setLastSearchParams(payload); - setLogs(await getLogs(payload)); - }; - - const getMoreLogs = async () => { - setLogs(logs.concat(await getLogs({ ...lastSearchParams, offset: logs.length }))); - }; - - return ( - - -
- {logs.length !== 0 && ( - - )} -
-
- ); -}; - -export default IngestorStateReport; diff --git a/packages/react-components/lib/reports/lift-state/index.ts b/packages/react-components/lib/reports/lift-state/index.ts deleted file mode 100644 index aea8be4ac..000000000 --- a/packages/react-components/lib/reports/lift-state/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './lift-state-report'; -export * from './lift-state-report-table'; diff --git a/packages/react-components/lib/reports/lift-state/lift-state-report-table.spec.tsx b/packages/react-components/lib/reports/lift-state/lift-state-report-table.spec.tsx deleted file mode 100644 index b3d0e7667..000000000 --- a/packages/react-components/lib/reports/lift-state/lift-state-report-table.spec.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import { cleanup, render, RenderResult, screen } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import { format } from 'date-fns'; -import React from 'react'; -import { getLiftLogs } from '../utils.spec'; -import { LiftStateReportTable } from './lift-state-report-table'; - -const timestamp = new Date('Mon Jan 1 00:00:02 UTC 2001'); - -describe('Lift table test', () => { - let mockAddMoreRows: ReturnType; - let root: RenderResult; - - beforeEach(() => { - mockAddMoreRows = jasmine.createSpy(); - root = render(); - }); - - afterEach(cleanup); - - it('formats dates correctly', async () => { - const tableFirstDateElement = (await root.getAllByTestId('lift-table-date'))[0]; - expect(tableFirstDateElement.innerHTML).toBe(format(timestamp, 'MMM dd yyyy hh:mm aaa')); - }); - - it('shows the correct number of rows', () => { - const allRows = root.container.querySelectorAll('.MuiDataGrid-row').length; - // -3. from the tr of the table header, filter and pagination table - expect(allRows).toBe(100); - }); - - it('shows titles correctly', () => { - expect(screen.queryByText('State')).toBeTruthy(); - expect(screen.queryByText('Door State')).toBeTruthy(); - expect(screen.queryByText('Destination Floor')).toBeTruthy(); - expect(screen.queryByText('Motion State')).toBeTruthy(); - expect(screen.queryByText('Current Floor')).toBeTruthy(); - expect(screen.queryByText('Session ID')).toBeTruthy(); - expect(screen.queryByText('Timestamp')).toBeTruthy(); - }); - - it('executes the addMoreRows', () => { - const nextPageButton = screen.queryByTitle('Go to next page'); - nextPageButton && userEvent.click(nextPageButton); - expect(mockAddMoreRows).toHaveBeenCalled(); - }); -}); diff --git a/packages/react-components/lib/reports/lift-state/lift-state-report-table.tsx b/packages/react-components/lib/reports/lift-state/lift-state-report-table.tsx deleted file mode 100644 index 41f6b51f9..000000000 --- a/packages/react-components/lib/reports/lift-state/lift-state-report-table.tsx +++ /dev/null @@ -1,107 +0,0 @@ -import React from 'react'; -import { DataGrid, GridRenderCellParams } from '@mui/x-data-grid'; -import { Typography } from '@mui/material'; -import { DefaultLogTableProps } from '../default-report-interface'; -import { format } from 'date-fns'; - -export type LiftStateRowsType = { - created: string; //date - state: string; - lift?: { id: number; name: string }; - door_state: string; - destination_floor: string; - motion_state: string; - current_floor: string; - session_id: string; -}[]; - -export interface LiftStateReportTable extends DefaultLogTableProps { - rows: LiftStateRowsType | []; -} - -export const LiftStateReportTable = (props: LiftStateReportTable): React.ReactElement => { - const { rows, addMoreRows } = props; - - return ( -
- r.lift.id} - columns={[ - { - headerName: 'Session ID', - field: 'session_id', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.session_id}; - }, - }, - { - headerName: 'State', - field: 'state', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.state}; - }, - }, - { - headerName: 'Door State', - field: 'door_state', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.door_state}; - }, - }, - { - headerName: 'Destination Floor', - field: 'destination_floor', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.destination_floor}; - }, - }, - { - headerName: 'Motion State', - field: 'motion_state', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.motion_state}; - }, - }, - { - headerName: 'Current Floor', - field: 'current_floor', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.current_floor}; - }, - }, - - { - headerName: 'Timestamp', - field: 'created', - type: 'datetime', - filterable: false, - align: 'center', - renderCell: (rowData: GridRenderCellParams) => { - return ( - - {format(new Date(rowData.value as number), 'MMM dd yyyy hh:mm aaa')} - - ); - }, - }, - ]} - rows={rows} - pageSize={100} - rowsPerPageOptions={[50, 100]} - onPageChange={() => { - if (addMoreRows) { - addMoreRows(); - } - }} - disableColumnMenu={true} - /> -
- ); -}; diff --git a/packages/react-components/lib/reports/lift-state/lift-state-report.spec.tsx b/packages/react-components/lib/reports/lift-state/lift-state-report.spec.tsx deleted file mode 100644 index 917e380f1..000000000 --- a/packages/react-components/lib/reports/lift-state/lift-state-report.spec.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { render, screen, waitFor } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import React from 'react'; -import { TestLocalizationProvider } from '../../test/locale'; -import { getLiftLogs, reportConfigProps } from '../utils.spec'; -import { LiftStateReport } from './lift-state-report'; - -const getLogsPromise = async () => getLiftLogs(); - -it('smoke test', async () => { - await waitFor(() => { - render(); - }); -}); - -it('doesn`t shows the table when logs list is empty', async () => { - await waitFor(() => { - render( []} />); - }); - - expect(screen.queryByText('Lift State')).toBeFalsy(); -}); - -it('calls the retrieve log function when the button is clicked', async () => { - const getLogsPromiseMock = jasmine.createSpy(); - const getLogsPromise = async () => { - getLogsPromiseMock(); - return getLiftLogs(); - }; - render(, { - wrapper: TestLocalizationProvider, - }); - expect(screen.getByRole('button', { name: /Retrieve Logs/i })).toBeTruthy(); - userEvent.click(screen.getByRole('button', { name: /Retrieve Logs/i })); - expect(getLogsPromiseMock).toHaveBeenCalled(); -}); diff --git a/packages/react-components/lib/reports/lift-state/lift-state-report.tsx b/packages/react-components/lib/reports/lift-state/lift-state-report.tsx deleted file mode 100644 index b7ae86377..000000000 --- a/packages/react-components/lib/reports/lift-state/lift-state-report.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import React from 'react'; -import { - DefaultReportQueryPayload, - defaultReportClasses, - DefaultReportContainer, -} from '../default-report-interface'; -import { DefaultDatesForm } from '../default-dates-form'; -import { LiftStateReportTable, LiftStateRowsType } from './lift-state-report-table'; -import { ReportConfigProps } from '../utils'; - -export interface LiftStateReportProps extends ReportConfigProps { - getLogs: (data: DefaultReportQueryPayload) => Promise; -} - -export const LiftStateReport = (props: LiftStateReportProps): React.ReactElement => { - const { getLogs, ...otherProps } = props; - const [logs, setLogs] = React.useState([]); - const [lastSearchParams, setLastSearchParams] = React.useState({}); - - const searchLogs = async (payload: DefaultReportQueryPayload) => { - setLastSearchParams(payload); - setLogs(await getLogs(payload)); - }; - - const getMoreLogs = async () => { - setLogs(logs.concat(await getLogs({ ...lastSearchParams, offset: logs.length }))); - }; - - return ( - - -
- {logs.length !== 0 && ( - - )} -
-
- ); -}; - -export default LiftStateReport; diff --git a/packages/react-components/lib/reports/log-management/custom-lookup-filter.spec.tsx b/packages/react-components/lib/reports/log-management/custom-lookup-filter.spec.tsx deleted file mode 100644 index ec79b17f3..000000000 --- a/packages/react-components/lib/reports/log-management/custom-lookup-filter.spec.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import { cleanup, render, screen } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import React from 'react'; -import { CustomLookupFilter } from './custom-lookup-filter'; - -describe('Custom Lookup filter', () => { - let mockOnChange: ReturnType; - beforeEach(() => { - mockOnChange = jasmine.createSpy(); - }); - - afterEach(() => { - cleanup(); - }); - - it('triggers a new search closing the filter selector', () => { - render( - , - ); - - // click on existing selected filter "error" to open up input to expose "warn" - userEvent.click(screen.getByText('error')); - userEvent.click(screen.getByText('warn')); - userEvent.type(screen.getByText('warn'), '{esc}'); - expect(mockOnChange).toHaveBeenCalled(); - }); -}); diff --git a/packages/react-components/lib/reports/log-management/custom-lookup-filter.tsx b/packages/react-components/lib/reports/log-management/custom-lookup-filter.tsx deleted file mode 100644 index c4c8af59b..000000000 --- a/packages/react-components/lib/reports/log-management/custom-lookup-filter.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import { - Checkbox, - FormControl, - Input, - InputLabel, - ListItemText, - MenuItem, - Select, -} from '@mui/material'; -import React from 'react'; - -const ITEM_HEIGHT = 48; -const ITEM_PADDING_TOP = 8; -const MenuProps = { - PaperProps: { - style: { - maxHeight: ITEM_HEIGHT * 7 + ITEM_PADDING_TOP, - width: 250, - }, - }, -}; - -export interface CustomLookupFilterProps { - lookup: Record; - selectedFilter: string[]; - onFilterChange: React.Dispatch>; -} - -export const CustomLookupFilter = (props: CustomLookupFilterProps): React.ReactElement => { - // FIXME - select closes every time a filter param is selected, which is bad for ux - const { lookup, selectedFilter, onFilterChange } = props; - return ( - - Level Filter - } - renderValue={(selecteds) => - (selecteds as string[]).map((selected: string) => lookup[selected]).join(', ') - } - MenuProps={MenuProps} - style={{ marginTop: 0 }} - > - {Object.keys(lookup).map((key: string) => ( - - -1} /> - - - ))} - - - ); -}; diff --git a/packages/react-components/lib/reports/log-management/index.ts b/packages/react-components/lib/reports/log-management/index.ts deleted file mode 100644 index ca7102e01..000000000 --- a/packages/react-components/lib/reports/log-management/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from './log-table'; -export * from './search-log-form'; -export * from './log-level'; -export * from './log-management'; diff --git a/packages/react-components/lib/reports/log-management/log-level.ts b/packages/react-components/lib/reports/log-management/log-level.ts deleted file mode 100644 index e1e7679da..000000000 --- a/packages/react-components/lib/reports/log-management/log-level.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Order by severity - */ - -export enum LogLevel { - Fatal = 'fatal', - Error = 'error', - Warn = 'warn', - Info = 'info', - Debug = 'debug', - Trace = 'trace', - All = 'all', -} diff --git a/packages/react-components/lib/reports/log-management/log-management.spec.tsx b/packages/react-components/lib/reports/log-management/log-management.spec.tsx deleted file mode 100644 index 3f834a572..000000000 --- a/packages/react-components/lib/reports/log-management/log-management.spec.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { render, waitFor } from '@testing-library/react'; -import React from 'react'; -import { LogManagement } from './log-management'; -import { LogRowsType } from './log-table'; -import { TestLocalizationProvider } from '../../test/locale'; - -const getLogLabels = () => [ - { label: 'Web Server', value: 'web-server' }, - { label: 'RMF core', value: 'rmf-core' }, -]; - -const getLogs = () => { - const rows: LogRowsType = []; - for (let i = 0; i < 200; i++) { - rows.push({ - message: 'Test' + i, - level: 'WARN', - created: new Date('Mon Jan 1 00:00:02 UTC 2001').toISOString(), - container: { id: i, name: 'container' }, - }); - } - return rows; -}; - -const getLogsPromise = async () => await getLogs(); -const getLabelsPromise = async () => await getLogLabels(); - -it('smoke test', async () => { - // Added the waitFor because this component is updating a state inside a useEffect. - await waitFor(() => { - render(, { - wrapper: TestLocalizationProvider, - }); - }); -}); diff --git a/packages/react-components/lib/reports/log-management/log-management.tsx b/packages/react-components/lib/reports/log-management/log-management.tsx deleted file mode 100644 index e139f1f74..000000000 --- a/packages/react-components/lib/reports/log-management/log-management.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import React from 'react'; -import { styled } from '@mui/material'; -import { SearchLogForm } from './search-log-form'; -import { LogRowsType, LogTable } from './log-table'; - -const classes = { - table: 'log-management-table', - background: 'log-management-background', -}; -const StyledDiv = styled('div')(({ theme }) => ({ - [`&.${classes.background}`]: { - backgroundColor: theme.palette.background.paper, - }, - [`& .${classes.table}`]: { - overflowY: 'scroll', - paddingTop: '20px', - }, -})); - -export interface LogQueryPayload { - toLogDate?: unknown | null; - fromLogDate?: unknown | null; - logLabel?: string | null; - logLevel?: string | null; - offset?: number | null; -} - -export interface LogManagementProps { - getLogs: (data: LogQueryPayload) => Promise; - getLabels: () => Promise<{ label: string; value: string }[]>; -} - -export const LogManagement = (props: LogManagementProps): React.ReactElement => { - const { getLogs, getLabels } = props; - const [logs, setLogs] = React.useState([]); - const [logLabels, setLogLabels] = React.useState<{ label: string; value: string }[]>([]); - const [lastSearchParams, setLastSearchParams] = React.useState({}); - - React.useEffect(() => { - const getLogLabels = async () => { - const labels = await getLabels(); - setLogLabels(labels); - }; - getLogLabels(); - }, [getLabels]); - - const searchLogs = async (payload: LogQueryPayload) => { - setLastSearchParams(payload); - setLogs(await getLogs(payload)); - }; - - const getMoreLogs = async () => { - setLogs(logs.concat(await getLogs({ ...lastSearchParams, offset: logs.length }))); - }; - - return ( - - -
- {logs.length !== 0 && } -
-
- ); -}; diff --git a/packages/react-components/lib/reports/log-management/log-table.spec.tsx b/packages/react-components/lib/reports/log-management/log-table.spec.tsx deleted file mode 100644 index ba71a469a..000000000 --- a/packages/react-components/lib/reports/log-management/log-table.spec.tsx +++ /dev/null @@ -1,118 +0,0 @@ -import { cleanup, render, RenderResult, screen } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import { format } from 'date-fns'; -import React from 'react'; -import { LogLevel } from './log-level'; -import { LogRowsType, LogTable } from './log-table'; - -const rows = [] as LogRowsType; - -const logLevels = Object.values(LogLevel); - -const getRandomLogLevel = () => { - const number = Math.floor(Math.random() * logLevels.length); - return logLevels[number]; -}; - -const timestamp = new Date('Mon Jan 1 00:00:02 UTC 2001'); - -for (let i = 0; i < 110; i++) { - rows.push({ - message: 'Test' + i, - level: getRandomLogLevel().toUpperCase(), - created: format(timestamp, 'MMM dd yyyy hh:mm aaa'), - container: { id: i, name: 'container' }, - }); -} - -describe('Log table test', () => { - let root: RenderResult; - beforeEach(() => { - root = render(); - }); - - afterEach(cleanup); - - it('formats dates correctly', async () => { - const tableFirstDateElement = (await root.getAllByTestId('log-table-date'))[0]; - expect(tableFirstDateElement.innerHTML).toBe(format(timestamp, 'MMM dd yyyy hh:mm aaa')); - }); - - it('shows the correct number of rows', () => { - const allRows = root.container.querySelectorAll('.MuiDataGrid-row').length; - expect(allRows).toBe(100); - }); -}); - -describe('Table footer Pagination', () => { - beforeEach(() => { - render(); - }); - - afterEach(cleanup); - - it('show the correct number of rows per page', () => { - // NOTE: mui v5 is using the unicode char '–', different from '-'!! - expect(screen.getByText('1–100 of 110')).toBeTruthy(); - }); - - it('can change the rows per page', async () => { - userEvent.click(screen.getByText('100')); - userEvent.click(screen.getByText('50')); - - // NOTE: mui v5 is using the unicode char '–', different from '-'!! - expect(await screen.getByText('1–50 of 110')).toBeTruthy(); - }); - - it('advance page when the `Next Page` button is clicked ', async () => { - const nextPageButton = screen.queryByLabelText('Go to next page'); - nextPageButton && userEvent.click(nextPageButton); - // NOTE: mui v5 is using the unicode char '–', different from '-'!! - expect(screen.getByText('101–110 of 110')).toBeTruthy(); - }); - - it('goes to previous page when the `Previous page` button is clicked ', () => { - const nextPageButton = screen.queryByLabelText('Go to next page'); - nextPageButton && userEvent.click(nextPageButton); - // NOTE: mui v5 is using the unicode char '–', different from '-'!! - expect(screen.getByText('101–110 of 110')).toBeTruthy(); - - const previousPageButton = screen.queryByLabelText('Go to previous page'); - previousPageButton && userEvent.click(previousPageButton); - // NOTE: mui v5 is using the unicode char '–', different from '-'!! - expect(screen.getByText('1–100 of 110')).toBeTruthy(); - }); -}); - -describe('Applies styles to labels correctly', () => { - const styleRows = [] as LogRowsType; - - for (let i = 0; i < logLevels.length; i++) { - styleRows.push({ - message: 'Test' + i, - level: logLevels[i].toUpperCase(), - created: format(timestamp, 'MMM dd yyyy hh:mm aaa'), - container: { id: i, name: 'container' }, - }); - } - - beforeEach(() => { - render(); - }); - - it('set the style correctly when the label ERROR ', () => { - expect(screen.getByText('ERROR').className).toContain('log-table-error'); - }); - - it('set the style correctly when the label DEBUG ', () => { - expect(screen.getByText('DEBUG').className).toContain('log-table-debug'); - }); - - it('set the style correctly when the label WARN ', () => { - expect(screen.getByText('WARN').className).toContain('log-table-warn'); - }); - - it('set the style correctly when the label FATAL ', () => { - expect(screen.getByText('FATAL').className).toContain('log-table-error'); - }); -}); diff --git a/packages/react-components/lib/reports/log-management/log-table.tsx b/packages/react-components/lib/reports/log-management/log-table.tsx deleted file mode 100644 index 54b4ecc42..000000000 --- a/packages/react-components/lib/reports/log-management/log-table.tsx +++ /dev/null @@ -1,176 +0,0 @@ -import React from 'react'; -import { DataGrid, GridRenderCellParams } from '@mui/x-data-grid'; -import { LogLevel } from '.'; -import { Typography, styled } from '@mui/material'; -import { format } from 'date-fns'; -import { CustomLookupFilter } from './custom-lookup-filter'; - -export type ContainerType = { - id: number; - name: string; -}; - -export type LogRowsType = { - level: string; - message: string; - created: string; - container: ContainerType; -}[]; - -export interface LogTableProps { - rows: LogRowsType | []; - addMoreRows?(): void; -} - -const classes = { - error: 'log-table-error', - debug: 'log-table-debug', - warn: 'log-table-warn', - info: 'log-table-info', - cellContent: 'log-table-cell-content', -}; -const StyledDiv = styled('div')(({ theme }) => ({ - [`& .${classes.error}`]: { - color: theme.palette.error.main, - }, - [`& .${classes.debug}`]: { - color: theme.palette.secondary.dark, - }, - [`& .${classes.warn}`]: { - color: theme.palette.warning.light, - }, - [`& .${classes.info}`]: { - color: theme.palette.info.main, - }, - [`& .${classes.cellContent}`]: { - display: 'block', - marginBlockStart: '1em', - marginBlockEnd: '1em', - marginInlineStart: '0px', - marginInlineEnd: '0px', - }, -})); - -export const LogTable = (props: LogTableProps): React.ReactElement => { - const { rows, addMoreRows } = props; - const [pageSize, setPageSize] = React.useState(100); - const getLogLevelStyle = (level: string): string | undefined => { - level = level.toLowerCase(); - switch (level) { - case LogLevel.Error: - return classes.error; - case LogLevel.Warn: - return classes.warn; - case LogLevel.Fatal: - return classes.error; - case LogLevel.Debug: - return classes.debug; - case LogLevel.Info: - return classes.info; - default: - return undefined; - } - }; - - const [selectedFilter, setSelectedFilter] = React.useState([]); - // FIXME: we cannot copy the LogLevel Enum directly and remove the all attribute because it has a protected attribute. - const logLevels = React.useMemo(() => { - const logLevelCopy: Record = {}; - Object.keys(LogLevel).forEach((element: string) => { - logLevelCopy[element] = LogLevel[element as keyof typeof LogLevel]; - }); - delete logLevelCopy['All']; - return logLevelCopy; - }, []); - - return ( - - r.container.id} - autoHeight={true} - columns={[ - { - headerName: 'Level', - field: 'level', - type: 'string', - align: 'center', - width: 100, - sortable: false, - renderCell: (rowData: GridRenderCellParams) => { - return ( - - {rowData.row.level} - - ); - }, - }, - { - headerName: 'Container', - field: 'name', - type: 'string', - align: 'center', - width: 120, - sortable: false, - renderCell: (rowData: GridRenderCellParams) => { - return ( - - {rowData.row.container.name ? rowData.row.container.name : 'Unknown'} - - ); - }, - }, - { - headerName: 'Message', - field: 'message', - type: 'string', - width: 1200, - sortable: false, - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.value}; - }, - }, - { - headerName: 'Timestamp', - field: 'created', - type: 'datetime', - filterable: false, - sortable: false, - align: 'center', - renderCell: (rowData: GridRenderCellParams) => { - return ( - - {format(new Date(rowData.value as number), 'MMM dd yyyy hh:mm aaa')} - - ); - }, - }, - ]} - components={{ - Toolbar: () => ( - - ), - }} - rows={rows.filter((row) => { - if (!selectedFilter.includes(row.level)) return row; - })} - pageSize={pageSize} - rowsPerPageOptions={[50, 100]} - onPageChange={() => { - if (addMoreRows) { - addMoreRows(); - } - }} - onPageSizeChange={(pageSize) => { - setPageSize(pageSize); - }} - disableColumnMenu={true} - /> - - ); -}; diff --git a/packages/react-components/lib/reports/log-management/logging.stories.tsx b/packages/react-components/lib/reports/log-management/logging.stories.tsx deleted file mode 100644 index c34541cf0..000000000 --- a/packages/react-components/lib/reports/log-management/logging.stories.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import { Meta, Story } from '@storybook/react'; -import React from 'react'; -import { LogManagement } from './log-management'; -import { LogTable } from './log-table'; -import { SearchLogForm } from './search-log-form'; - -export default { - title: 'Logging', - argTypes: {}, -} as Meta; - -const getLogLabels = () => [ - { label: 'Web Server', value: 'web-server' }, - { label: 'RMF core', value: 'rmf-core' }, -]; - -const timestamp = new Date('Mon Jan 1 00:00:02 UTC 2001').toISOString(); - -function randomDate(start: Date, end: Date) { - return new Date(start.getTime() + Math.random() * (end.getTime() - start.getTime())); -} - -const getLogs = () => { - const rows = []; - for (let i = 0; i < 500; i++) { - rows.push({ - message: 'Test' + i, - level: 'Debug', - created: randomDate(new Date(2012, 0, 1), new Date()).toISOString(), - container: { id: i + 2, name: 'container_test' }, - }); - } - return rows; -}; - -const getLogsPromise = async () => getLogs(); -const getLabelsPromise = async () => getLogLabels(); - -export const LogManagementExample: Story = (args) => ( - -); - -export const SimpleSearchLogForm: Story = (args) => ( - -); - -export const SimpleLogTable: Story = (args) => { - const logs = getLogs(); - logs.unshift({ - message: ` - npm ERR! code ELIFECYCLE - npm ERR! errno 1 - npm ERR! react-components@0.0.1 build: 'npm run lint && tsc --build' - npm ERR! Exit status 1 - npm ERR! - npm ERR! Failed at the react-components@0.0.1 build script. - npm ERR! This is probably not a problem with npm. There is likely additional logging output above. - - npm ERR! A complete log of this run can be found in: - npm ERR! /home/ekumen/.npm/_logs/2021-01-18T21_20_07_480Z-debug.log`, - level: 'Error', - created: timestamp, - container: { id: 1, name: 'container_test' }, - }); - logs.unshift({ - message: `long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long msg`, - level: 'Debug', - created: timestamp, - container: { id: 0, name: 'container_test' }, - }); - - return ; -}; diff --git a/packages/react-components/lib/reports/log-management/search-filter.tsx b/packages/react-components/lib/reports/log-management/search-filter.tsx deleted file mode 100644 index d056f0eec..000000000 --- a/packages/react-components/lib/reports/log-management/search-filter.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import React from 'react'; -import { - FormControl, - InputLabel, - MenuItem, - Select, - SelectChangeEvent, - styled, -} from '@mui/material'; - -const classes = { - formControl: 'search-filter-formcontrol', -}; -const StyledDiv = styled('div')(({ theme }) => ({ - [`& .${classes.formControl}`]: { - margin: theme.spacing(1), - minWidth: '230px', - }, -})); - -interface SearchFilterProps { - options: { label: string; value: string }[]; - handleOnChange: (event: SelectChangeEvent, child: React.ReactNode) => void; - label: string; - name: string; - currentValue: string | number; -} - -export const SearchFilter = (props: SearchFilterProps): React.ReactElement => { - const { handleOnChange, options, name, label, currentValue } = props; - - return ( - -
- - {label} - - -
-
- ); -}; diff --git a/packages/react-components/lib/reports/log-management/search-log-form.spec.tsx b/packages/react-components/lib/reports/log-management/search-log-form.spec.tsx deleted file mode 100644 index 483468a2d..000000000 --- a/packages/react-components/lib/reports/log-management/search-log-form.spec.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { render } from '@testing-library/react'; -import React from 'react'; -import { TestLocalizationProvider } from '../../test/locale'; -import { SearchLogForm } from './search-log-form'; - -describe('Search log form tests', () => { - const logLabel = [ - { label: 'Web Server', value: 'web-server' }, - { label: 'RMF core', value: 'rmf-core' }, - ]; - - it('smoke test', () => { - render(, { - wrapper: TestLocalizationProvider, - }); - }); -}); diff --git a/packages/react-components/lib/reports/log-management/search-log-form.tsx b/packages/react-components/lib/reports/log-management/search-log-form.tsx deleted file mode 100644 index 0c4e3a3d2..000000000 --- a/packages/react-components/lib/reports/log-management/search-log-form.tsx +++ /dev/null @@ -1,130 +0,0 @@ -import { DateTimePickerProps } from '@mui/lab'; -import DateTimePicker from '@mui/lab/DateTimePicker'; -import { SelectChangeEvent, styled, TextField } from '@mui/material'; -import Button from '@mui/material/Button'; -import React from 'react'; -import { LogQueryPayload } from '.'; -import { LogLevel } from './log-level'; -import { SearchFilter } from './search-filter'; - -interface SearchLogFormProps { - logLabelValues: { label: string; value: string }[]; - search?: (payload: LogQueryPayload) => void; -} - -const classes = { - searchForm: 'search-log-search-from', - searchButton: 'search-log-search-button', - formControl: 'search-log-form-form-control', - background: 'search-log-form-background', -}; -const StyledDiv = styled('div')(({ theme }) => ({ - [`&.${classes.background}`]: { - backgroundColor: theme.palette.background.paper, - }, - [`& .${classes.searchForm}`]: { - display: 'grid', - gridTemplateColumns: '1fr 1fr 1fr 1fr', - alignItems: 'center', - justifyItems: 'center', - }, - [`& .${classes.searchButton}`]: { - width: '100%', - }, - [`& .${classes.formControl}`]: { - margin: theme.spacing(1), - minWidth: 120, - }, -})); - -const logLevelValues = [ - { label: 'ALL', value: LogLevel.All }, - { label: 'FATAL', value: LogLevel.Fatal }, - { label: 'ERROR', value: LogLevel.Error }, - { label: 'WARN', value: LogLevel.Warn }, - { label: 'INFO', value: LogLevel.Info }, - { label: 'DEBUG', value: LogLevel.Debug }, -]; - -export const SearchLogForm = (props: SearchLogFormProps): React.ReactElement => { - const { search, logLabelValues } = props; - // The log contains information from different services, the label help us differentiate the service - const [logLabel, setLogLabel] = React.useState(''); - const [logLevel, setLogLevel] = React.useState(LogLevel.All); - const [fromLogDate, setFromLogDate] = React.useState(new Date()); - const [toLogDate, setToLogDate] = React.useState(new Date()); - - const searchQuery = () => { - search && search({ toLogDate, fromLogDate, logLabel, logLevel }); - }; - - const handleLogLabelChange = React.useCallback((event: SelectChangeEvent) => { - setLogLabel(event.target.value as string); - }, []); - - const handleLogLevelChange = React.useCallback((event: SelectChangeEvent) => { - setLogLevel(event.target.value as LogLevel); - }, []); - - const handleFromLogDateChange: Required['onChange'] = React.useCallback( - (date) => { - setFromLogDate(date); - }, - [], - ); - - const handleToLogDateChange: Required['onChange'] = React.useCallback( - (date) => { - setToLogDate(date); - }, - [], - ); - - return ( - -
- - - - - } - /> - } - /> -
- -

- -
- ); -}; diff --git a/packages/react-components/lib/reports/multi-level-menu.spec.tsx b/packages/react-components/lib/reports/multi-level-menu.spec.tsx deleted file mode 100644 index 08e832101..000000000 --- a/packages/react-components/lib/reports/multi-level-menu.spec.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import { cleanup, render, screen } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import React from 'react'; -import { ExpandableMultilevelMenuProps, MultiLevelMenu } from './multi-level-menu'; - -describe('Multi level menu', () => { - let buildMenuStructure: ExpandableMultilevelMenuProps[]; - let mockClickAllLogs: ReturnType; - let mockClickRobotStates: ReturnType; - - beforeEach(() => { - mockClickAllLogs = jasmine.createSpy(); - mockClickRobotStates = jasmine.createSpy(); - - const buildReportMenuStructure = (): ExpandableMultilevelMenuProps[] => { - return [ - { - icon:

Mock Icon 1

, - title: 'All logs', - items: [], - onClick: mockClickAllLogs, - }, - { - icon:

Mock Icon 2

, - title: 'Robots', - items: [ - { - title: 'Robot states', - items: [], - onClick: mockClickRobotStates, - }, - ], - }, - ] as ExpandableMultilevelMenuProps[]; - }; - buildMenuStructure = buildReportMenuStructure(); - }); - - afterEach(() => { - cleanup(); - }); - - it('renders correctly with `All logs` and `Robots` titles on the first level', () => { - render(); - expect(screen.getByText('Mock Icon 1')).toBeTruthy(); - expect(screen.getByText('All logs')).toBeTruthy(); - - expect(screen.getByText('Mock Icon 2')).toBeTruthy(); - expect(screen.getByText('Robots')).toBeTruthy(); - - expect(screen.queryByText('Robot states')).toBeFalsy(); - }); - - it('triggers an action, if defined, when clicking on a title ', () => { - render(); - userEvent.click(screen.getByText('All logs')); - expect(mockClickAllLogs).toHaveBeenCalledTimes(1); - }); - - it('shows the child level after clicking the parent level', () => { - render(); - userEvent.click(screen.getByText('Robots')); - expect(screen.getByText('Robot states')).toBeTruthy(); - }); - - it('triggers an action, if defined, after clicking on a child level title', () => { - render(); - userEvent.click(screen.getByText('Robots')); - userEvent.click(screen.getByText('Robot states')); - expect(mockClickRobotStates).toHaveBeenCalledTimes(1); - }); - - it('shows the parent level and the child level titles simultaneously', () => { - render(); - userEvent.click(screen.getByText('Robots')); - screen.getByText('Robot states'); - expect(screen.getByText('Robots')).toBeTruthy(); - expect(screen.getByText('Robot states')).toBeTruthy(); - }); -}); diff --git a/packages/react-components/lib/reports/multi-level-menu.stories.tsx b/packages/react-components/lib/reports/multi-level-menu.stories.tsx deleted file mode 100644 index 21dab7618..000000000 --- a/packages/react-components/lib/reports/multi-level-menu.stories.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import AndroidIcon from '@mui/icons-material/Android'; -import ArrowDropUpIcon from '@mui/icons-material/ArrowDropUp'; -import { styled } from '@mui/material'; -import SearchIcon from '@mui/icons-material/Search'; -import { Meta, Story } from '@storybook/react'; -import React from 'react'; -import { ExpandableMultilevelMenuProps, MultiLevelMenu } from './multi-level-menu'; - -const classes = { - container: 'mlm-story-container', -}; - -const MlmStory = styled('div')(({ theme }) => ({ - [`&.${classes.container}`]: { - backgroundColor: theme.palette.background.paper, - }, -})); - -export default { - title: 'Multi level menu', -} as Meta; - -const menuStructure = [ - { - icon: , - title: 'All logs', - items: [], - }, - { - icon: , - title: 'Robots', - items: [ - { - title: 'Robot states', - items: [], - }, - ], - }, - { - icon: , - title: 'Test', - items: [ - { - title: 'Test-child', - items: [], - }, - ], - }, -] as ExpandableMultilevelMenuProps[]; - -export const MultiLevelMenuStory: Story = (args) => ( - - - -); diff --git a/packages/react-components/lib/reports/multi-level-menu.tsx b/packages/react-components/lib/reports/multi-level-menu.tsx deleted file mode 100644 index 8ade25fbe..000000000 --- a/packages/react-components/lib/reports/multi-level-menu.tsx +++ /dev/null @@ -1,113 +0,0 @@ -import React, { useState } from 'react'; -import { styled, ListProps } from '@mui/material'; -import List from '@mui/material/List'; -import ListItem from '@mui/material/ListItem'; -import ListItemIcon from '@mui/material/ListItemIcon'; -import ListItemText from '@mui/material/ListItemText'; -import Collapse from '@mui/material/Collapse'; -import ExpandLess from '@mui/icons-material/ExpandLess'; -import ExpandMore from '@mui/icons-material/ExpandMore'; - -const classes = { - textAndIcon: 'mlm-text-and-icon', -}; - -const StyledList = styled((props: ListProps) => )(({ theme }) => ({ - [`& .${classes.textAndIcon}`]: { - color: theme.palette.text.primary, - }, -})); - -interface ListItemBodyProps { - icon?: JSX.Element; - title: string; -} - -const ListItemBody = (props: ListItemBodyProps): JSX.Element => { - return ( - <> - {props.icon} - - - ); -}; - -interface MenuItemProps { - icon?: JSX.Element; - title: string; - items?: { - title: string; - to: string; - }[]; - onClick?: () => void; -} - -const MenuItem = React.memo((props: MenuItemProps): JSX.Element => { - return ( - - - - ); -}); - -export interface ExpandableMultilevelMenuProps { - icon?: JSX.Element; - title: string; - items: MenuItemProps[]; - onClick?: () => void; -} - -const ExpandableMenuItem = (props: ExpandableMultilevelMenuProps): JSX.Element => { - const { items, icon, title } = props; - const [open, setOpen] = useState(false); - const handleClick = () => { - setOpen(!open); - }; - - return ( -
- - - {open ? : } - - - - -
- ); -}; - -export interface MultilevelMenuProps { - menuStructure: (ExpandableMultilevelMenuProps | MenuItemProps)[]; -} - -export const MultiLevelMenu = React.memo((props: MultilevelMenuProps): React.ReactElement => { - const createList = (items: (ExpandableMultilevelMenuProps | MenuItemProps)[]) => { - const menu: JSX.Element[] = []; - items.map((menuItem: ExpandableMultilevelMenuProps | MenuItemProps) => { - // If it has children's - if (Array.isArray(menuItem.items) && menuItem.items.length > 0) { - menu.push( - , - ); - } else { - menu.push( - , - ); - } - }); - return menu.concat(); - }; - - return {createList(props.menuStructure)}; -}); diff --git a/packages/react-components/lib/reports/task-summary/index.ts b/packages/react-components/lib/reports/task-summary/index.ts deleted file mode 100644 index ce0aeaab6..000000000 --- a/packages/react-components/lib/reports/task-summary/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './task-summary-report'; -export * from './task-summary-report-table'; diff --git a/packages/react-components/lib/reports/task-summary/task-summary-report-table.spec.tsx b/packages/react-components/lib/reports/task-summary/task-summary-report-table.spec.tsx deleted file mode 100644 index 9e322124e..000000000 --- a/packages/react-components/lib/reports/task-summary/task-summary-report-table.spec.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import { cleanup, render, RenderResult, screen } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import { format } from 'date-fns'; -import React from 'react'; -import { getTaskSummaryLogs } from '../utils.spec'; -import { TaskSummaryReportTable } from './task-summary-report-table'; - -const timestamp = new Date('Mon Jan 1 00:00:02 UTC 2001'); - -describe('Task Summary Table Test', () => { - let root: RenderResult; - let mockAddMoreRows: ReturnType; - - beforeEach(() => { - mockAddMoreRows = jasmine.createSpy(); - root = render( - , - ); - }); - - afterEach(cleanup); - - it('formats dates correctly', async () => { - const tableFirstDateElement = (await root.getAllByTestId('task-table-date'))[0]; - expect(tableFirstDateElement.innerHTML).toBe(format(timestamp, 'MMM dd yyy hh:mm aaa')); - }); - - it('shows the correct number of rows', () => { - const allRows = root.container.querySelectorAll('.MuiDataGrid-row').length; - expect(allRows).toBe(100); - }); - - it('shows the correct headers', () => { - expect(screen.queryByText('Task ID')).toBeTruthy(); - expect(screen.queryByText('Fleet')).toBeTruthy(); - expect(screen.queryByText('Robot')).toBeTruthy(); - expect(screen.queryByText('Task Description')).toBeTruthy(); - expect(screen.queryByText('State')).toBeTruthy(); - expect(screen.queryByText('Time')).toBeTruthy(); - expect(screen.queryByText('Timestamp')).toBeTruthy(); - }); - - it('executes the addMoreRows function', () => { - const nextPageButton = screen.queryByTitle('Go to next page'); - nextPageButton && userEvent.click(nextPageButton); - expect(mockAddMoreRows).toHaveBeenCalled(); - }); -}); diff --git a/packages/react-components/lib/reports/task-summary/task-summary-report-table.tsx b/packages/react-components/lib/reports/task-summary/task-summary-report-table.tsx deleted file mode 100644 index db1a5a7d6..000000000 --- a/packages/react-components/lib/reports/task-summary/task-summary-report-table.tsx +++ /dev/null @@ -1,126 +0,0 @@ -import React from 'react'; -import { DataGrid, GridRenderCellParams } from '@mui/x-data-grid'; -import type { Time } from 'api-client'; -import { format } from 'date-fns'; -import { Typography } from '@mui/material'; -import { rosTimeToJs } from '../../utils'; -import { DefaultLogTableProps } from '../default-report-interface'; -import { returnTaskDetails } from './utils'; - -export type TaskSummaryRowsType = { - created: string; //date - fleet: { id: number; name: string }; - robot: { id: number; name: string; model?: string }; - task_id: string; - task_profile: any; - state: string; - status: string; - submission_time: Time; - start_time: Time; - end_time: Time; -}[]; - -export interface TaskSummaryReportTable extends DefaultLogTableProps { - rows: TaskSummaryRowsType | []; -} - -export const TaskSummaryReportTable = (props: TaskSummaryReportTable): React.ReactElement => { - const { rows, addMoreRows } = props; - - return ( -
- r.task_id} - columns={[ - { - headerName: 'Task ID', - field: 'task_id', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.task_id}; - }, - }, - { - headerName: 'Fleet', - field: 'fleet_name', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.fleet.name}; - }, - }, - { - headerName: 'Robot', - field: 'robot_name', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.robot.name}; - }, - }, - { - headerName: 'Task Description', - field: 'description', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - const taskTypeDetails = returnTaskDetails( - rowData.row.task_id, - rowData.row.task_profile.description, - ); - return taskTypeDetails; - }, - }, - { - headerName: 'State', - field: 'state', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.state}; - }, - }, - { - headerName: 'Time', - field: 'time_information', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - const submissionTime = rosTimeToJs( - rowData.row.task_profile.submission_time, - ).toLocaleTimeString(); - const startTime = rosTimeToJs(rowData.row.start_time).toLocaleTimeString(); - const endTime = rosTimeToJs(rowData.row.end_time).toLocaleTimeString(); - return ( - <> - Submitted: {submissionTime} - Start: {startTime} - End: {endTime} - - ); - }, - }, - { - headerName: 'Timestamp', - field: 'created', - type: 'datetime', - filterable: false, - align: 'center', - renderCell: (rowData: GridRenderCellParams) => { - return ( - - {format(new Date(rowData.value as number), 'MMM dd yyyy hh:mm aaa')} - - ); - }, - }, - ]} - rows={rows} - pageSize={100} - rowsPerPageOptions={[50, 100]} - onPageChange={() => { - if (addMoreRows) { - addMoreRows(); - } - }} - disableColumnMenu={true} - /> -
- ); -}; diff --git a/packages/react-components/lib/reports/task-summary/task-summary-report.spec.tsx b/packages/react-components/lib/reports/task-summary/task-summary-report.spec.tsx deleted file mode 100644 index fd86510e8..000000000 --- a/packages/react-components/lib/reports/task-summary/task-summary-report.spec.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import { render, screen, waitFor } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import React from 'react'; -import { TestLocalizationProvider } from '../../test/locale'; -import { getTaskSummaryLogs, reportConfigProps } from '../utils.spec'; -import { TaskSummaryReport } from './task-summary-report'; - -const getLogsPromise = async () => getTaskSummaryLogs(); - -it('smoke test', async () => { - await waitFor(() => { - render(); - }); -}); - -it('does not show the table when the logs list is empty', async () => { - await waitFor(() => { - render( []} />); - }); - - expect(screen.queryByText('Task Summary')).toBeFalsy(); -}); - -it('calls the Retrieve Logs function when the button is clicked', async () => { - const getLogsPromiseMock = jasmine.createSpy(); - const getLogsPromise = async () => { - getLogsPromiseMock(); - return getTaskSummaryLogs(); - }; - - render(, { - wrapper: TestLocalizationProvider, - }); - const retrieveLogsButton = screen.getByRole('button', { name: /Retrieve Logs/i }); - expect(retrieveLogsButton).toBeTruthy(); - userEvent.click(retrieveLogsButton); - expect(getLogsPromiseMock).toHaveBeenCalled(); -}); diff --git a/packages/react-components/lib/reports/task-summary/task-summary-report.tsx b/packages/react-components/lib/reports/task-summary/task-summary-report.tsx deleted file mode 100644 index f7d0c2835..000000000 --- a/packages/react-components/lib/reports/task-summary/task-summary-report.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import React from 'react'; -import { - DefaultReportQueryPayload, - defaultReportClasses, - DefaultReportContainer, -} from '../default-report-interface'; -import { DefaultDatesForm } from '../default-dates-form'; -import { TaskSummaryReportTable, TaskSummaryRowsType } from './task-summary-report-table'; -import { ReportConfigProps } from '../utils'; - -export interface TaskSummaryReportProps extends ReportConfigProps { - getLogs: (data: DefaultReportQueryPayload) => Promise; -} - -export const TaskSummaryReport = (props: TaskSummaryReportProps): React.ReactElement => { - const { getLogs, ...otherProps } = props; - const [logs, setLogs] = React.useState([]); - const [lastSearchParams, setLastSearchParams] = React.useState({}); - - const searchLogs = async (payload: DefaultReportQueryPayload) => { - setLastSearchParams(payload); - setLogs(await getLogs(payload)); - }; - - const getMoreLogs = async () => { - setLogs(logs.concat(await getLogs({ ...lastSearchParams, offset: logs.length }))); - }; - - return ( - - -
- {logs.length !== 0 && ( - - )} -
-
- ); -}; - -export default TaskSummaryReport; diff --git a/packages/react-components/lib/reports/task-summary/utils.tsx b/packages/react-components/lib/reports/task-summary/utils.tsx deleted file mode 100644 index 6437b6277..000000000 --- a/packages/react-components/lib/reports/task-summary/utils.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import { Typography } from '@mui/material'; -// import type { TaskDescription } from 'api-client'; -import React from 'react'; - -export const returnTaskDetails = (taskId: string, taskDescription: any): React.ReactNode => { - let taskTypeDetails; - if (taskId.includes('Loop')) { - taskTypeDetails = taskDescription.loop; - return ( - <> - Num of Loops: {taskTypeDetails.num_loops} - Start Point: {taskTypeDetails.start_name} - End Point: {taskTypeDetails.finish_name} - - ); - } else if (taskId.includes('Delivery')) { - taskTypeDetails = taskDescription.delivery; - return ( - <> - Pick Up: {taskTypeDetails.pickup_place_name} - Drop Off: {taskTypeDetails.dropoff_place_name} - End Point: {taskTypeDetails.items} - - ); - } else if (taskId.includes('Clean')) { - taskTypeDetails = taskDescription.clean; - return ( - <> - Start Point: {taskTypeDetails.start_waypoint} - - ); - } -}; diff --git a/packages/react-components/lib/reports/user-report/index.ts b/packages/react-components/lib/reports/user-report/index.ts deleted file mode 100644 index 9c04239c0..000000000 --- a/packages/react-components/lib/reports/user-report/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -export * from './user-login-failure-report-table'; -export * from './user-login-failure-report'; -export * from './user-login-report'; -export * from './user-login-report-table'; -export * from './user-logout-report'; -export * from './user-logout-report-table'; diff --git a/packages/react-components/lib/reports/user-report/user-login-failure-report-table.tsx b/packages/react-components/lib/reports/user-report/user-login-failure-report-table.tsx deleted file mode 100644 index c8198dfb9..000000000 --- a/packages/react-components/lib/reports/user-report/user-login-failure-report-table.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import React from 'react'; -import { DataGrid, GridRenderCellParams } from '@mui/x-data-grid'; -import { Typography } from '@mui/material'; -import { DefaultLogTableProps } from '../default-report-interface'; -import { format } from 'date-fns'; - -export type UserLoginFailureRowsType = { - created: string; //date - ip_address: string; - client_id: string; - username: string; - error: string; -}[]; - -export interface UserLoginFailureReportTable extends DefaultLogTableProps { - rows: UserLoginFailureRowsType; -} - -export const UserLoginFailureReportTable = ( - props: UserLoginFailureReportTable, -): React.ReactElement => { - const { rows, addMoreRows } = props; - - return ( -
- r.client_id} - columns={[ - { - headerName: 'Username', - field: 'username', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.username}; - }, - }, - - { - headerName: 'Client ID', - field: 'client_id', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.client_id}; - }, - }, - { - headerName: 'IP Addr.', - field: 'ip_address', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.ip_address}; - }, - }, - - { - headerName: 'Timestamp', - field: 'created', - type: 'datetime', - filterable: false, - align: 'center', - renderCell: (rowData: GridRenderCellParams) => { - return ( - - {format(new Date(rowData.value as number), 'MMM dd yyyy hh:mm aaa')} - - ); - }, - }, - ]} - rows={rows} - pageSize={100} - rowsPerPageOptions={[50, 100]} - onPageChange={() => { - if (addMoreRows) { - addMoreRows(); - } - }} - disableColumnMenu={true} - /> -
- ); -}; diff --git a/packages/react-components/lib/reports/user-report/user-login-failure-report.tsx b/packages/react-components/lib/reports/user-report/user-login-failure-report.tsx deleted file mode 100644 index d40ee77af..000000000 --- a/packages/react-components/lib/reports/user-report/user-login-failure-report.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import React from 'react'; -import { - DefaultReportQueryPayload, - DefaultReportContainer, - defaultReportClasses, -} from '../default-report-interface'; -import { DefaultDatesForm } from '../default-dates-form'; -import { - UserLoginFailureReportTable, - UserLoginFailureRowsType, -} from './user-login-failure-report-table'; -import { ReportConfigProps } from '../utils'; - -export interface UserLoginFailureReportProps extends ReportConfigProps { - getLogs: (data: DefaultReportQueryPayload) => Promise; -} - -export const UserLoginFailureReport = (props: UserLoginFailureReportProps): React.ReactElement => { - const { getLogs, ...otherProps } = props; - const [logs, setLogs] = React.useState([]); - const [lastSearchParams, setLastSearchParams] = React.useState({}); - - const searchLogs = async (payload: DefaultReportQueryPayload) => { - setLastSearchParams(payload); - setLogs(await getLogs(payload)); - }; - - const getMoreLogs = async () => { - setLogs(logs.concat(await getLogs({ ...lastSearchParams, offset: logs.length }))); - }; - - return ( - - -
- {logs.length !== 0 && ( - - )} -
-
- ); -}; - -export default UserLoginFailureReport; diff --git a/packages/react-components/lib/reports/user-report/user-login-report-table.tsx b/packages/react-components/lib/reports/user-report/user-login-report-table.tsx deleted file mode 100644 index b1a95ba53..000000000 --- a/packages/react-components/lib/reports/user-report/user-login-report-table.tsx +++ /dev/null @@ -1,87 +0,0 @@ -import React from 'react'; -import { DataGrid, GridRenderCellParams } from '@mui/x-data-grid'; -import { Typography } from '@mui/material'; -import { DefaultLogTableProps } from '../default-report-interface'; -import { format } from 'date-fns'; - -export type UserLoginRowsType = { - client_id: string; - created: string; //date - ip_address: string; - user_id: string; - username: string; -}[]; - -export interface UserLoginReportTable extends DefaultLogTableProps { - rows: UserLoginRowsType; -} - -export const UserLoginReportTable = (props: UserLoginReportTable): React.ReactElement => { - const { rows, addMoreRows } = props; - - return ( -
- r.client_id} - columns={[ - { - headerName: 'Username', - field: 'username', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.username}; - }, - }, - { - headerName: 'Timestamp', - field: 'created', - type: 'datetime', - filterable: false, - align: 'center', - renderCell: (rowData: GridRenderCellParams) => { - return ( - - {format(new Date(rowData.value as number), 'MMM dd yyyy hh:mm aaa')} - - ); - }, - }, - { - headerName: 'Client ID', - field: 'client_id', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.client_id}; - }, - }, - { - headerName: 'User ID', - field: 'user_id', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.user_id}; - }, - }, - { - headerName: 'IP Addr', - field: 'ip_address', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.ip_address}; - }, - }, - ]} - rows={rows} - pageSize={100} - rowsPerPageOptions={[50, 100]} - onPageChange={() => { - if (addMoreRows) { - addMoreRows(); - } - }} - disableColumnMenu={true} - /> -
- ); -}; diff --git a/packages/react-components/lib/reports/user-report/user-login-report.tsx b/packages/react-components/lib/reports/user-report/user-login-report.tsx deleted file mode 100644 index 630fa09dc..000000000 --- a/packages/react-components/lib/reports/user-report/user-login-report.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import React from 'react'; -import { - DefaultReportQueryPayload, - defaultReportClasses, - DefaultReportContainer, -} from '../default-report-interface'; -import { DefaultDatesForm } from '../default-dates-form'; -import { UserLoginReportTable, UserLoginRowsType } from './user-login-report-table'; - -import { ReportConfigProps } from '../utils'; -export interface UserLoginReportProps extends ReportConfigProps { - getLogs: (data: DefaultReportQueryPayload) => Promise; -} - -export const UserLoginReport = (props: UserLoginReportProps): React.ReactElement => { - const { getLogs, ...otherProps } = props; - const [logs, setLogs] = React.useState([]); - const [lastSearchParams, setLastSearchParams] = React.useState({}); - - const searchLogs = async (payload: DefaultReportQueryPayload) => { - setLastSearchParams(payload); - setLogs(await getLogs(payload)); - }; - - const getMoreLogs = async () => { - setLogs(logs.concat(await getLogs({ ...lastSearchParams, offset: logs.length }))); - }; - - return ( - - -
- {logs.length !== 0 && ( - - )} -
-
- ); -}; - -export default UserLoginReport; diff --git a/packages/react-components/lib/reports/user-report/user-logout-report-table.tsx b/packages/react-components/lib/reports/user-report/user-logout-report-table.tsx deleted file mode 100644 index cc96e062f..000000000 --- a/packages/react-components/lib/reports/user-report/user-logout-report-table.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import React from 'react'; -import { DataGrid, GridRenderCellParams } from '@mui/x-data-grid'; -import { Typography } from '@mui/material'; -import { DefaultLogTableProps } from '../default-report-interface'; -import { format } from 'date-fns'; - -export type UserLogoutRowsType = { - created: string; //date - ip_address: string; - user_id: string; - username: string; -}[]; - -export interface UserLogoutReportTable extends DefaultLogTableProps { - rows: UserLogoutRowsType; -} - -export const UserLogoutReportTable = (props: UserLogoutReportTable): React.ReactElement => { - const { rows, addMoreRows } = props; - - return ( -
- r.user_id} - columns={[ - { - headerName: 'Username', - field: 'username', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.username}; - }, - }, - { - headerName: 'Timestamp', - field: 'created', - type: 'datetime', - filterable: false, - align: 'center', - renderCell: (rowData: GridRenderCellParams) => { - return ( - - {format(new Date(rowData.value as number), 'MMM dd yyyy hh:mm aaa')} - - ); - }, - }, - { - headerName: 'User ID', - field: 'user_id', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.user_id}; - }, - }, - { - headerName: 'IP Addr', - field: 'ip_address', - type: 'string', - renderCell: (rowData: GridRenderCellParams) => { - return {rowData.row.ip_address}; - }, - }, - ]} - rows={rows} - pageSize={100} - rowsPerPageOptions={[50, 100]} - onPageChange={() => { - if (addMoreRows) { - addMoreRows(); - } - }} - disableColumnMenu={true} - /> -
- ); -}; diff --git a/packages/react-components/lib/reports/user-report/user-logout-report.tsx b/packages/react-components/lib/reports/user-report/user-logout-report.tsx deleted file mode 100644 index 05198e7f9..000000000 --- a/packages/react-components/lib/reports/user-report/user-logout-report.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import React from 'react'; -import { - DefaultReportQueryPayload, - defaultReportClasses, - DefaultReportContainer, -} from '../default-report-interface'; -import { DefaultDatesForm } from '../default-dates-form'; -import { UserLogoutReportTable, UserLogoutRowsType } from './user-logout-report-table'; -import { ReportConfigProps } from '../utils'; - -export interface UserLogoutReportProps extends ReportConfigProps { - getLogs: (data: DefaultReportQueryPayload) => Promise; -} - -export const UserLogoutReport = (props: UserLogoutReportProps): React.ReactElement => { - const { getLogs, ...otherProps } = props; - const [logs, setLogs] = React.useState([]); - const [lastSearchParams, setLastSearchParams] = React.useState({}); - - const searchLogs = async (payload: DefaultReportQueryPayload) => { - setLastSearchParams(payload); - setLogs(await getLogs(payload)); - }; - - const getMoreLogs = async () => { - setLogs(logs.concat(await getLogs({ ...lastSearchParams, offset: logs.length }))); - }; - - return ( - - -
- {logs.length !== 0 && ( - - )} -
-
- ); -}; - -export default UserLogoutReport; diff --git a/packages/react-components/lib/reports/utils.spec.ts b/packages/react-components/lib/reports/utils.spec.ts deleted file mode 100644 index d3ac72ada..000000000 --- a/packages/react-components/lib/reports/utils.spec.ts +++ /dev/null @@ -1,138 +0,0 @@ -import { - DispenserStateRowsType, - DoorStateRowsType, - FleetStateRowsType, - HealthRowsType, - IngestorStateRowsType, - LiftStateRowsType, - TaskSummaryRowsType, -} from '.'; -import { ReportConfigProps } from './utils'; - -const timestamp = new Date('Mon Jan 1 00:00:02 UTC 2001').toISOString(); - -export const getDispenserLogs = (): DispenserStateRowsType => { - const rows = []; - for (let i = 0; i < 200; i++) { - rows.push({ - state: 'OPEN', - guid: `dispenser_test_${i}`, - created: timestamp, - }); - } - return rows; -}; - -export const getDoorLogs = (): DoorStateRowsType => { - const rows = []; - for (let i = 0; i < 200; i++) { - rows.push({ - created: timestamp, - state: 'OPEN', - door: { id: i, name: 'door_test' }, - }); - } - return rows; -}; - -export const getFleetLogs = (): FleetStateRowsType => { - const rows: FleetStateRowsType = []; - for (let i = 0; i < 200; i++) { - rows.push({ - created: timestamp, - fleet: { id: 1, name: 'fleet_test' }, - robot: { id: i, name: 'robot_test', model: 'model' }, - robot_battery_percent: 'test', - robot_location: 'test', - robot_mode: 'test', - robot_seq: 1, - robot_task_id: 'test', - }); - } - return rows as FleetStateRowsType; -}; - -export const getHealthLogs = (): HealthRowsType => { - const rows = []; - for (let i = 0; i < 200; i++) { - rows.push({ - device: { id: i, type: 'door', actor: 'door-1' }, - health_status: 'DEAD', - health_message: 'this is a message', - created: timestamp, - }); - } - return rows; -}; - -export const getIngestorLogs = (): IngestorStateRowsType => - getDispenserLogs() as IngestorStateRowsType; - -export const getLiftLogs = (): LiftStateRowsType => { - const rows = []; - for (let i = 0; i < 200; i++) { - rows.push({ - state: 'AVG', - door_state: 'Closed', - destination_floor: 'L1', - motion_state: 'DOWN', - current_floor: 'L2', - session_id: 'session', - created: timestamp, - lift: { id: i, name: `lift_${i}` }, - }); - } - return rows; -}; - -export const getTaskSummaryLogs = (): TaskSummaryRowsType => { - const exampleData = { - task_id: 'test', - submission_time: { sec: 131, nanosec: 553000000 }, - description: { - start_time: { sec: 1623383402, nanosec: 0 }, - priority: { value: 0 }, - task_type: { type: 1 }, - station: { task_id: '', robot_type: '', place_name: '' }, - loop: { task_id: '', robot_type: '', num_loops: 1, start_name: '', finish_name: '' }, - delivery: { - task_id: '1', - items: [], - pickup_place_name: '', - pickup_dispenser: '', - pickup_behavior: { name: '', parameters: [] }, - dropoff_place_name: '', - dropoff_ingestor: '', - dropoff_behavior: { name: '', parameters: [] }, - }, - clean: { start_waypoint: '' }, - }, - }; - const rows = []; - for (let i = 0; i < 200; i++) { - rows.push({ - created: timestamp, - fleet: { id: 1, name: 'fleet_test' }, - robot: { id: i, name: 'robot_test', model: 'model' }, - task_id: i.toString(), - task_profile: exampleData, - state: 'test', - status: 'test', - submission_time: { sec: 131, nanosec: 553000000 }, - start_time: { sec: 131, nanosec: 553000000 }, - end_time: { sec: 131, nanosec: 553000000 }, - }); - } - return rows; -}; - -export const reportConfigProps: ReportConfigProps = { - toLogDate: new Date(), - fromLogDate: new Date(), - onSelectFromDate: (/* date: Date */) => { - /* no-op */ - }, - onSelectToDate: (/* date: Date */) => { - /* no-op */ - }, -}; diff --git a/packages/react-components/lib/reports/utils.ts b/packages/react-components/lib/reports/utils.ts deleted file mode 100644 index 1defe236c..000000000 --- a/packages/react-components/lib/reports/utils.ts +++ /dev/null @@ -1,6 +0,0 @@ -export interface ReportConfigProps { - fromLogDate?: Date; - toLogDate?: Date; - onSelectFromDate?: (date: unknown) => void; - onSelectToDate?: (date: unknown) => void; -} diff --git a/packages/react-components/lib/tasks/utils.ts b/packages/react-components/lib/tasks/utils.ts index d303b487a..dd1f00363 100644 --- a/packages/react-components/lib/tasks/utils.ts +++ b/packages/react-components/lib/tasks/utils.ts @@ -33,7 +33,10 @@ function parsePhaseDetail(phases: TaskState['phases'], category?: string) { return {}; } -export function parseTaskDetail(task: TaskState, category?: string) { +export function parseTaskDetail( + task: TaskState, + category?: string, +): { to: string; from: string } | {} { if (category?.includes('Loop')) return parsePhaseDetail(task.phases, category); if (category?.includes('Delivery')) { const from = category?.split('[place:')[1].split(']')[0]; diff --git a/packages/reporting-e2e/.eslintrc.js b/packages/reporting-e2e/.eslintrc.js deleted file mode 100644 index 933322cd0..000000000 --- a/packages/reporting-e2e/.eslintrc.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = { - extends: ['../../.eslintrc.js'], - globals: { - browser: 'readonly', - }, -}; diff --git a/packages/reporting-e2e/.gitignore b/packages/reporting-e2e/.gitignore deleted file mode 100644 index 15105c597..000000000 --- a/packages/reporting-e2e/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/static/ -/build -/artifacts \ No newline at end of file diff --git a/packages/reporting-e2e/README.md b/packages/reporting-e2e/README.md deleted file mode 100644 index df011cc28..000000000 --- a/packages/reporting-e2e/README.md +++ /dev/null @@ -1,77 +0,0 @@ -# Running with Keycloak - -The tests are run with keycloak as the authentication server and we use docker and docker-compose to simplify the setup of keycloak. - -## Ubuntu 20.04 - -Install docker and docker-compose -```bash -sudo apt update && sudo apt install docker.io docker-compose -``` - -If you're using GNOME or KDE, there will be a pop-up window to ask for privilege escalation when running the Docker container which runs Keycloak, the authentication mechanism we are currently using. -If you're using `i3` or other "unusual" window managers, this pop-up may not occur, which can be confusing since a password prompt can be easily lost in the console text stream. -You can add yourself to the `docker` group to allow containers to start without requesting your password: -``` -sudo usermod -aG docker $USER -``` -After issuing this command, you may need to logout/login or restart your system depending on your OS/environment. - -Keep in mind that this convenience has security implications. This tradeoff is described in more detail in the Docker documentation: -https://docs.docker.com/engine/install/linux-postinstall/#manage-docker-as-a-non-root-user - -## Others - -See - -* [docker](https://docs.docker.com/engine/install/ubuntu/) -* [docker-compose](https://docs.docker.com/compose/install/) - -# Settings - -There are some environment variables that control how the test runs. - -| Key | Description | -|---|---| -| E2E_DOCKER_NETWORK | _(string)_ The network that services uses, defaults to `rmf-web_default` | -| E2E_NO_AUTH | _(bool)_ Do not launch the authentication provider service | -| E2E_NO_REPORTING | _(bool)_ Do not launch the reporting ui server | -| E2E_NO_REPORTING_SERVER | _(bool)_ Do not launch the reporting server | -| E2E_USER | _(string)_ The user to login with | -| E2E_PASSWORD | _(string)_ The password to login with | -| E2E_REPORTING_URL | _(string)_ Base url where the dashboard is hosted | - -Boolean values can be 0/1/true/false. - -There are also some environment variables the test runner sets by default - -| Key | Default Value | -|---|---| -| REACT_APP_AUTH_PROVIDER | keycloak | -| REACT_APP_KEYCLOAK_CONFIG | { "realm": "master", "clientId": "reporting", "url": "http://localhost:8088/auth" } | -| E2E_USER | admin | -| E2E_PASSWORD | admin | -| E2E_REPORTING_URL | http://localhost:5000 | - -You can overwrite them by setting them in your environment variables. - -# E2E workflow in CI - -NOTE: This section only pertains to running the e2e tests in github workflows. - -This is to document the flow and interaction of the e2e services in the github environment when: - -- starting up the services -- running the tests - -## Starting up the services - -Below is a diagram representing the flow of commands when running `npm run test` - -![Flow of commands diagram](docs/resources/reporting-e2e-process.png) - -## Container and network interactions - -The key difference between running the tests locally and in github workflows is that in github, the tests are ran from inside a container with docker-beside-docker. So, the docker commands connect to the host daemon, this causes that the "localhost" in GitHub refers to the container. "Localhost" in local runs refers to the host (where the docker daemon is running). Tests in CI runs wouldn't be able to connect to the auth service with "localhost" because when a port is "exposed" or "published" is mapped to the host and not the container. - -Instead of host <-> container communication, we will need to do container <-> container communication. This is achieved by setting `E2E_DOCKER_NETWORK` to the github workflow's network and setting `REACT_APP_KEYCLOAK_CONFIG` to point to the auth service via the container name. \ No newline at end of file diff --git a/packages/reporting-e2e/docs/resources/reporting-e2e-process.png b/packages/reporting-e2e/docs/resources/reporting-e2e-process.png deleted file mode 100644 index 2485a361f..000000000 Binary files a/packages/reporting-e2e/docs/resources/reporting-e2e-process.png and /dev/null differ diff --git a/packages/reporting-e2e/package.json b/packages/reporting-e2e/package.json deleted file mode 100644 index a78bb848d..000000000 --- a/packages/reporting-e2e/package.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "name": "reporting-e2e", - "description": "", - "main": "index.js", - "directories": { - "test": "tests" - }, - "scripts": { - "start:reporting-server": "scripts/start-reporting-server.sh", - "test": "node scripts/test-e2e.js", - "test:dev": "E2E_REPORTING_URL=http://localhost:3000 RMF_LAUNCH_MODE=none wdio" - }, - "devDependencies": { - "@types/mocha": "^9.0.0", - "@wdio/cli": "7.11.1", - "@wdio/local-runner": "7.11.1", - "@wdio/mocha-framework": "7.11.1", - "@wdio/spec-reporter": "7.10.1", - "concurrently": "^5.3.0", - "node-fetch": "^2.6.1", - "reporting": "file:../reporting", - "serve": "^11.3.2", - "ts-node": "^9.1.1", - "typescript": "~4.4.4" - } -} diff --git a/packages/reporting-e2e/scripts/start-reporting-server.sh b/packages/reporting-e2e/scripts/start-reporting-server.sh deleted file mode 100755 index 8b572ef6a..000000000 --- a/packages/reporting-e2e/scripts/start-reporting-server.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash -set -e; - -export PIPENV_PIPFILE=../../Pipfile - -pipenv run reporting_server diff --git a/packages/reporting-e2e/scripts/test-e2e.js b/packages/reporting-e2e/scripts/test-e2e.js deleted file mode 100644 index cfc3b06f6..000000000 --- a/packages/reporting-e2e/scripts/test-e2e.js +++ /dev/null @@ -1,48 +0,0 @@ -const concurrently = require('concurrently'); -const { execSync } = require('child_process'); - -process.env.BUILD_PATH = process.env.BUILD_PATH || '../reporting-e2e/build'; -process.env.REACT_APP_REPORTING_SERVER = - process.env.REACT_APP_REPORTING_SERVER || 'http://localhost:8002'; -process.env.E2E_REPORTING_SERVER = process.env.E2E_REPORTING_SERVER || 'http://localhost:8003'; -process.env.E2E_REPORTING_URL = process.env.E2E_REPORTING_URL || 'http://localhost:5000'; - -execSync('npm --prefix ../reporting run build', { stdio: 'inherit' }); - -// wrap in double quotes to support args with spaces -const wdioArgs = process.argv - .slice(2) - .map((arg) => `"${arg}"`) - .join(' '); - -const services = []; - -// eslint-disable-next-line no-eval -if (!eval(process.env.E2E_NO_REPORTING)) { - services.push('serve build'); -} -// eslint-disable-next-line no-eval -if (!eval(process.env.E2E_NO_REPORTING_SERVER)) { - services.push('npm run start:reporting-server'); -} - -concurrently([...services, `wdio ${wdioArgs}`], { - killOthers: ['success', 'failure'], - successCondition: 'first', -}) - .then( - function onSuccess(/* exitInfo */) { - // This code is necessary to make sure the parent terminates - // when the application is closed successfully. - process.exit(); - }, - function onFailure(/* exitInfo */) { - // This code is necessary to make sure the parent terminates - // when the application is closed because of a failure. - process.exit(); - }, - ) - .catch((e) => { - console.error(e); - process.exitCode = -1; - }); diff --git a/packages/reporting-e2e/tests/1-logs.test.ts b/packages/reporting-e2e/tests/1-logs.test.ts deleted file mode 100644 index c90ad0f26..000000000 --- a/packages/reporting-e2e/tests/1-logs.test.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { rmfData } from './mock-data'; -import fetch from 'node-fetch'; - -describe('logs', () => { - it('should store the logs correctly', async () => { - const options = { - method: 'POST', - body: JSON.stringify(rmfData), - headers: { - 'Content-Type': 'application/json', - }, - }; - - let response; - try { - const res = await fetch(`http://localhost:8003/log/rmfserver`, options); - response = await res.text(); - } catch (error) { - console.log(error); - } - - expect(response).toBe('"Logs were saved correctly"'); - }); -}); diff --git a/packages/reporting-e2e/tests/2-reporting.test.ts b/packages/reporting-e2e/tests/2-reporting.test.ts deleted file mode 100644 index 71a2b7053..000000000 --- a/packages/reporting-e2e/tests/2-reporting.test.ts +++ /dev/null @@ -1,95 +0,0 @@ -import fetch from 'node-fetch'; -import { rmfData } from './mock-data'; -import { getReport, overwriteClick } from './utils'; - -describe('reporting interactions', () => { - before(() => overwriteClick()); - before(() => browser.url('/')); - - before(async () => { - const options = { - method: 'POST', - body: JSON.stringify(rmfData), - headers: { - 'Content-Type': 'application/json', - }, - }; - - try { - const res = await fetch(`http://localhost:8003/log/rmfserver`, options); - await res.text(); - } catch (error) { - console.log(error); - } - }); - - it('should retrieve dispenser state report', async () => { - const options = { - listOrder: 2, - elemName: 'div*=Dispensers', - reportTitle: 'h6*=Dispenser State', - }; - await getReport(options); - await expect($('h6*=Dispenser State')).toBeDisplayed(); - }); - - it('should retrieve door state report', async () => { - const options = { - listOrder: 3, - elemName: 'div*=Doors', - reportTitle: 'h6*=Door State', - }; - await getReport(options); - await expect($('h6*=Door State')).toBeDisplayed(); - }); - - it('should retrieve the fleet state report', async () => { - const options = { - listOrder: 4, - elemName: 'div*=Fleets', - reportTitle: 'h6*=Fleet State', - }; - await getReport(options); - await expect($('h6*=Fleet State')).toBeDisplayed(); - }); - - it('should retrieve the health report', async () => { - const options = { - listOrder: 5, - elemName: 'div*=Health', - reportTitle: 'h6*=Health', - }; - await getReport(options); - await expect($('h6*=Health')).toBeDisplayed(); - }); - - it('should retrieve ingestor state report', async () => { - const options = { - listOrder: 6, - elemName: 'div*=Ingestor', - reportTitle: 'h6*=Ingestor State', - }; - await getReport(options); - await expect($('h6*=Ingestor State')).toBeDisplayed(); - }); - - it('should retrieve lift state report', async () => { - const options = { - listOrder: 7, - elemName: 'div*=Lifts', - reportTitle: 'h6*=Lift State', - }; - await getReport(options); - await expect($('h6*=Lift State')).toBeDisplayed(); - }); - - it('should retrieve the task summary report', async () => { - const options = { - listOrder: 8, - elemName: 'div*=Tasks', - reportTitle: 'h6*=Task', - }; - await getReport(options); - await expect($('h6*=Task')).toBeDisplayed(); - }); -}); diff --git a/packages/reporting-e2e/tests/mock-data.ts b/packages/reporting-e2e/tests/mock-data.ts deleted file mode 100644 index 940b4a304..000000000 --- a/packages/reporting-e2e/tests/mock-data.ts +++ /dev/null @@ -1,147 +0,0 @@ -export const rmfData = [ - { - log: - 'INFO:app.BookKeeper.dispenser_state:{"time": {"sec": 1600, "nanosec": 0}, "guid": "coke_dispenser", "mode": 0, "request_guid_queue": [], "seconds_remaining": 0.0}\n', - stream: 'stdout', - kubernetes: { - container_name: 'app-that-writes-logs', - namespace_name: 'default', - pod_name: 'app-that-writes-logs', - container_image: 'busybox:latest', - container_image_id: - 'docker-pullable://busybox@sha256:ae39a6f5c07297d7ab64dbd4f82c77c874cc6a94cea29fdec309d0992574b4f7', - pod_id: '978761c6-2a19-422f-b710-d43da2348f1f', - host: 'minikube', - master_url: 'https://10.96.0.1:443/api', - namespace_id: 'e192acd4-e6e7-46c2-8514-44a27a367749', - }, - }, - { - log: - 'INFO:app.BookKeeper.door_state:{"door_time": {"sec": 1596, "nanosec": 548000000}, "door_name": "hardware_door", "current_mode": {"value": 0}}\n', - stream: 'stdout', - }, - { - log: - 'INFO:app.BookKeeper.fleet_state:{"name": "tinyRobot", "robots": [{"name": "tinyRobot1", "model": "", "task_id": "", "seq": 3194, "mode": {"mode": 1, "mode_request_id": 0}, "battery_percent": 100.0, "location": {"t": {"sec": 1600, "nanosec": 189000000}, "x": 11.55367374420166, "y": -11.317498207092285, "yaw": -1.5998055934906006, "level_name": "L1", "index": 0}, "path": []}, {"name": "tinyRobot2", "model": "", "task_id": "", "seq": 3194, "mode": {"mode": 1, "mode_request_id": 0}, "battery_percent": 100.0, "location": {"t": {"sec": 1600, "nanosec": 189000000}, "x": 15.15751838684082, "y": -11.22861385345459, "yaw": -1.5839799642562866, "level_name": "L1", "index": 0}, "path": []}]}\n', - stream: 'stdout', - }, - { - log: - 'INFO:app.BookKeeper.door_health:{"id": "hardware_door", "health_status": "HealthStatus.HEALTHY", "health_message": null}\n', - stream: 'stdout', - }, - { - log: - 'INFO:app.BookKeeper.ingestor_state:{"time": {"sec": 1600, "nanosec": 0}, "guid": "coke_ingestor", "mode": 0, "request_guid_queue": [], "seconds_remaining": 0.0}\n', - stream: 'stdout', - }, - { - log: - 'INFO:app.BookKeeper.lift_state: {"lift_name": "test_lift", "lift_time": 0, "available_floors": ["L1", "L2"], "current_floor": "L1", "destination_floor": "L2", "door_state": 0, "motion_state": 0, "available_modes": [0], "current_mode": 0, "session_id": "test_session"}\n', - stream: 'stdout', - }, - { - log: - 'INFO:app.BookKeeper.task_summary:{"fleet_name": "tinyRobot", "task_id": "Loop0", "task_profile": {"task_id": "Loop0", "submission_time": {"sec": 131, "nanosec": 553000000}, "description": {"start_time": {"sec": 1623383402, "nanosec": 0}, "priority": {"value": 0}, "task_type": {"type": 1}, "station": {"task_id": "", "robot_type": "", "place_name": ""}, "loop": {"task_id": "", "robot_type": "", "num_loops": 1, "start_name": "supplies", "finish_name": "coe"}, "delivery": {"task_id": "", "items": [], "pickup_place_name": "", "pickup_dispenser": "", "pickup_behavior": {"name": "", "parameters": []}, "dropoff_place_name": "", "dropoff_ingestor": "", "dropoff_behavior": {"name": "", "parameters": []}}, "clean": {"start_waypoint": ""}}}, "state": 0, "status": "test_status", "submission_time": {"sec": 0, "nanosec": 0}, "start_time": {"sec": 1623383362, "nanosec": 348338289}, "end_time": {"sec": 1623383449, "nanosec": 79154833}, "robot_name": "tinyRobot2"}', - stream: 'stdout', - }, - { - log: - 'INFO:app.BookKeeper.task_summary:{"fleet_name": "tinyRobot", "task_id": "Delivery1", "task_profile": {"task_id": "Delivery1", "submission_time": {"sec": 132, "nanosec": 553000098}, "description": {"start_time": {"sec": 1623383487, "nanosec": 0}, "priority": {"value": 0}, "task_type": {"type": 2}, "station": {"task_id": "", "robot_type": "", "place_name": ""}, "loop": {"task_id": "", "robot_type": "", "num_loops": "", "start_name": "", "finish_name": ""}, "delivery": {"task_id": "", "items": ["item1", "item2"], "pickup_place_name": "pantry", "pickup_dispenser": "coke_dispenser", "pickup_behavior": {"name": "", "parameters": []}, "dropoff_place_name": "lounge", "dropoff_ingestor": "ingestor", "dropoff_behavior": {"name": "", "parameters": []}}, "clean": {"start_waypoint": ""}}}, "state": 0, "status": "test_status2", "submission_time": {"sec": 0, "nanosec": 0}, "start_time": {"sec": 1623383362, "nanosec": 348338289}, "end_time": {"sec": 1623383449, "nanosec": 79154833}, "robot_name": "tinyRobot1"}', - stream: 'stdout', - }, - { - log: - 'INFO:app.BookKeeper.task_summary:{"fleet_name": "tinyRobot", "task_id": "Clean2", "task_profile": {"task_id": "Clean2", "submission_time": {"sec": 131, "nanosec": 552120070}, "description": {"start_time": {"sec": 145383402, "nanosec": 0}, "priority": {"value": 0}, "task_type": {"type": 4}, "station": {"task_id": "", "robot_type": "", "place_name": ""}, "loop": {"task_id": "", "robot_type": "", "num_loops": "", "start_name": "", "finish_name": ""}, "delivery": {"task_id": "", "items": [], "pickup_place_name": "", "pickup_dispenser": "", "pickup_behavior": {"name": "", "parameters": []}, "dropoff_place_name": "", "dropoff_ingestor": "", "dropoff_behavior": {"name": "", "parameters": []}}, "clean": {"start_waypoint": "cleanzone"}}}, "state": 0, "status": "test_status3", "submission_time": {"sec": 0, "nanosec": 0}, "start_time": {"sec": 1623283162, "nanosec": 348332939}, "end_time": {"sec": 162593449, "nanosec": 79154833}, "robot_name": "tinyRobot3"}', - stream: 'stdout', - }, -]; - -export const dispenserStateData = [ - { - log: - 'INFO:app.BookKeeper.dispenser_state:{"time": {"sec": 1600, "nanosec": 0}, "guid": "coke_dispenser", "mode": 0, "request_guid_queue": [], "seconds_remaining": 0.0}\n', - stream: 'stdout', - kubernetes: { - container_name: 'app-that-writes-logs', - namespace_name: 'default', - pod_name: 'app-that-writes-logs', - container_image: 'busybox:latest', - container_image_id: - 'docker-pullable://busybox@sha256:ae39a6f5c07297d7ab64dbd4f82c77c874cc6a94cea29fdec309d0992574b4f7', - pod_id: '978761c6-2a19-422f-b710-d43da2348f1f', - host: 'minikube', - master_url: 'https://10.96.0.1:443/api', - namespace_id: 'e192acd4-e6e7-46c2-8514-44a27a367749', - }, - }, -]; - -export const doorStateData = [ - { - log: - 'INFO:app.BookKeeper.door_state:{"door_time": {"sec": 1596, "nanosec": 548000000}, "door_name": "hardware_door", "current_mode": {"value": 0}}\n', - stream: 'stdout', - }, -]; - -export const fleetStateData = [ - { - log: - 'INFO:app.BookKeeper.fleet_state:{"name": "tinyRobot", "robots": [{"name": "tinyRobot1", "model": "", "task_id": "", "seq": 3194, "mode": {"mode": 1, "mode_request_id": 0}, "battery_percent": 100.0, "location": {"t": {"sec": 1600, "nanosec": 189000000}, "x": 11.55367374420166, "y": -11.317498207092285, "yaw": -1.5998055934906006, "level_name": "L1", "index": 0}, "path": []}, {"name": "tinyRobot2", "model": "", "task_id": "", "seq": 3194, "mode": {"mode": 1, "mode_request_id": 0}, "battery_percent": 100.0, "location": {"t": {"sec": 1600, "nanosec": 189000000}, "x": 15.15751838684082, "y": -11.22861385345459, "yaw": -1.5839799642562866, "level_name": "L1", "index": 0}, "path": []}]}\n', - stream: 'stdout', - }, -]; - -export const healthData = [ - { - log: - 'INFO:app.BookKeeper.door_health:{"id": "hardware_door", "health_status": "HealthStatus.HEALTHY", "health_message": null}\n', - stream: 'stdout', - }, -]; - -export const ingestorData = [ - { - log: - 'INFO:app.BookKeeper.ingestor_state:{"time": {"sec": 1600, "nanosec": 0}, "guid": "coke_ingestor", "mode": 0, "request_guid_queue": [], "seconds_remaining": 0.0}\n', - stream: 'stdout', - }, -]; - -export const liftsStateData = [ - { - log: - 'INFO:app.BookKeeper.lift_state: {"lift_name": "test_lift", "lift_time": 0, "available_floors": ["L1", "L2"], "current_floor": "L1", "destination_floor": "L2", "door_state": 0, "motion_state": 0, "available_modes": [0], "current_mode": 0, "session_id": "test_session"}\n', - stream: 'stdout', - }, -]; - -export const taskSummaryData = [ - { - log: - 'INFO:app.BookKeeper.task_summary:{"fleet_name": "tinyRobot", "task_id": "Loop0", "task_profile": {"task_id": "Loop0", "submission_time": {"sec": 131, "nanosec": 553000000}, "description": {"start_time": {"sec": 1623383402, "nanosec": 0}, "priority": {"value": 0}, "task_type": {"type": 1}, "station": {"task_id": "", "robot_type": "", "place_name": ""}, "loop": {"task_id": "", "robot_type": "", "num_loops": 1, "start_name": "supplies", "finish_name": "coe"}, "delivery": {"task_id": "", "items": [], "pickup_place_name": "", "pickup_dispenser": "", "pickup_behavior": {"name": "", "parameters": []}, "dropoff_place_name": "", "dropoff_ingestor": "", "dropoff_behavior": {"name": "", "parameters": []}}, "clean": {"start_waypoint": ""}}}, "state": 0, "status": "test_status", "submission_time": {"sec": 0, "nanosec": 0}, "start_time": {"sec": 1623383362, "nanosec": 348338289}, "end_time": {"sec": 1623383449, "nanosec": 79154833}, "robot_name": "tinyRobot2"}', - stream: 'stdout', - }, - { - log: - 'INFO:app.BookKeeper.task_summary:{"fleet_name": "tinyRobot", "task_id": "Delivery1", "task_profile": {"task_id": "Delivery1", "submission_time": {"sec": 132, "nanosec": 553000098}, "description": {"start_time": {"sec": 1623383487, "nanosec": 0}, "priority": {"value": 0}, "task_type": {"type": 2}, "station": {"task_id": "", "robot_type": "", "place_name": ""}, "loop": {"task_id": "", "robot_type": "", "num_loops": "", "start_name": "", "finish_name": ""}, "delivery": {"task_id": "", "items": ["item1", "item2"], "pickup_place_name": "pantry", "pickup_dispenser": "coke_dispenser", "pickup_behavior": {"name": "", "parameters": []}, "dropoff_place_name": "lounge", "dropoff_ingestor": "ingestor", "dropoff_behavior": {"name": "", "parameters": []}}, "clean": {"start_waypoint": ""}}}, "state": 0, "status": "test_status2", "submission_time": {"sec": 0, "nanosec": 0}, "start_time": {"sec": 1623383362, "nanosec": 348338289}, "end_time": {"sec": 1623383449, "nanosec": 79154833}, "robot_name": "tinyRobot1"}', - stream: 'stdout', - }, - { - log: - 'INFO:app.BookKeeper.task_summary:{"fleet_name": "tinyRobot", "task_id": "Clean2", "task_profile": {"task_id": "Clean2", "submission_time": {"sec": 131, "nanosec": 552120070}, "description": {"start_time": {"sec": 145383402, "nanosec": 0}, "priority": {"value": 0}, "task_type": {"type": 4}, "station": {"task_id": "", "robot_type": "", "place_name": ""}, "loop": {"task_id": "", "robot_type": "", "num_loops": "", "start_name": "", "finish_name": ""}, "delivery": {"task_id": "", "items": [], "pickup_place_name": "", "pickup_dispenser": "", "pickup_behavior": {"name": "", "parameters": []}, "dropoff_place_name": "", "dropoff_ingestor": "", "dropoff_behavior": {"name": "", "parameters": []}}, "clean": {"start_waypoint": "cleanzone"}}}, "state": 0, "status": "test_status3", "submission_time": {"sec": 0, "nanosec": 0}, "start_time": {"sec": 1623283162, "nanosec": 348332939}, "end_time": {"sec": 162593449, "nanosec": 79154833}, "robot_name": "tinyRobot3"}', - stream: 'stdout', - }, -]; - -export const singleLog = [ - { - log: - 'INFO:app.BookKeeper.task_summary:{"fleet_name": "tinyRobot", "task_id": "Clean2", "task_profile": {"task_id": "Clean2", "submission_time": {"sec": 131, "nanosec": 552120070}, "description": {"start_time": {"sec": 145383402, "nanosec": 0}, "priority": {"value": 0}, "task_type": {"type": 4}, "station": {"task_id": "", "robot_type": "", "place_name": ""}, "loop": {"task_id": "", "robot_type": "", "num_loops": "", "start_name": "", "finish_name": ""}, "delivery": {"task_id": "", "items": [], "pickup_place_name": "", "pickup_dispenser": "", "pickup_behavior": {"name": "", "parameters": []}, "dropoff_place_name": "", "dropoff_ingestor": "", "dropoff_behavior": {"name": "", "parameters": []}}, "clean": {"start_waypoint": "cleanzone"}}}, "state": 0, "status": "test_status3", "submission_time": {"sec": 0, "nanosec": 0}, "start_time": {"sec": 1623283162, "nanosec": 348332939}, "end_time": {"sec": 162593449, "nanosec": 79154833}, "robot_name": "tinyRobot3"}', - stream: 'stdout', - }, -]; - -export const userData = []; diff --git a/packages/reporting-e2e/tests/utils.ts b/packages/reporting-e2e/tests/utils.ts deleted file mode 100644 index 00eaec060..000000000 --- a/packages/reporting-e2e/tests/utils.ts +++ /dev/null @@ -1,85 +0,0 @@ -import fetch from 'node-fetch'; - -/** - * Overwrites the default click command to wait for animation to finish before attempting to click, - * this can fix flaky tests where the click is missed as the position changes as the animation is - * running. - */ -export function overwriteClick(): void { - browser.overwriteCommand( - 'click', - async function (this: WebdriverIO.Element, origClick) { - await this.waitForClickable(); - let prevLocation = await this.getLocation(); - await this.waitUntil(async () => { - const newLocation = await this.getLocation(); - const stablized = prevLocation.x === newLocation.x && prevLocation.y === newLocation.y; - prevLocation = newLocation; - return stablized; - }); - return origClick(); - }, - true, - ); -} - -/** - * Return a list of backspace characters. This is only used in case we want to delete characters from the Autocomplete material-ui component - */ -export function removeTextFromAutocomplete(characterNum: number): string { - const backspace = '\u0008'; - let backspaces = ''; - for (let index = 0; index < characterNum; index++) { - backspaces += backspace; - } - return backspaces; -} - -export const populateDatabase = async ( - data: { log: string; stream: string }[], -): Promise => { - const options = { - method: 'POST', - body: JSON.stringify(data), - headers: { - 'Content-Type': 'application/json', - }, - }; - - try { - const res = await fetch('http:localhost:8003/log/rmfserver', options); - return await res.json(); - } catch (error) { - return error as Error; - } -}; - -export const selectDateAndRetrieveLogs = async (): Promise => { - const datePickerIconButton = await (await $('.MuiInputBase-root')).$('.MuiIconButton-root'); - await datePickerIconButton.click(); - const prevMonthButton = (await $$('.MuiPickersCalendarHeader-iconButton'))[0]; - await prevMonthButton.click(); - const dayOneButton = await $('button=1'); - await dayOneButton.click(); - await (await $('body')).click(); - await (await $('button=Retrieve Logs')).click(); - await browser.pause(1000); -}; - -export const getReport = async (options: { - listOrder: number; - elemName: string; - reportTitle: string; -}): Promise => { - await browser.waitUntil( - async () => (await (await $('.MuiList-root')).waitForDisplayed()) === true, - ); - const targetButton = await ( - await $(`.MuiList-root .MuiListItem-root:nth-child(${options.listOrder})`) - ).$(options.elemName); - await targetButton.click(); - await selectDateAndRetrieveLogs(); - await browser.waitUntil( - async () => (await (await $(options.reportTitle)).waitForDisplayed({ timeout: 5000 })) === true, - ); -}; diff --git a/packages/reporting-e2e/tsconfig.json b/packages/reporting-e2e/tsconfig.json deleted file mode 100644 index c77ffd695..000000000 --- a/packages/reporting-e2e/tsconfig.json +++ /dev/null @@ -1,66 +0,0 @@ -{ - "compilerOptions": { - /* Basic Options */ - // "incremental": true, /* Enable incremental compilation */ - "target": "es2019", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ - "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ - // "lib": [], /* Specify library files to be included in the compilation. */ - // "allowJs": true, /* Allow javascript files to be compiled. */ - // "checkJs": true, /* Report errors in .js files. */ - // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ - // "declaration": true, /* Generates corresponding '.d.ts' file. */ - // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ - // "sourceMap": true, /* Generates corresponding '.map' file. */ - // "outFile": "./", /* Concatenate and emit output to single file. */ - // "outDir": "./", /* Redirect output structure to the directory. */ - // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ - // "composite": true, /* Enable project compilation */ - // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ - // "removeComments": true, /* Do not emit comments to output. */ - // "noEmit": true, /* Do not emit outputs. */ - // "importHelpers": true, /* Import emit helpers from 'tslib'. */ - // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ - // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ - - /* Strict Type-Checking Options */ - "strict": true, /* Enable all strict type-checking options. */ - // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ - // "strictNullChecks": true, /* Enable strict null checks. */ - // "strictFunctionTypes": true, /* Enable strict checking of function types. */ - // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ - // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ - // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ - // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ - - /* Additional Checks */ - // "noUnusedLocals": true, /* Report errors on unused locals. */ - // "noUnusedParameters": true, /* Report errors on unused parameters. */ - // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ - // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ - - /* Module Resolution Options */ - // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ - // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ - // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ - // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ - // "typeRoots": [], /* List of folders to include type definitions from. */ - "types": ["node", "webdriverio/async", "@wdio/mocha-framework", "expect-webdriverio"], /* Type declaration files to be included in compilation. */ - // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ - "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ - // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ - // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ - - /* Source Map Options */ - // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ - // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ - // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ - // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ - - /* Experimental Options */ - // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ - // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ - - /* Advanced Options */ - "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ - } -} diff --git a/packages/reporting-e2e/wdio.conf.js b/packages/reporting-e2e/wdio.conf.js deleted file mode 100644 index ee7f1b093..000000000 --- a/packages/reporting-e2e/wdio.conf.js +++ /dev/null @@ -1,291 +0,0 @@ -const path = require('path'); -const fs = require('fs'); -const os = require('os'); -const { execSync } = require('child_process'); - -const headlessArgs = process.env.CI ? ['--headless', '--disable-gpu'] : []; -const chromeArgs = [...headlessArgs]; -if (os.userInfo().uid === 0) { - chromeArgs.push('--no-sandbox'); -} - -exports.config = { - // - // ==================== - // Runner Configuration - // ==================== - // - // WebdriverIO allows it to run your tests in arbitrary locations (e.g. locally or - // on a remote machine). - runner: 'local', - // - // ================== - // Specify Test Files - // ================== - // Define which test specs should run. The pattern is relative to the directory - // from which `wdio` was called. Notice that, if you are calling `wdio` from an - // NPM script (see https://docs.npmjs.com/cli/run-script) then the current working - // directory is where your package.json resides, so `wdio` will be called from there. - // - specs: ['tests/**/*.test.ts'], - // Patterns to exclude. - exclude: [ - // 'path/to/excluded/files' - ], - // - // ============ - // Capabilities - // ============ - // Define your capabilities here. WebdriverIO can run multiple capabilities at the same - // time. Depending on the number of capabilities, WebdriverIO launches several test - // sessions. Within your capabilities you can overwrite the spec and exclude options in - // order to group specific specs to a specific capability. - // - // First, you can define how many instances should be started at the same time. Let's - // say you have 3 different capabilities (Chrome, Firefox, and Safari) and you have - // set maxInstances to 1; wdio will spawn 3 processes. Therefore, if you have 10 spec - // files and you set maxInstances to 10, all spec files will get tested at the same time - // and 30 processes will get spawned. The property handles how many capabilities - // from the same test should run tests. - // - maxInstances: 1, - // - // If you have trouble getting all important capabilities together, check out the - // Sauce Labs platform configurator - a great tool to configure your capabilities: - // https://docs.saucelabs.com/reference/platforms-configurator - // - capabilities: [ - { - // maxInstances can get overwritten per capability. So if you have an in-house Selenium - // grid with only 5 firefox instances available you can make sure that not more than - // 5 instances get started at a time. - maxInstances: 1, - // - browserName: 'chrome', - // If outputDir is provided WebdriverIO can capture driver session logs - // it is possible to configure which logTypes to include/exclude. - // excludeDriverLogs: ['*'], // pass '*' to exclude all driver session logs - // excludeDriverLogs: ['bugreport', 'server'], - - acceptInsecureCerts: true, - - 'goog:chromeOptions': { - binary: process.env.CHROME_BIN || undefined, - // to run chrome headless the following flags are required - // (see https://developers.google.com/web/updates/2017/04/headless-chrome) - args: [...chromeArgs, '--window-size=1366,768'], - }, - }, - ], - // - // =================== - // Test Configurations - // =================== - // Define all options that are relevant for the WebdriverIO instance here - // - // Level of logging verbosity: trace | debug | info | warn | error | silent - logLevel: 'warn', - // - // Set specific log levels per logger - // loggers: - // - webdriver, webdriverio - // - @wdio/applitools-service, @wdio/browserstack-service, @wdio/devtools-service, @wdio/sauce-service - // - @wdio/mocha-framework, @wdio/jasmine-framework - // - @wdio/local-runner, @wdio/lambda-runner - // - @wdio/sumologic-reporter - // - @wdio/cli, @wdio/config, @wdio/sync, @wdio/utils - // Level of logging verbosity: trace | debug | info | warn | error | silent - // logLevels: { - // webdriver: 'info', - // '@wdio/applitools-service': 'info' - // }, - // - // If you only want to run your tests until a specific amount of tests have failed use - // bail (default is 0 - don't bail, run all tests). - bail: 1, - // - // Set a base URL in order to shorten url command calls. If your `url` parameter starts - // with `/`, the base url gets prepended, not including the path portion of your baseUrl. - // If your `url` parameter starts without a scheme or `/` (like `some/path`), the base url - // gets prepended directly. - baseUrl: process.env.E2E_REPORTING_URL, - // - // Default timeout for all waitFor* commands. - waitforTimeout: 10000, - // - // Default timeout in milliseconds for request - // if browser driver or grid doesn't send response - connectionRetryTimeout: 120000, - // - // Default request retries count - connectionRetryCount: 3, - // - // Test runner services - // Services take over a specific job you don't want to take care of. They enhance - // your test setup with almost no effort. Unlike plugins, they don't add new - // commands. Instead, they hook themselves up into the test process. - services: [], - - // Framework you want to run your specs with. - // The following are supported: Mocha, Jasmine, and Cucumber - // see also: https://webdriver.io/docs/frameworks.html - // - // Make sure you have the wdio adapter package for the specific framework installed - // before running any tests. - framework: 'mocha', - // - // The number of times to retry the entire specfile when it fails as a whole - // specFileRetries: 1, - // - // Whether or not retried specfiles should be retried immediately or deferred to the end of the queue - // specFileRetriesDeferred: false, - // - // Test reporter for stdout. - // The only one supported by default is 'dot' - // see also: https://webdriver.io/docs/dot-reporter.html - reporters: ['spec'], - - // - // Options to be passed to Mocha. - // See the full list at http://mochajs.org/ - mochaOpts: { - // as of wdio 6.12.1, it automatically registers ts-node, registering it again would cause conflict - // require: ['ts-node/register'], - ui: 'bdd', - timeout: 60000, - }, - // - // ===== - // Hooks - // ===== - // WebdriverIO provides several hooks you can use to interfere with the test process in order to enhance - // it and to build services around it. You can either apply a single function or an array of - // methods to it. If one of them returns with a promise, WebdriverIO will wait until that promise got - // resolved to continue. - /** - * Gets executed once before all workers get launched. - * @param {Object} config wdio configuration object - * @param {Array.} capabilities list of capabilities details - */ - // onPrepare: function (config, capabilities) { - // }, - /** - * Gets executed before a worker process is spawned and can be used to initialise specific service - * for that worker as well as modify runtime environments in an async fashion. - * @param {String} cid capability id (e.g 0-0) - * @param {[type]} caps object containing capabilities for session that will be spawn in the worker - * @param {[type]} specs specs to be run in the worker process - * @param {[type]} args object that will be merged with the main configuration once worker is initialised - * @param {[type]} execArgv list of string arguments passed to the worker process - */ - // onWorkerStart: function (cid, caps, specs, args, execArgv) { - // }, - /** - * Gets executed just before initialising the webdriver session and test framework. It allows you - * to manipulate configurations depending on the capability or spec. - * @param {Object} config wdio configuration object - * @param {Array.} capabilities list of capabilities details - * @param {Array.} specs List of spec file paths that are to be run - */ - // beforeSession: function (config, capabilities, specs) { - // }, - /** - * Gets executed before test execution begins. At this point you can access to all global - * variables like `browser`. It is the perfect place to define custom commands. - * @param {Array.} capabilities list of capabilities details - * @param {Array.} specs List of spec file paths that are to be run - */ - // before: function (capabilities, specs) { - // }, - /** - * Runs before a WebdriverIO command gets executed. - * @param {String} commandName hook command name - * @param {Array} args arguments that command would receive - */ - // beforeCommand: function (commandName, args) { - // }, - /** - * Hook that gets executed before the suite starts - * @param {Object} suite suite details - */ - // beforeSuite: function (suite) { - // }, - /** - * Function to be executed before a test (in Mocha/Jasmine) starts. - */ - // beforeTest: function (test, context) { - // }, - /** - * Hook that gets executed _before_ a hook within the suite starts (e.g. runs before calling - * beforeEach in Mocha) - */ - // beforeHook: function (test, context) { - // }, - /** - * Hook that gets executed _after_ a hook within the suite starts (e.g. runs after calling - * afterEach in Mocha) - */ - // afterHook: function (test, context, { error, result, duration, passed, retries }) { - // }, - /** - * Function to be executed after a test (in Mocha/Jasmine). - */ - afterTest: async function (test /*, context, { error, result, duration, passed, retries }*/) { - const testPath = path.relative('tests', test.file); - const artifactDir = `artifacts/${testPath}/${test.title}`; - fs.mkdirSync(artifactDir, { recursive: true }); - await browser.saveScreenshot(`${artifactDir}/end.png`); - }, - - /** - * Hook that gets executed after the suite has ended - * @param {Object} suite suite details - */ - // afterSuite: function (suite) { - // }, - /** - * Runs after a WebdriverIO command gets executed - * @param {String} commandName hook command name - * @param {Array} args arguments that command would receive - * @param {Number} result 0 - command success, 1 - command error - * @param {Object} error error object if any - */ - // afterCommand: function (commandName, args, result, error) { - // }, - /** - * Gets executed after all tests are done. You still have access to all global variables from - * the test. - * @param {Number} result 0 - test pass, 1 - test fail - * @param {Array.} capabilities list of capabilities details - * @param {Array.} specs List of spec file paths that ran - */ - // after: function (result, capabilities, specs) { - // }, - /** - * Gets executed right after terminating the webdriver session. - * @param {Object} config wdio configuration object - * @param {Array.} capabilities list of capabilities details - * @param {Array.} specs List of spec file paths that ran - */ - // afterSession: function (config, capabilities, specs) { - // }, - /** - * Gets executed after all workers got shut down and the process is about to exit. An error - * thrown in the onComplete hook will result in the test run failing. - * @param {Object} exitCode 0 - success, 1 - fail - * @param {Object} config wdio configuration object - * @param {Array.} capabilities list of capabilities details - * @param {} results object containing test results - */ - onComplete: function (/* exitCode, config, capabilities, results */) { - // FIXME: This is very dangerous. Also force killing the process is definitely not a right way to close it. - execSync('kill -9 `pgrep reporting`'); - }, - /** - * Gets executed when a refresh happens. - * @param {String} oldSessionId session ID of the old session - * @param {String} newSessionId session ID of the new session - */ - //onReload: function(oldSessionId, newSessionId) { - //} -}; diff --git a/packages/reporting-server/.coverage b/packages/reporting-server/.coverage deleted file mode 100644 index 40ada75b8..000000000 Binary files a/packages/reporting-server/.coverage and /dev/null differ diff --git a/packages/reporting-server/.coveragerc b/packages/reporting-server/.coveragerc deleted file mode 100644 index c020df653..000000000 --- a/packages/reporting-server/.coveragerc +++ /dev/null @@ -1,7 +0,0 @@ -[run] -source = - rest_server -omit = - # test files - **/test_*.py - diff --git a/packages/reporting-server/.gitignore b/packages/reporting-server/.gitignore deleted file mode 100644 index 6c816b8e7..000000000 --- a/packages/reporting-server/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -/reporting_server.egg-info -/static -/test_artifacts/ -/.coverage -/.coverage.* -/htmlcov/ -/build/ -/dist/ - diff --git a/packages/reporting-server/.pylintrc b/packages/reporting-server/.pylintrc deleted file mode 100644 index 4cc9f477f..000000000 --- a/packages/reporting-server/.pylintrc +++ /dev/null @@ -1,600 +0,0 @@ -[MASTER] - -# A comma-separated list of package or module names from where C extensions may -# be loaded. Extensions are loading into the active Python interpreter and may -# run arbitrary code. -extension-pkg-whitelist=pydantic - -# Specify a score threshold to be exceeded before program exits with error. -fail-under=10.0 - -# Add files or directories to the blacklist. They should be base names, not -# paths. -ignore=CVS - -# Add files or directories matching the regex patterns to the blacklist. The -# regex matches against base names, not paths. -ignore-patterns= - -# Python code to execute, usually for sys.path manipulation such as -# pygtk.require(). -#init-hook= - -# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the -# number of processors available to use. -jobs=1 - -# Control the amount of potential inferred values when inferring a single -# object. This can help the performance when dealing with large functions or -# complex, nested conditions. -limit-inference-results=100 - -# List of plugins (as comma separated values of python module names) to load, -# usually to register additional checkers. -load-plugins= - -# Pickle collected data for later comparisons. -persistent=yes - -# When enabled, pylint would attempt to guess common misconfiguration and emit -# user-friendly hints instead of false-positive error messages. -suggestion-mode=yes - -# Allow loading of arbitrary C extensions. Extensions are imported into the -# active Python interpreter and may run arbitrary code. -unsafe-load-any-extension=no - - -[MESSAGES CONTROL] - -# Only show warnings with the listed confidence levels. Leave empty to show -# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED. -confidence= - -# Disable the message, report, category or checker with the given id(s). You -# can either give multiple identifiers separated by comma (,) or put this -# option multiple times (only on the command line, not in the configuration -# file where it should appear only once). You can also use "--disable=all" to -# disable everything first and then reenable specific checks. For example, if -# you want to run only the similarities checker, you can use "--disable=all -# --enable=similarities". If you want to run only the classes checker, but have -# no Warning level messages displayed, use "--disable=all --enable=classes -# --disable=W". -disable=print-statement, - parameter-unpacking, - unpacking-in-except, - old-raise-syntax, - backtick, - long-suffix, - old-ne-operator, - old-octal-literal, - import-star-module-level, - non-ascii-bytes-literal, - raw-checker-failed, - bad-inline-option, - locally-disabled, - file-ignored, - suppressed-message, - useless-suppression, - deprecated-pragma, - use-symbolic-message-instead, - apply-builtin, - basestring-builtin, - buffer-builtin, - cmp-builtin, - coerce-builtin, - execfile-builtin, - file-builtin, - long-builtin, - raw_input-builtin, - reduce-builtin, - standarderror-builtin, - unicode-builtin, - xrange-builtin, - coerce-method, - delslice-method, - getslice-method, - setslice-method, - no-absolute-import, - old-division, - dict-iter-method, - dict-view-method, - next-method-called, - metaclass-assignment, - indexing-exception, - raising-string, - reload-builtin, - oct-method, - hex-method, - nonzero-method, - cmp-method, - input-builtin, - round-builtin, - intern-builtin, - unichr-builtin, - map-builtin-not-iterating, - zip-builtin-not-iterating, - range-builtin-not-iterating, - filter-builtin-not-iterating, - using-cmp-argument, - eq-without-hash, - div-method, - idiv-method, - rdiv-method, - exception-message-attribute, - invalid-str-codec, - sys-max-int, - bad-python3-import, - deprecated-string-function, - deprecated-str-translate-call, - deprecated-itertools-function, - deprecated-types-field, - next-method-defined, - dict-items-not-iterating, - dict-keys-not-iterating, - dict-values-not-iterating, - deprecated-operator-function, - deprecated-urllib-function, - xreadlines-attribute, - deprecated-sys-function, - exception-escape, - comprehension-escape, - -# added disables - design, - similarities, - invalid-name, - missing-module-docstring, - missing-class-docstring, - missing-function-docstring, - attribute-defined-outside-init, - fixme, - logging-fstring-interpolation, - line-too-long, - -# Enable the message, report, category or checker with the given id(s). You can -# either give multiple identifier separated by comma (,) or put this option -# multiple time (only on the command line, not in the configuration file where -# it should appear only once). See also the "--disable" option for examples. -enable=c-extension-no-member - - -[REPORTS] - -# Python expression which should return a score less than or equal to 10. You -# have access to the variables 'error', 'warning', 'refactor', and 'convention' -# which contain the number of messages in each category, as well as 'statement' -# which is the total number of statements analyzed. This score is used by the -# global evaluation report (RP0004). -evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) - -# Template used to display messages. This is a python new-style format string -# used to format the message information. See doc for all details. -#msg-template= - -# Set the output format. Available formats are text, parseable, colorized, json -# and msvs (visual studio). You can also give a reporter class, e.g. -# mypackage.mymodule.MyReporterClass. -output-format=text - -# Tells whether to display a full report or only the messages. -reports=no - -# Activate the evaluation score. -score=yes - - -[REFACTORING] - -# Maximum number of nested blocks for function / method body -max-nested-blocks=5 - -# Complete name of functions that never returns. When checking for -# inconsistent-return-statements if a never returning function is called then -# it will be considered as an explicit return statement and no message will be -# printed. -never-returning-functions=sys.exit - - -[LOGGING] - -# The type of string formatting that logging methods do. `old` means using % -# formatting, `new` is for `{}` formatting. -logging-format-style=old - -# Logging modules to check that the string format arguments are in logging -# function parameter format. -logging-modules=logging - - -[TYPECHECK] - -# List of decorators that produce context managers, such as -# contextlib.contextmanager. Add to this list to register other decorators that -# produce valid context managers. -contextmanager-decorators=contextlib.contextmanager - -# List of members which are set dynamically and missed by pylint inference -# system, and so shouldn't trigger E1101 when accessed. Python regular -# expressions are accepted. -generated-members= - -# Tells whether missing members accessed in mixin class should be ignored. A -# mixin class is detected if its name ends with "mixin" (case insensitive). -ignore-mixin-members=yes - -# Tells whether to warn about missing members when the owner of the attribute -# is inferred to be None. -ignore-none=yes - -# This flag controls whether pylint should warn about no-member and similar -# checks whenever an opaque object is returned when inferring. The inference -# can return multiple potential results while evaluating a Python object, but -# some branches might not be evaluated, which results in partial inference. In -# that case, it might be useful to still emit no-member and other checks for -# the rest of the inferred objects. -ignore-on-opaque-inference=yes - -# List of class names for which member attributes should not be checked (useful -# for classes with dynamically set attributes). This supports the use of -# qualified names. -ignored-classes=optparse.Values,thread._local,_thread._local - -# List of module names for which member attributes should not be checked -# (useful for modules/projects where namespaces are manipulated during runtime -# and thus existing member attributes cannot be deduced by static analysis). It -# supports qualified module names, as well as Unix pattern matching. -ignored-modules= - -# Show a hint with possible names when a member name was not found. The aspect -# of finding the hint is based on edit distance. -missing-member-hint=yes - -# The minimum edit distance a name should have in order to be considered a -# similar match for a missing member name. -missing-member-hint-distance=1 - -# The total number of similar names that should be taken in consideration when -# showing a hint for a missing member. -missing-member-max-choices=1 - -# List of decorators that change the signature of a decorated function. -signature-mutators= - - -[BASIC] - -# Naming style matching correct argument names. -argument-naming-style=snake_case - -# Regular expression matching correct argument names. Overrides argument- -# naming-style. -#argument-rgx= - -# Naming style matching correct attribute names. -attr-naming-style=snake_case - -# Regular expression matching correct attribute names. Overrides attr-naming- -# style. -#attr-rgx= - -# Bad variable names which should always be refused, separated by a comma. -bad-names=foo, - bar, - baz, - toto, - tutu, - tata - -# Bad variable names regexes, separated by a comma. If names match any regex, -# they will always be refused -bad-names-rgxs= - -# Naming style matching correct class attribute names. -class-attribute-naming-style=any - -# Regular expression matching correct class attribute names. Overrides class- -# attribute-naming-style. -#class-attribute-rgx= - -# Naming style matching correct class names. -class-naming-style=PascalCase - -# Regular expression matching correct class names. Overrides class-naming- -# style. -#class-rgx= - -# Naming style matching correct constant names. -const-naming-style=UPPER_CASE - -# Regular expression matching correct constant names. Overrides const-naming- -# style. -#const-rgx= - -# Minimum line length for functions/classes that require docstrings, shorter -# ones are exempt. -docstring-min-length=-1 - -# Naming style matching correct function names. -function-naming-style=snake_case - -# Regular expression matching correct function names. Overrides function- -# naming-style. -#function-rgx= - -# Good variable names which should always be accepted, separated by a comma. -good-names=i, - j, - k, - ex, - Run, - _ - -# Good variable names regexes, separated by a comma. If names match any regex, -# they will always be accepted -good-names-rgxs= - -# Include a hint for the correct naming format with invalid-name. -include-naming-hint=no - -# Naming style matching correct inline iteration names. -inlinevar-naming-style=any - -# Regular expression matching correct inline iteration names. Overrides -# inlinevar-naming-style. -#inlinevar-rgx= - -# Naming style matching correct method names. -method-naming-style=snake_case - -# Regular expression matching correct method names. Overrides method-naming- -# style. -#method-rgx= - -# Naming style matching correct module names. -module-naming-style=snake_case - -# Regular expression matching correct module names. Overrides module-naming- -# style. -#module-rgx= - -# Colon-delimited sets of names that determine each other's naming style when -# the name regexes allow several styles. -name-group= - -# Regular expression which should only match function or class names that do -# not require a docstring. -no-docstring-rgx=^_ - -# List of decorators that produce properties, such as abc.abstractproperty. Add -# to this list to register other decorators that produce valid properties. -# These decorators are taken in consideration only for invalid-name. -property-classes=abc.abstractproperty - -# Naming style matching correct variable names. -variable-naming-style=snake_case - -# Regular expression matching correct variable names. Overrides variable- -# naming-style. -#variable-rgx= - - -[FORMAT] - -# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. -expected-line-ending-format= - -# Regexp for a line that is allowed to be longer than the limit. -ignore-long-lines=^\s*(# )??$ - -# Number of spaces of indent required inside a hanging or continued line. -indent-after-paren=4 - -# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 -# tab). -indent-string=' ' - -# Maximum number of characters on a single line. -max-line-length=100 - -# Maximum number of lines in a module. -max-module-lines=1000 - -# Allow the body of a class to be on the same line as the declaration if body -# contains single statement. -single-line-class-stmt=no - -# Allow the body of an if to be on the same line as the test if there is no -# else. -single-line-if-stmt=no - - -[SIMILARITIES] - -# Ignore comments when computing similarities. -ignore-comments=yes - -# Ignore docstrings when computing similarities. -ignore-docstrings=yes - -# Ignore imports when computing similarities. -ignore-imports=no - -# Minimum lines number of a similarity. -min-similarity-lines=4 - - -[STRING] - -# This flag controls whether inconsistent-quotes generates a warning when the -# character used as a quote delimiter is used inconsistently within a module. -check-quote-consistency=no - -# This flag controls whether the implicit-str-concat should generate a warning -# on implicit string concatenation in sequences defined over several lines. -check-str-concat-over-line-jumps=no - - -[SPELLING] - -# Limits count of emitted suggestions for spelling mistakes. -max-spelling-suggestions=4 - -# Spelling dictionary name. Available dictionaries: none. To make it work, -# install the python-enchant package. -spelling-dict= - -# List of comma separated words that should not be checked. -spelling-ignore-words= - -# A path to a file that contains the private dictionary; one word per line. -spelling-private-dict-file= - -# Tells whether to store unknown words to the private dictionary (see the -# --spelling-private-dict-file option) instead of raising a message. -spelling-store-unknown-words=no - - -[MISCELLANEOUS] - -# List of note tags to take in consideration, separated by a comma. -notes=FIXME, - XXX, - TODO - -# Regular expression of note tags to take in consideration. -#notes-rgx= - - -[VARIABLES] - -# List of additional names supposed to be defined in builtins. Remember that -# you should avoid defining new builtins when possible. -additional-builtins= - -# Tells whether unused global variables should be treated as a violation. -allow-global-unused-variables=yes - -# List of strings which can identify a callback function by name. A callback -# name must start or end with one of those strings. -callbacks=cb_, - _cb - -# A regular expression matching the name of dummy variables (i.e. expected to -# not be used). -dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ - -# Argument names that match this expression will be ignored. Default to name -# with leading underscore. -ignored-argument-names=_.*|^ignored_|^unused_ - -# Tells whether we should check for unused import in __init__ files. -init-import=no - -# List of qualified module names which can have objects that can redefine -# builtins. -redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io - - -[IMPORTS] - -# List of modules that can be imported at any level, not just the top level -# one. -allow-any-import-level= - -# Allow wildcard imports from modules that define __all__. -allow-wildcard-with-all=no - -# Analyse import fallback blocks. This can be used to support both Python 2 and -# 3 compatible code, which means that the block might have code that exists -# only in one or another interpreter, leading to false positives when analysed. -analyse-fallback-blocks=no - -# Deprecated modules which should not be used, separated by a comma. -deprecated-modules=optparse,tkinter.tix - -# Create a graph of external dependencies in the given file (report RP0402 must -# not be disabled). -ext-import-graph= - -# Create a graph of every (i.e. internal and external) dependencies in the -# given file (report RP0402 must not be disabled). -import-graph= - -# Create a graph of internal dependencies in the given file (report RP0402 must -# not be disabled). -int-import-graph= - -# Force import order to recognize a module as part of the standard -# compatibility libraries. -known-standard-library= - -# Force import order to recognize a module as part of a third party library. -known-third-party=enchant - -# Couples of modules and preferred modules, separated by a comma. -preferred-modules= - - -[DESIGN] - -# Maximum number of arguments for function / method. -max-args=5 - -# Maximum number of attributes for a class (see R0902). -max-attributes=7 - -# Maximum number of boolean expressions in an if statement (see R0916). -max-bool-expr=5 - -# Maximum number of branch for function / method body. -max-branches=12 - -# Maximum number of locals for function / method body. -max-locals=15 - -# Maximum number of parents for a class (see R0901). -max-parents=7 - -# Maximum number of public methods for a class (see R0904). -max-public-methods=20 - -# Maximum number of return / yield for function / method body. -max-returns=6 - -# Maximum number of statements in function / method body. -max-statements=50 - -# Minimum number of public methods for a class (see R0903). -min-public-methods=2 - - -[CLASSES] - -# List of method names used to declare (i.e. assign) instance attributes. -defining-attr-methods=__init__, - __new__, - setUp, - __post_init__ - -# List of member names, which should be excluded from the protected access -# warning. -exclude-protected=_asdict, - _fields, - _replace, - _source, - _make - -# List of valid names for the first argument in a class method. -valid-classmethod-first-arg=cls - -# List of valid names for the first argument in a metaclass class method. -valid-metaclass-classmethod-first-arg=cls - - -[EXCEPTIONS] - -# Exceptions that will emit a warning when being caught. Defaults to -# "BaseException, Exception". -overgeneral-exceptions=BaseException, - Exception diff --git a/packages/reporting-server/LICENSE b/packages/reporting-server/LICENSE deleted file mode 100644 index 261eeb9e9..000000000 --- a/packages/reporting-server/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/packages/reporting-server/README.md b/packages/reporting-server/README.md deleted file mode 100644 index 88aa0a8c7..000000000 --- a/packages/reporting-server/README.md +++ /dev/null @@ -1,122 +0,0 @@ -# Description - -This project is about a server that exposes two APIs, one for writing logs to a persistent storage and the other for generating reports. As the project's name says, the idea is to work as a reporting server. The reporting server is designed to receive data through [Fluentd](https://www.fluentd.org/) (a data collection tool) from different pods within a Kubernetes cluster. Log data is transformed via a parser from the format that Fluentd uses for storage in the server. You can find the log formats [here](https://github.com/open-rmf/rmf-web/blob/main/packages/reporting-server/rest_server/__mocks__/raw_data.py). - -In the following image, we can observe how all the pods interact with the reporting server -![rmf-web kubernetes cluster diagram](https://user-images.githubusercontent.com/28668944/123916706-8b56e300-d9b4-11eb-990f-69e717f87b38.png) - - -# Setup - -Install pipenv - -```bash -pip3 install pipenv -``` - -If not already done so, [bootstrap](../../README.md#bootstrap) the project, you can use - -```bash -scripts/bootstrap.sh reporting-server -``` - -to bootstrap only this package. - -# Run the server - -```bash -reporting_server -``` - -When you run this command, two instances of the reporting server will run. One on port 8002 where the endpoints will be enabled to ask for reports and 8003 where the endpoints will be enabled to send logs to the reporting server. - -![image](https://user-images.githubusercontent.com/11761240/123881439-b12bab80-d912-11eb-987a-77591add6c5d.png) - -For development we recommend running this command: - -```bash -uvicorn --reload rest_server.app:get_app -``` - -This would only create one instance of the reporting-server and it'll serve on the default port. - -## Configuration - -Config files are python modules that export a variable named `config`. See [default_config.py](rest_server/default_config.py) for an example and list of the options available. All options are REQUIRED unless specified otherwise. - -Configuration is read from the file specified in the env `RMF_REPORT_REST_SERVER_CONFIG`, if not provided, the default config is used. - -e.g. -```bash -RMF_REPORT_REST_SERVER_CONFIG='my_config.py' reporting_server -``` - - -## Supported databases - -`reporting-server` uses [tortoise-orm](https://github.com/tortoise/tortoise-orm/) to perform database operations. Currently, the supported databases are - -* PostgreSQL -* SQLite -* MySQL -* MariaDB - -by default it uses a in-memory sqlite instance, to use other databases, install rmf-server with the relevalent extras - -* PostgreSQL - postgres -* MySQL - mysql -* MariaDB - maria - -.e.g. - -```bash -pip3 install reporting-server[postgres] -``` - -Then in your config, set the `db_url` accordingly, the url should be in the form - -``` -DB_TYPE://USERNAME:PASSWORD@HOST:PORT/DB_NAME?PARAM1=value&PARAM2=value -``` - -for example, to connect to postgres - -``` -postgres://:@/ -``` - -for more information, see https://tortoise-orm.readthedocs.io/en/latest/databases.html. - - -# Developers - -## Running tests - -### Running unit tests - -```bash -npm run test -``` - -### Collecting code coverage - -```bash -npm run test:cov -``` - -Generate coverage report -```bash -npm run test:report -``` - -## Live reload - -```bash -uvicorn --reload rest_server.app:get_app -``` - -## QA - -* I have a zombie process running either on port 8002 or 8003? - - The `reporting_server` runs two instances of the app on the same process. So, sometimes when you shut down one of the reporting-server instances, the other stay alive, resulting in a zombie process. You can kill it by running this command `kill -9 ` (on Linux based OS). That's why we recommend using `uvicorn --reload rest_server.app:get_app` for development purposes. diff --git a/packages/reporting-server/dependencies/__init__.py b/packages/reporting-server/dependencies/__init__.py deleted file mode 100644 index 09fb7aa3b..000000000 --- a/packages/reporting-server/dependencies/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -from .auth import auth_scheme, authenticator -from .logging import logger diff --git a/packages/reporting-server/dependencies/auth.py b/packages/reporting-server/dependencies/auth.py deleted file mode 100644 index 8729c606e..000000000 --- a/packages/reporting-server/dependencies/auth.py +++ /dev/null @@ -1,53 +0,0 @@ -from dependencies.logging import logger -from fastapi import Depends, HTTPException -from fastapi.security import OpenIdConnect -from rest_server.app_config import app_config -from rest_server.authenticator import AuthenticationError, JwtAuthenticator - -if app_config.jwt_public_key: - authenticator = JwtAuthenticator(app_config.jwt_public_key) - - if app_config.oidc_url: - oidc_url = app_config.oidc_url - else: - oidc_url = "" - - # TODO: Authorization code flow doesn't actually work because of this bug https://github.com/swagger-api/swagger-ui/issues/6741. - # althrough fastapi uses the latest version of swagger ui from a cdn, - # the oauth2-redirect is hardcoded using an old version. - # - # Also, password flow may not work because it is not actually part of the oidc standard. - # For keycloak, it doesn't return the id_token, which is required in a typical authorization code - # flow. - # - # On top of the above problems, swagger ui doesn't actually fully support oidc in the - # way we use it. We expect the id_token because it is the only token that MUST be a - # JWT according to oidc spec (although most implementation nowadays also uses JWT - # for the access token). Swagger UI passes the access_token to the server instead of the - # id_token, so in the case that the authentication provider is not using a JWT for the - # access_token, the authentication will fail. Even if the provider uses JWT for access_token, - # it may still fail because of the different claims present in the access_token and id_token. - # In the oidc spec, for an id_token, the "aud" claim MUST contain the client_id, but in - # the case of keycloak, even though the access_token is also a JWT, it does not have to - # follow the id_token spec, so the "aud" claim MAY NOT contain the client_id. - # This causes authentication to fail because in the JWT spec, if the "aud" claim is present, - # the verifier MUST check that it contains an expected value, since we expect an oidc - # id_token, we MUST verify that the "aud" contains the client_id. - security_scheme = OpenIdConnect(openIdConnectUrl=oidc_url) - - def auth_scheme(auth_header: str = Depends(security_scheme)): - parts = auth_header.split(" ") - if len(parts) != 2 or parts[0].lower() != "bearer": - raise HTTPException(401, "invalid bearer format") - try: - authenticator.verify_token(parts[1]) - except AuthenticationError as e: - raise HTTPException(401, str(e)) from e - - -else: - logger.warning("authentication is disabled") - authenticator = None - - def auth_scheme(): - return None # no authentication diff --git a/packages/reporting-server/dependencies/logging.py b/packages/reporting-server/dependencies/logging.py deleted file mode 100644 index 6419ce6ae..000000000 --- a/packages/reporting-server/dependencies/logging.py +++ /dev/null @@ -1,13 +0,0 @@ -import logging -import os -import sys - -from rest_server.app_config import app_config - -logger = logging.getLogger("app") -handler = logging.StreamHandler(sys.stdout) -handler.setFormatter(logging.Formatter(logging.BASIC_FORMAT)) -logger.addHandler(handler) -logger.setLevel(app_config.log_level) -if "RMF_API_SERVER_LOG_LEVEL" in os.environ: - logger.setLevel(os.environ["RMF_API_SERVER_LOG_LEVEL"]) diff --git a/packages/reporting-server/models/__init__.py b/packages/reporting-server/models/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/packages/reporting-server/models/pydantic_models.py b/packages/reporting-server/models/pydantic_models.py deleted file mode 100644 index 03642cb1e..000000000 --- a/packages/reporting-server/models/pydantic_models.py +++ /dev/null @@ -1,33 +0,0 @@ -from tortoise.contrib.pydantic import pydantic_model_creator - -from .tortoise_models import ( - AuthEvents, - Container, - DispenserState, - Door, - DoorState, - Fleet, - FleetState, - HealthStatus, - IngestorState, - Lift, - LiftState, - RawLog, - Robot, - TaskSummary, -) - -AuthEvents_Pydantic = pydantic_model_creator(AuthEvents, name="AuthEvents") -Container_Pydantic = pydantic_model_creator(Container, name="Container") -DispenserState_Pydantic = pydantic_model_creator(DispenserState, name="DispenserState") -Door_Pydantic = pydantic_model_creator(Door, name="Door") -DoorState_Pydantic = pydantic_model_creator(DoorState, name="DoorState") -Fleet_Pydantic = pydantic_model_creator(Fleet, name="Fleet") -FleetState_Pydantic = pydantic_model_creator(FleetState, name="FleetState") -HealthStatus_Pydantic = pydantic_model_creator(HealthStatus, name="HealthStatus") -IngestorState_Pydantic = pydantic_model_creator(IngestorState, name="IngestorState") -Lift_Pydantic = pydantic_model_creator(Lift, name="Lift") -LiftState_Pydantic = pydantic_model_creator(LiftState, name="LiftStates") -RawLog_Pydantic = pydantic_model_creator(RawLog, name="RawLog") -Robot_Pydantic = pydantic_model_creator(Robot, name="Robot") -TaskSummary_Pydantic = pydantic_model_creator(TaskSummary, name="TaskSummary") diff --git a/packages/reporting-server/models/tortoise_models/__init__.py b/packages/reporting-server/models/tortoise_models/__init__.py deleted file mode 100644 index 5e7749c73..000000000 --- a/packages/reporting-server/models/tortoise_models/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -from .auth_events import AuthEvents -from .container import Container -from .dispenser_state import DispenserState -from .door import Door -from .door_state import DoorState -from .fleet import Fleet, Robot -from .fleet_state import FleetState -from .health import Device, HealthStatus -from .ingestor_state import IngestorState -from .lift import Lift -from .lift_state import LiftState -from .raw_log import RawLog -from .task_summary import TaskSummary diff --git a/packages/reporting-server/models/tortoise_models/auth_events.py b/packages/reporting-server/models/tortoise_models/auth_events.py deleted file mode 100644 index c43be16d9..000000000 --- a/packages/reporting-server/models/tortoise_models/auth_events.py +++ /dev/null @@ -1,16 +0,0 @@ -from tortoise import fields, models - - -class AuthEvents(models.Model): - id = fields.IntField(pk=True) - # user = fields.ForeignKey(User, null=True) - username = fields.CharField(max_length=100, null=True) - user_keycloak_id = fields.CharField(max_length=100, null=True) - event_type = fields.CharField(max_length=100) - realm_id = fields.CharField(max_length=100, null=True) - client_id = fields.CharField(max_length=100, null=True) - ip_address = fields.CharField(max_length=50, null=True) - created = fields.DatetimeField(auto_now_add=True) - - def __str__(self): - return str(self.event_type) diff --git a/packages/reporting-server/models/tortoise_models/container.py b/packages/reporting-server/models/tortoise_models/container.py deleted file mode 100644 index 91395d764..000000000 --- a/packages/reporting-server/models/tortoise_models/container.py +++ /dev/null @@ -1,6 +0,0 @@ -from tortoise import fields, models - - -class Container(models.Model): - id = fields.IntField(pk=True) - name = fields.TextField() diff --git a/packages/reporting-server/models/tortoise_models/dispenser_state.py b/packages/reporting-server/models/tortoise_models/dispenser_state.py deleted file mode 100644 index e0434a172..000000000 --- a/packages/reporting-server/models/tortoise_models/dispenser_state.py +++ /dev/null @@ -1,28 +0,0 @@ -from enum import Enum - -from tortoise import fields, models - - -class DispenserStateEnum(str, Enum): - IDLE = "idle" - BUSY = "busy" - OFFLINE = "offline" - - -class DispenserStateService: - def get_state_name(self, state: int): - if state == 0: - return DispenserStateEnum.IDLE - elif state == 1: - return DispenserStateEnum.BUSY - elif state == 2: - return DispenserStateEnum.OFFLINE - - -class DispenserState(models.Model): - id = fields.IntField(pk=True) - state = fields.CharEnumField(DispenserStateEnum) - guid = fields.CharField(max_length=200) - created = fields.DatetimeField(auto_now_add=True) - - service = DispenserStateService() diff --git a/packages/reporting-server/models/tortoise_models/door.py b/packages/reporting-server/models/tortoise_models/door.py deleted file mode 100644 index 6485f07fe..000000000 --- a/packages/reporting-server/models/tortoise_models/door.py +++ /dev/null @@ -1,6 +0,0 @@ -from tortoise import fields, models - - -class Door(models.Model): - id = fields.IntField(pk=True) - name = fields.CharField(max_length=100) diff --git a/packages/reporting-server/models/tortoise_models/door_state.py b/packages/reporting-server/models/tortoise_models/door_state.py deleted file mode 100644 index 9d9fbd493..000000000 --- a/packages/reporting-server/models/tortoise_models/door_state.py +++ /dev/null @@ -1,38 +0,0 @@ -from enum import Enum - -from tortoise import fields, models - - -class DoorStateEnum(str, Enum): - CLOSED = "closed" - MOVING = "moving" - OPEN = "open" - OFFLINE = "offline" - UNKNOWN = "unknown" - - -class DoorStateService: - def get_state_name(self, door_state: int): - if door_state == 0: - return DoorStateEnum.CLOSED - elif door_state == 1: - return DoorStateEnum.MOVING - elif door_state == 2: - return DoorStateEnum.OPEN - elif door_state == 3: - return DoorStateEnum.OFFLINE - elif door_state == 4: - return DoorStateEnum.UNKNOWN - else: - return DoorStateEnum.UNKNOWN - - -class DoorState(models.Model): - id = fields.IntField(pk=True) - state: DoorStateEnum = fields.CharEnumField( - DoorStateEnum, default=DoorStateEnum.UNKNOWN - ) - door = fields.ForeignKeyField("models.Door", related_name="door_states", null=True) - created = fields.DatetimeField(auto_now_add=True) - - service = DoorStateService() diff --git a/packages/reporting-server/models/tortoise_models/fleet.py b/packages/reporting-server/models/tortoise_models/fleet.py deleted file mode 100644 index 679d045e5..000000000 --- a/packages/reporting-server/models/tortoise_models/fleet.py +++ /dev/null @@ -1,10 +0,0 @@ -from tortoise import fields, models - - -class Robot(models.Model): - name = fields.CharField(max_length=100) - model = fields.CharField(max_length=100, null=True) - - -class Fleet(models.Model): - name = fields.CharField(max_length=100) diff --git a/packages/reporting-server/models/tortoise_models/fleet_state.py b/packages/reporting-server/models/tortoise_models/fleet_state.py deleted file mode 100644 index 2e13c31fb..000000000 --- a/packages/reporting-server/models/tortoise_models/fleet_state.py +++ /dev/null @@ -1,57 +0,0 @@ -from enum import Enum - -from tortoise import fields, models - - -class RobotStateEnum(str, Enum): - MODE_IDLE = "closed" - MODE_CHARGING = "charging" - MODE_MOVING = "moving" - MODE_PAUSED = "paused" - MODE_WAITING = "waiting" - MODE_EMERGENCY = "emergency" - MODE_GOING_HOME = "going_home" - MODE_DOCKING = "docking" - MODE_ADAPTER_ERROR = "adapter_error" - - -class RobotStateService: - def get_robot_state_name(self, state: int): - if state == 0: - return RobotStateEnum.MODE_IDLE - elif state == 1: - return RobotStateEnum.MODE_CHARGING - elif state == 2: - return RobotStateEnum.MODE_MOVING - elif state == 3: - return RobotStateEnum.MODE_PAUSED - elif state == 4: - return RobotStateEnum.MODE_WAITING - elif state == 5: - return RobotStateEnum.MODE_EMERGENCY - elif state == 6: - return RobotStateEnum.MODE_GOING_HOME - elif state == 7: - return RobotStateEnum.MODE_DOCKING - elif state == 8: - return RobotStateEnum.MODE_ADAPTER_ERROR - - -class FleetState(models.Model): - id = fields.IntField(pk=True) - created = fields.DatetimeField(auto_now_add=True) - fleet = fields.ForeignKeyField( - "models.Fleet", related_name="fleet_states", null=True - ) - robot = fields.ForeignKeyField( - "models.Robot", related_name="fleet_states", null=True - ) - robot_battery_percent = fields.CharField(max_length=200) - robot_location = fields.CharField(max_length=200) - robot_mode: RobotStateEnum = fields.CharEnumField( - RobotStateEnum, default=RobotStateEnum.MODE_IDLE - ) - robot_seq = fields.IntField() - robot_task_id = fields.CharField(max_length=200) - - service = RobotStateService() diff --git a/packages/reporting-server/models/tortoise_models/health.py b/packages/reporting-server/models/tortoise_models/health.py deleted file mode 100644 index 799c957f9..000000000 --- a/packages/reporting-server/models/tortoise_models/health.py +++ /dev/null @@ -1,36 +0,0 @@ -from enum import Enum - -from tortoise import fields, models - - -class HealthStatusEmun(str, Enum): - HEALTHY = "Healthy" - UNHEALTHY = "Unhealthy" - DEAD = "Dead" - - -class Device(models.Model): - id = fields.IntField(pk=True) - type = fields.TextField() - actor = fields.CharField(max_length=25, null=True) - - -class HealthStatusService: - def get_health_status(self, status: str): - if status == "HealthStatus.HEALTHY": - return HealthStatusEmun.HEALTHY - elif status == "HealthStatus.UNHEALTHY": - return HealthStatusEmun.UNHEALTHY - elif status == "HealthStatus.DEAD": - return HealthStatusEmun.DEAD - - -class HealthStatus(models.Model): - device = fields.ForeignKeyField( - "models.Device", related_name="health_status", null=True - ) - health_status = fields.CharField(max_length=25, null=True) - health_message = fields.TextField(null=True) - created = fields.DatetimeField(auto_now_add=True) - - service = HealthStatusService() diff --git a/packages/reporting-server/models/tortoise_models/ingestor_state.py b/packages/reporting-server/models/tortoise_models/ingestor_state.py deleted file mode 100644 index cb9036185..000000000 --- a/packages/reporting-server/models/tortoise_models/ingestor_state.py +++ /dev/null @@ -1,30 +0,0 @@ -from enum import Enum - -from tortoise import fields, models - - -class IngestorStateEnum(str, Enum): - IDLE = "idle" - BUSY = "busy" - OFFLINE = "offline" - - -class IngestorStateService: - def get_state_name(self, state: int): - if state == 0: - return IngestorStateEnum.IDLE - elif state == 1: - return IngestorStateEnum.BUSY - elif state == 2: - return IngestorStateEnum.OFFLINE - - -class IngestorState(models.Model): - id = fields.IntField(pk=True) - state: IngestorStateEnum = fields.CharEnumField( - IngestorStateEnum, default=IngestorStateEnum.OFFLINE - ) - guid = fields.CharField(max_length=200) - created = fields.DatetimeField(auto_now_add=True) - - service = IngestorStateService() diff --git a/packages/reporting-server/models/tortoise_models/lift.py b/packages/reporting-server/models/tortoise_models/lift.py deleted file mode 100644 index c08dab43f..000000000 --- a/packages/reporting-server/models/tortoise_models/lift.py +++ /dev/null @@ -1,6 +0,0 @@ -from tortoise import fields, models - - -class Lift(models.Model): - id = fields.IntField(pk=True) - name = fields.CharField(max_length=100) diff --git a/packages/reporting-server/models/tortoise_models/lift_state.py b/packages/reporting-server/models/tortoise_models/lift_state.py deleted file mode 100644 index 0f0158df2..000000000 --- a/packages/reporting-server/models/tortoise_models/lift_state.py +++ /dev/null @@ -1,82 +0,0 @@ -from enum import Enum - -from tortoise import fields, models - - -class LiftStateEnum(str, Enum): - MODE_AGV = "avg" - MODE_EMERGENCY = "emergency" - MODE_FIRE = "fire" - MODE_HUMAN = "human" - MODE_OFFLINE = "offline" - MODE_UNKNOWN = "unknown" - - -# FIXME: add all lift states -class LiftMotionStateEnum(str, Enum): - MOTION_DOWN = "down" - MOTION_STOPPED = "stopped" - MOTION_UNKNOWN = "unknown" - MOTION_UP = "up" - - -class LiftDoorStateEmun(str, Enum): - DOOR_CLOSED = "closed" - DOOR_MOVING = "moving" - DOOR_OPEN = "open" - - -class LiftStateService: - def get_state_name(self, state: int): - if state == 0: - return LiftStateEnum.MODE_UNKNOWN - elif state == 1: - return LiftStateEnum.MODE_HUMAN - elif state == 2: - return LiftStateEnum.MODE_AGV - elif state == 3: - return LiftStateEnum.MODE_FIRE - elif state == 4: - return LiftStateEnum.MODE_OFFLINE - elif state == 5: - return LiftStateEnum.MODE_EMERGENCY - - def get_motion_state_name(self, state: int): - if state == 0: - return LiftMotionStateEnum.MOTION_STOPPED - elif state == 1: - return LiftMotionStateEnum.MOTION_UP - elif state == 2: - return LiftMotionStateEnum.MOTION_DOWN - elif state == 3: - return LiftMotionStateEnum.MOTION_UNKNOWN - - def get_door_state_name(self, state: int): - if state == 0: - return LiftDoorStateEmun.DOOR_CLOSED - elif state == 1: - return LiftDoorStateEmun.DOOR_MOVING - elif state == 2: - return LiftDoorStateEmun.DOOR_OPEN - - -class LiftState(models.Model): - id = fields.IntField(pk=True) - lift = fields.ForeignKeyField( - "models.Lift", related_name="lift_states", on_delete="CASCADE" - ) - door_state: LiftDoorStateEmun = fields.CharEnumField( - LiftDoorStateEmun, default=LiftDoorStateEmun.DOOR_CLOSED - ) - state: LiftStateEnum = fields.CharEnumField( - LiftStateEnum, default=LiftStateEnum.MODE_UNKNOWN - ) - destination_floor = fields.CharField(max_length=20) - motion_state: LiftMotionStateEnum = fields.CharEnumField( - LiftMotionStateEnum, default=LiftMotionStateEnum.MOTION_STOPPED - ) - current_floor = fields.CharField(max_length=20) - session_id = fields.CharField(max_length=200) - created = fields.DatetimeField(auto_now_add=True) - - service = LiftStateService() diff --git a/packages/reporting-server/models/tortoise_models/raw_log.py b/packages/reporting-server/models/tortoise_models/raw_log.py deleted file mode 100644 index cbb5aed06..000000000 --- a/packages/reporting-server/models/tortoise_models/raw_log.py +++ /dev/null @@ -1,25 +0,0 @@ -from enum import Enum - -from tortoise import fields, models - - -class LogLevel(str, Enum): - CRITICAL = "critical" - ERROR = "error" - WARN = "warn" - INFO = "info" - DEBUG = "debug" - UNKNOWN = "unknown" - - -class RawLog(models.Model): - id = fields.IntField(pk=True) - level: LogLevel = fields.CharEnumField(LogLevel, default=LogLevel.INFO) - message = fields.TextField() - created = fields.DatetimeField(auto_now_add=True) - container = fields.ForeignKeyField( - "models.Container", related_name="containers", null=True - ) - - def __str__(self): - return str(self.message) diff --git a/packages/reporting-server/models/tortoise_models/task_summary.py b/packages/reporting-server/models/tortoise_models/task_summary.py deleted file mode 100644 index dc41c198b..000000000 --- a/packages/reporting-server/models/tortoise_models/task_summary.py +++ /dev/null @@ -1,128 +0,0 @@ -from enum import Enum - -from tortoise import fields, models -from tortoise.contrib.pydantic import pydantic_model_creator - - -class TaskStateEnum(str, Enum): - STATE_ACTIVE = "active" - STATE_CANCELLED = "cancelled" - STATE_COMPLETED = "completed" - STATE_FAILED = "failed" - STATE_PENDING = "pending" - STATE_QUEUED = "queued" - - -class TaskTypeEnum(str, Enum): - CLEAN = "clean" - LOOP = "loop" - DELIVERY = "delivery" - - -class TaskSummaryService: - def get_task_state_name(self, state: int): - if state == 0: - return TaskStateEnum.STATE_ACTIVE - elif state == 1: - return TaskStateEnum.STATE_CANCELLED - elif state == 2: - return TaskStateEnum.STATE_COMPLETED - elif state == 3: - return TaskStateEnum.STATE_FAILED - elif state == 4: - return TaskStateEnum.STATE_PENDING - elif state == 5: - return TaskStateEnum.STATE_QUEUED - - def get_task_type_name(self, type: int): - if type == 0: - return TaskTypeEnum.CLEAN - elif type == 1: - return TaskTypeEnum.LOOP - elif type == 2: - return TaskTypeEnum.DELIVERY - - -class Time: - sec = fields.IntField(pk=True) - nanosec = fields.IntField(pk=True) - - -class Priority: - value = fields.IntField(pk=True) - - -class TaskType: - type = TaskTypeEnum = fields.CharEnumField(TaskTypeEnum) - - -class Station: - task_id = fields.CharField(max_length=50) - robot_type = fields.CharField(max_length=50) - place_name = fields.CharField(max_length=50) - - -class Loop: - task_id = fields.CharField(max_length=50) - robot_type = fields.CharField(max_length=50) - num_loops = fields.IntField(max_length=50) - start_name = fields.CharField(max_length=50) - finish_name = fields.CharField(max_length=50) - - -class Behavior: - name = fields.CharField(max_length=50) - parameters = [] - - -class Delivery: - task_id = fields.CharField(max_length=50) - items = [] - pickup_place_name = fields.CharField(max_length=50) - pickup_dispenser = fields.CharField(max_length=50) - pickup_behavior: Behavior = fields.JSONField() - dropoff_place_name = fields.CharField(max_length=50) - dropoff_ingestor = fields.CharField(max_length=50) - dropoff_behavior: Behavior = fields.JSONField() - - -class Clean: - start_waypoint = fields.CharField(max_length=50) - - -class Description: - start_time: Time = fields.JSONField() - priority: Priority = fields.JSONField() - task_type: TaskType = fields.JSONField() - station: Station = fields.JSONField() - loop: Loop = fields.JSONField() - delivery: Delivery = fields.JSONField() - clean: Clean = fields.JSONField() - - -class TaskProfile: - task_id = fields.CharField(max_length=50) - submission_time: Time = fields.JSONField() - description: Description = fields.JSONField() - - -class TaskSummary(models.Model): - id = fields.IntField(pk=True) - created = fields.DatetimeField(auto_now_add=True) - fleet = fields.ForeignKeyField( - "models.Fleet", related_name="task_summary", null=True - ) - robot = fields.ForeignKeyField( - "models.Robot", related_name="task_summary", null=True - ) - task_id = fields.CharField(max_length=50) - task_profile: TaskProfile = fields.JSONField() - state: TaskStateEnum = fields.CharEnumField( - TaskStateEnum, default=TaskStateEnum.STATE_PENDING - ) - status = fields.CharField(max_length=50, null=True) - submission_time: Time = fields.JSONField() - start_time: Time = fields.JSONField() - end_time: Time = fields.JSONField() - - service = TaskSummaryService() diff --git a/packages/reporting-server/models/tortoise_models/user.py b/packages/reporting-server/models/tortoise_models/user.py deleted file mode 100644 index 5c3307fd8..000000000 --- a/packages/reporting-server/models/tortoise_models/user.py +++ /dev/null @@ -1,11 +0,0 @@ -from tortoise import fields, models -from tortoise.contrib.pydantic import pydantic_model_creator - - -class User(models.Model): - id = fields.IntField(pk=True) - username = fields.TextField(null=True) - user_id = fields.TextField(null=True) - - def __str__(self): - return str(self.username) diff --git a/packages/reporting-server/package.json b/packages/reporting-server/package.json deleted file mode 100644 index d2cc1dce9..000000000 --- a/packages/reporting-server/package.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "reporting-server", - "version": "0.0.1", - "description": "dummy package", - "private": true, - "scripts": { - "prepack": "pipenv run python setup.py bdist_wheel", - "start": "pipenv run python -m reporting_server", - "test": "python3 -m pipenv run python -m unittest", - "test:cov": "RMF_API_SERVER_LOG_LEVEL=CRITICAL RMF_API_SERVER_TEST_COVERAGE=1 pipenv run python -m coverage run -p -m unittest && pipenv run python -m coverage combine", - "test:report": "pipenv run python -m coverage html && xdg-open htmlcov/index.html", - "lint": "python3 -m pipenv run pylint rest_server" - }, - "devDependencies": { - "pipenv-install": "file:../../pipenv-install" - } -} diff --git a/packages/reporting-server/parsers/__init__.py b/packages/reporting-server/parsers/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/packages/reporting-server/parsers/auth_event_parser.py b/packages/reporting-server/parsers/auth_event_parser.py deleted file mode 100644 index bd147a624..000000000 --- a/packages/reporting-server/parsers/auth_event_parser.py +++ /dev/null @@ -1,16 +0,0 @@ -import json - - -async def auth_event_parser(fullstring: str): - splitted_string = fullstring.split("JSON_EVENT::") - modified_string = splitted_string[1] - state_json = json.loads(modified_string) - - return { - "username": state_json.get("username", None), - "user_keycloak_id": state_json.get("userId", None), - "event_type": state_json["type"], - "realm_id": state_json.get("realmId", None), - "client_id": state_json.get("clientId", None), - "ip_address": state_json["ipAddress"], - } diff --git a/packages/reporting-server/parsers/dispenser_state_parser.py b/packages/reporting-server/parsers/dispenser_state_parser.py deleted file mode 100644 index bc88537bd..000000000 --- a/packages/reporting-server/parsers/dispenser_state_parser.py +++ /dev/null @@ -1,13 +0,0 @@ -import json - -from models.tortoise_models.dispenser_state import DispenserState - - -async def dispenser_state_parser(fullstring: str): - modified_string = fullstring.replace("dispenser_state:", "") - state_json = json.loads(modified_string) - - return { - "state": DispenserState.service.get_state_name(state_json["mode"]), - "guid": state_json["guid"], - } diff --git a/packages/reporting-server/parsers/doors_state_parser.py b/packages/reporting-server/parsers/doors_state_parser.py deleted file mode 100644 index d1d81a241..000000000 --- a/packages/reporting-server/parsers/doors_state_parser.py +++ /dev/null @@ -1,15 +0,0 @@ -import json - -from models.tortoise_models.door_state import DoorState - - -async def doors_state_parser(fullstring: str): - modified_string = fullstring.replace("door_state:", "") - door_state_json = json.loads(modified_string) - - return { - "state": DoorState.service.get_state_name( - door_state_json["current_mode"]["value"] - ), - "name": door_state_json["door_name"], - } diff --git a/packages/reporting-server/parsers/fleet_state_parser.py b/packages/reporting-server/parsers/fleet_state_parser.py deleted file mode 100644 index 5b2d6d4cc..000000000 --- a/packages/reporting-server/parsers/fleet_state_parser.py +++ /dev/null @@ -1,31 +0,0 @@ -import json - -from models.tortoise_models import FleetState - - -async def fleet_state_parser(fullstring: str) -> list: - modified_string = fullstring.replace("fleet_state:", "") - state_json = json.loads(modified_string) - - if not isinstance(state_json["robots"], list) or len(state_json["robots"]) == 0: - print("robots should be a list") - return [] - - fleet_list = [] - for robot in state_json["robots"]: - fleet_list.append( - { - "fleet_name": state_json["name"], - "robot_battery_percent": robot["battery_percent"], - "robot_location": robot["location"], - "robot_mode": FleetState.service.get_robot_state_name( - robot["mode"]["mode"] - ), - "robot_model": robot["model"], - "robot_name": robot["name"], - "robot_seq": robot["seq"], - "robot_task_id": robot["task_id"], - } - ) - - return fleet_list diff --git a/packages/reporting-server/parsers/health_parser.py b/packages/reporting-server/parsers/health_parser.py deleted file mode 100644 index e5690dd21..000000000 --- a/packages/reporting-server/parsers/health_parser.py +++ /dev/null @@ -1,17 +0,0 @@ -import json - -from models.tortoise_models.health import HealthStatus - - -async def health_status_parser(fullstring: str, health_device: str): - modified_string = fullstring.replace(health_device + ":", "") - state_json = json.loads(modified_string) - - return { - "device": health_device, - "actor_id": state_json["id"], - "health_status": HealthStatus.service.get_health_status( - state_json["health_status"] - ), - "health_message": state_json["health_message"], - } diff --git a/packages/reporting-server/parsers/ingestor_state_parser.py b/packages/reporting-server/parsers/ingestor_state_parser.py deleted file mode 100644 index 5b1e096b1..000000000 --- a/packages/reporting-server/parsers/ingestor_state_parser.py +++ /dev/null @@ -1,13 +0,0 @@ -import json - -from models.tortoise_models.ingestor_state import IngestorState - - -async def ingestor_state_parser(fullstring: str): - modified_string = fullstring.replace("ingestor_state:", "") - state_json = json.loads(modified_string) - - return { - "state": IngestorState.service.get_state_name(state_json["mode"]), - "guid": state_json["guid"], - } diff --git a/packages/reporting-server/parsers/lift_state_parser.py b/packages/reporting-server/parsers/lift_state_parser.py deleted file mode 100644 index 3e50ee798..000000000 --- a/packages/reporting-server/parsers/lift_state_parser.py +++ /dev/null @@ -1,37 +0,0 @@ -import json - -from models.tortoise_models.lift_state import LiftState - - -async def lift_state_parser(fullstring: str): - modified_string = fullstring.replace("lift_state:", "") - state_json = json.loads(modified_string) - - # In case it is wrapped in a dict - motion_state = LiftState.service.get_motion_state_name( - state_json["motion_state"]["value"] - if isinstance(state_json["motion_state"], dict) - else state_json["motion_state"] - ) - - door_state = LiftState.service.get_door_state_name( - state_json["door_state"]["value"] - if isinstance(state_json["door_state"], dict) - else state_json["door_state"] - ) - - state = LiftState.service.get_state_name( - state_json["current_mode"]["value"] - if isinstance(state_json["current_mode"], dict) - else state_json["current_mode"] - ) - - return { - "name": state_json["lift_name"], - "state": state, - "motion_state": motion_state, - "door_state": door_state, - "session_id": state_json["session_id"], - "destination_floor": state_json["destination_floor"], - "current_floor": state_json["current_floor"], - } diff --git a/packages/reporting-server/parsers/log_type_parser.py b/packages/reporting-server/parsers/log_type_parser.py deleted file mode 100644 index 39a5b366e..000000000 --- a/packages/reporting-server/parsers/log_type_parser.py +++ /dev/null @@ -1,19 +0,0 @@ -from models.tortoise_models.raw_log import LogLevel - - -def get_log_type(fullstring, stream_type="stdout"): - - if "critical" in fullstring.lower(): - return LogLevel.CRITICAL - elif "error" in fullstring.lower(): - return LogLevel.ERROR - elif "warn" in fullstring.lower(): - return LogLevel.WARN - elif "info" in fullstring.lower(): - return LogLevel.INFO - elif "debug" in fullstring.lower(): - return LogLevel.DEBUG - else: - if stream_type == "stderr": - return LogLevel.ERROR - return LogLevel.DEBUG diff --git a/packages/reporting-server/parsers/task_summary_parser.py b/packages/reporting-server/parsers/task_summary_parser.py deleted file mode 100644 index 1b5aa8ff2..000000000 --- a/packages/reporting-server/parsers/task_summary_parser.py +++ /dev/null @@ -1,27 +0,0 @@ -import json - -from models.tortoise_models.task_summary import TaskSummary - - -async def task_summary_parser(fullstring: str): - modified_string = fullstring.replace("task_summary:", "") - state_json = json.loads(modified_string) - - if len(state_json) > 0: - if len(state_json["status"]) <= 0: - status = None - else: - status = state_json["status"] - return { - "fleet_name": state_json["fleet_name"], - "task_id": state_json["task_id"], - "task_profile": state_json["task_profile"], - "state": TaskSummary.service.get_task_state_name(state_json["state"]), - "status": status, - "submission_time": state_json["submission_time"], - "start_time": state_json["start_time"], - "end_time": state_json["end_time"], - "robot_name": state_json["robot_name"], - } - else: - return {} diff --git a/packages/reporting-server/parsers/test_auth_event_parser.py b/packages/reporting-server/parsers/test_auth_event_parser.py deleted file mode 100644 index 1a6e31d63..000000000 --- a/packages/reporting-server/parsers/test_auth_event_parser.py +++ /dev/null @@ -1,21 +0,0 @@ -import unittest - -from rest_server.__mocks__ import raw_data - -from .auth_event_parser import auth_event_parser - - -class TestCaseAuthEvent(unittest.IsolatedAsyncioTestCase): - def setUp(self): - self.data = raw_data.mock_keycloak_login_error["log"] - - async def test_parse_and_get_values(self): - parsed_values = await auth_event_parser(self.data) - self.assertEqual(parsed_values["username"], "test") - self.assertEqual(parsed_values["user_keycloak_id"], None) - self.assertEqual(parsed_values["event_type"], "LOGIN_ERROR") - self.assertEqual( - parsed_values["realm_id"], "579ce396-83c7-4094-964d-7ea07553089f" - ) - self.assertEqual(parsed_values["client_id"], "reporting") - self.assertEqual(parsed_values["ip_address"], "192.168.49.1") diff --git a/packages/reporting-server/parsers/test_dispenser_state_parser.py b/packages/reporting-server/parsers/test_dispenser_state_parser.py deleted file mode 100644 index 9d196224a..000000000 --- a/packages/reporting-server/parsers/test_dispenser_state_parser.py +++ /dev/null @@ -1,20 +0,0 @@ -import unittest - -from models.tortoise_models.dispenser_state import DispenserState -from rest_server.__mocks__.parsed_data import mock_dispenser_state - -from .dispenser_state_parser import dispenser_state_parser - - -class TestCaseDispenserState(unittest.IsolatedAsyncioTestCase): - def setUp(self): - - self.data = mock_dispenser_state - - async def test_parse_and_get_values(self): - parsed_values = await dispenser_state_parser(self.data) - self.assertEqual( - parsed_values["state"], DispenserState.service.get_state_name(0) - ) - - self.assertEqual(parsed_values["guid"], "coke_dispenser") diff --git a/packages/reporting-server/parsers/test_doors_state_parser.py b/packages/reporting-server/parsers/test_doors_state_parser.py deleted file mode 100644 index 2a52742f1..000000000 --- a/packages/reporting-server/parsers/test_doors_state_parser.py +++ /dev/null @@ -1,16 +0,0 @@ -import unittest - -from models.tortoise_models.door_state import DoorState -from rest_server.__mocks__.parsed_data import mock_door_state - -from .doors_state_parser import doors_state_parser - - -class TestCaseDoorsState(unittest.IsolatedAsyncioTestCase): - def setUp(self): - self.data = mock_door_state - - async def test_parse_and_get_values(self): - parsed_values = await doors_state_parser(self.data) - self.assertEqual(parsed_values["state"], DoorState.service.get_state_name(0)) - self.assertEqual(parsed_values["name"], "hardware_door") diff --git a/packages/reporting-server/parsers/test_fleet_state_parser.py b/packages/reporting-server/parsers/test_fleet_state_parser.py deleted file mode 100644 index f600335a4..000000000 --- a/packages/reporting-server/parsers/test_fleet_state_parser.py +++ /dev/null @@ -1,44 +0,0 @@ -import unittest - -from models.tortoise_models.fleet_state import FleetState -from rest_server.__mocks__.parsed_data import mock_fleet_state - -from .fleet_state_parser import fleet_state_parser - - -class TestCaseFleetState(unittest.IsolatedAsyncioTestCase): - def setUp(self): - self.data = mock_fleet_state - - async def test_parse_and_get_values(self): - parsed_values = await fleet_state_parser(self.data) - - self.assertEqual(len(parsed_values), 2) - first_robot = parsed_values[0] - second_robot = parsed_values[1] - - self.assertEqual(first_robot["fleet_name"], "tinyRobot") - self.assertEqual(first_robot["robot_name"], "tinyRobot1") - self.assertEqual(first_robot["robot_model"], "") - self.assertEqual(first_robot["robot_task_id"], "") - self.assertEqual(first_robot["robot_battery_percent"], 100.0) - self.assertEqual(first_robot["robot_seq"], 3190) - self.assertEqual( - first_robot["robot_mode"], FleetState.service.get_robot_state_name(1) - ) - - self.assertEqual(second_robot["fleet_name"], "tinyRobot") - self.assertEqual(second_robot["robot_name"], "tinyRobot2") - self.assertEqual(second_robot["robot_model"], "") - self.assertEqual(second_robot["robot_task_id"], "") - self.assertEqual(second_robot["robot_battery_percent"], 100.0) - self.assertEqual(second_robot["robot_seq"], 3191) - self.assertEqual( - second_robot["robot_mode"], FleetState.service.get_robot_state_name(1) - ) - - async def test_return_empty_list_if_no_robot(self): - parsed_values = await fleet_state_parser( - 'fleet_state:{"name": "tinyRobot", "robots":[]}' - ) - self.assertEqual(parsed_values, []) diff --git a/packages/reporting-server/parsers/test_health_parser.py b/packages/reporting-server/parsers/test_health_parser.py deleted file mode 100644 index db6b72535..000000000 --- a/packages/reporting-server/parsers/test_health_parser.py +++ /dev/null @@ -1,17 +0,0 @@ -import unittest - -from models.tortoise_models.health import HealthStatusEmun - -from .health_parser import health_status_parser - - -class TestCaseHealth(unittest.IsolatedAsyncioTestCase): - def setUp(self): - self.data = 'door_health:{"id": "hardware_door", "health_status": "HealthStatus.HEALTHY", "health_message": null}\n' - - async def test_parse_and_get_values(self): - parsed_values = await health_status_parser(self.data, "door_health") - self.assertEqual(parsed_values["device"], "door_health") - self.assertEqual(parsed_values["actor_id"], "hardware_door") - self.assertEqual(parsed_values["health_status"], HealthStatusEmun.HEALTHY) - self.assertEqual(parsed_values["health_message"], None) diff --git a/packages/reporting-server/parsers/test_ingestor_parser.py b/packages/reporting-server/parsers/test_ingestor_parser.py deleted file mode 100644 index 5d0126903..000000000 --- a/packages/reporting-server/parsers/test_ingestor_parser.py +++ /dev/null @@ -1,18 +0,0 @@ -import unittest - -from models.tortoise_models.ingestor_state import IngestorState -from rest_server.__mocks__.parsed_data import mock_ingestor_state - -from .ingestor_state_parser import ingestor_state_parser - - -class TestCaseIngestorState(unittest.IsolatedAsyncioTestCase): - def setUp(self): - self.data = mock_ingestor_state - - async def test_parse_and_get_values(self): - parsed_values = await ingestor_state_parser(self.data) - self.assertEqual(parsed_values["guid"], "coke_ingestor") - self.assertEqual( - parsed_values["state"], IngestorState.service.get_state_name(0) - ) diff --git a/packages/reporting-server/parsers/test_lift_state_parser.py b/packages/reporting-server/parsers/test_lift_state_parser.py deleted file mode 100644 index e5dc6ec9a..000000000 --- a/packages/reporting-server/parsers/test_lift_state_parser.py +++ /dev/null @@ -1,38 +0,0 @@ -import unittest - -from models.tortoise_models.lift_state import LiftState -from rest_server.__mocks__.parsed_data import mock_lift_state - -from .lift_state_parser import lift_state_parser - - -class TestCaseLiftState(unittest.IsolatedAsyncioTestCase): - def setUp(self): - self.data = mock_lift_state - self.data_with_dict_states = 'lift_state: {"lift_name": "test_lift", "lift_time": 0, "available_floors": ["L1", "L2"], "current_floor": "L1", "destination_floor": "L2", "door_state": {"value": 0}, "motion_state": {"value": 0}, "available_modes": [0], "current_mode": {"value": 0}, "session_id": "test_session"}\n' - - async def test_parse_and_get_values(self): - parsed_values = await lift_state_parser(self.data) - self.assertEqual(parsed_values["state"], LiftState.service.get_state_name(0)) - self.assertEqual( - parsed_values["motion_state"], LiftState.service.get_motion_state_name(0) - ) - self.assertEqual( - parsed_values["door_state"], LiftState.service.get_door_state_name(0) - ) - - self.assertEqual(parsed_values["current_floor"], "L1") - self.assertEqual(parsed_values["destination_floor"], "L2") - self.assertEqual(parsed_values["session_id"], "test_session") - - self.assertEqual(parsed_values["state"], LiftState.service.get_state_name(0)) - - async def test_parse_and_get_values_with_dict_states(self): - parsed_values = await lift_state_parser(self.data_with_dict_states) - self.assertEqual(parsed_values["state"], LiftState.service.get_state_name(0)) - self.assertEqual( - parsed_values["motion_state"], LiftState.service.get_motion_state_name(0) - ) - self.assertEqual( - parsed_values["door_state"], LiftState.service.get_door_state_name(0) - ) diff --git a/packages/reporting-server/parsers/test_log_type_parser.py b/packages/reporting-server/parsers/test_log_type_parser.py deleted file mode 100644 index 21ca3fdb2..000000000 --- a/packages/reporting-server/parsers/test_log_type_parser.py +++ /dev/null @@ -1,37 +0,0 @@ -import unittest - -from models.tortoise_models.raw_log import LogLevel - -from .log_type_parser import get_log_type - - -class TestCaseLogTypeParserCase(unittest.TestCase): - def setUp(self): - pass - - def test_warning_messages(self): - self.assertEqual(get_log_type("this is a Warn", "stdout"), LogLevel.WARN) - self.assertEqual(get_log_type("WARN:", "stdout"), LogLevel.WARN) - self.assertEqual(get_log_type("WARNING:", "stdout"), LogLevel.WARN) - self.assertEqual(get_log_type("warn", "stdout"), LogLevel.WARN) - - def test_error_messages(self): - self.assertEqual(get_log_type("this is an Error", "stdout"), LogLevel.ERROR) - self.assertEqual(get_log_type("ERROR:", "stdout"), LogLevel.ERROR) - self.assertEqual(get_log_type("error", "stdout"), LogLevel.ERROR) - self.assertEqual( - get_log_type("normal test with problems", "stderr"), LogLevel.ERROR - ) - - def test_debug_messages(self): - self.assertEqual(get_log_type("this is a Debug msg", "stdout"), LogLevel.DEBUG) - self.assertEqual(get_log_type("DEBUG:", "stdout"), LogLevel.DEBUG) - self.assertEqual(get_log_type("debug", "stdout"), LogLevel.DEBUG) - self.assertEqual( - get_log_type("This is a random text", "stdout"), LogLevel.DEBUG - ) - - def test_info_messages(self): - self.assertEqual(get_log_type("this is an Info msg", "stdout"), LogLevel.INFO) - self.assertEqual(get_log_type("INFO:", "stdout"), LogLevel.INFO) - self.assertEqual(get_log_type("info", "stdout"), LogLevel.INFO) diff --git a/packages/reporting-server/parsers/test_task_summary_parser.py b/packages/reporting-server/parsers/test_task_summary_parser.py deleted file mode 100644 index fae6a945d..000000000 --- a/packages/reporting-server/parsers/test_task_summary_parser.py +++ /dev/null @@ -1,40 +0,0 @@ -import unittest - -from models.tortoise_models.task_summary import TaskSummary -from rest_server.__mocks__.parsed_data import mock_task_summary - -from .task_summary_parser import task_summary_parser - - -class TestCaseTaskSummary(unittest.IsolatedAsyncioTestCase): - def setUp(self): - self.data = mock_task_summary - - async def test_parse_and_get_values(self): - parsed_values = await task_summary_parser(self.data) - - self.assertEqual(parsed_values["task_id"], "Loop0") - self.assertEqual(parsed_values["fleet_name"], "tinyRobot") - self.assertEqual( - parsed_values["state"], TaskSummary.service.get_task_state_name(0) - ) - self.assertEqual(parsed_values["status"], None) - self.assertEqual( - parsed_values["task_profile"]["submission_time"], - {"sec": 131, "nanosec": 553000000}, - ) - self.assertEqual(parsed_values["submission_time"], {"sec": 0, "nanosec": 0}) - self.assertEqual( - parsed_values["start_time"], {"sec": 1623383362, "nanosec": 348338289} - ) - self.assertEqual( - parsed_values["end_time"], {"sec": 1623383449, "nanosec": 79154833} - ) - self.assertEqual(parsed_values["robot_name"], "tinyRobot2") - self.assertEqual( - parsed_values["task_profile"]["description"]["task_type"]["type"], 1 - ) - - async def test_return_empty_list_if_no_task(self): - parsed_values = await task_summary_parser("task_summary:{}") - self.assertEqual(parsed_values, {}) diff --git a/packages/reporting-server/rest_server/__init__.py b/packages/reporting-server/rest_server/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/packages/reporting-server/rest_server/__main__.py b/packages/reporting-server/rest_server/__main__.py deleted file mode 100644 index 1d4896963..000000000 --- a/packages/reporting-server/rest_server/__main__.py +++ /dev/null @@ -1,47 +0,0 @@ -import asyncio - -from uvicorn import Config, Server - -from .app import get_app -from .app_config import SystemMode, app_config - - -class CustomUvicornServer(Server): - def run(self, sockets=None): - self.config.setup_event_loop() - return self.serve(sockets=sockets) - - -async def run(): - apps = [] - config1 = Config( - get_app(SystemMode.FLUENTD), - host=app_config.host, - port=app_config.port_fluentd, - root_path=app_config.public_url.path, - log_level=app_config.log_level.lower(), - ) - config2 = Config( - get_app(SystemMode.REPORT), - host=app_config.host, - port=app_config.port, - root_path=app_config.public_url.path, - log_level=app_config.log_level.lower(), - ) - - server1 = CustomUvicornServer(config=config1) - server2 = CustomUvicornServer(config=config2) - - apps.append(server1.run()) - apps.append(server2.run()) - - return await asyncio.gather(*apps) - - -def main(): - loop = asyncio.get_event_loop() - loop.run_until_complete(run()) - - -if __name__ == "__main__": - main() diff --git a/packages/reporting-server/rest_server/__mocks__/__init__.py b/packages/reporting-server/rest_server/__mocks__/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/packages/reporting-server/rest_server/__mocks__/parsed_data.py b/packages/reporting-server/rest_server/__mocks__/parsed_data.py deleted file mode 100644 index 43e962d76..000000000 --- a/packages/reporting-server/rest_server/__mocks__/parsed_data.py +++ /dev/null @@ -1,23 +0,0 @@ -from . import raw_data - - -def parse_log(log): - modified_log = log["log"].replace("INFO:app.BookKeeper.", "") - return modified_log - - -# States -mock_dispenser_state: str = parse_log(raw_data.mock_dispenser_state) - -mock_door_state: str = parse_log(raw_data.mock_door_state) - -mock_fleet_state: str = parse_log(raw_data.mock_fleet_state) - -mock_task_summary: str = parse_log(raw_data.mock_task_summary) - -mock_ingestor_state: str = parse_log(raw_data.mock_ingestor_state) - -mock_lift_state: str = parse_log(raw_data.mock_lift_state) - -# Health -mock_door_health: str = parse_log(raw_data.mock_door_health) diff --git a/packages/reporting-server/rest_server/__mocks__/raw_data.py b/packages/reporting-server/rest_server/__mocks__/raw_data.py deleted file mode 100644 index 7e7c1812d..000000000 --- a/packages/reporting-server/rest_server/__mocks__/raw_data.py +++ /dev/null @@ -1,67 +0,0 @@ -# States -mock_dispenser_state = { - "log": 'INFO:app.BookKeeper.dispenser_state:{"time": {"sec": 1600, "nanosec": 0}, "guid": "coke_dispenser", "mode": 0, "request_guid_queue": [], "seconds_remaining": 0.0}\n', - "stream": "stdout", - "kubernetes": { - "container_name": "app-that-writes-logs", - "namespace_name": "default", - "pod_name": "app-that-writes-logs", - "container_image": "busybox:latest", - "container_image_id": "docker-pullable://busybox@sha256:ae39a6f5c07297d7ab64dbd4f82c77c874cc6a94cea29fdec309d0992574b4f7", - "pod_id": "978761c6-2a19-422f-b710-d43da2348f1f", - "host": "minikube", - "master_url": "https://10.96.0.1:443/api", - "namespace_id": "e192acd4-e6e7-46c2-8514-44a27a367749", - }, -} - -mock_door_state = { - "log": 'INFO:app.BookKeeper.door_state:{"door_time": {"sec": 1596, "nanosec": 548000000}, "door_name": "hardware_door", "current_mode": {"value": 0}}\n', - "stream": "stdout", -} - -mock_fleet_state = { - "log": 'INFO:app.BookKeeper.fleet_state:{"name": "tinyRobot", "robots": [{"name": "tinyRobot1", "model": "", "task_id": "", "seq": 3190, "mode": {"mode": 1, "mode_request_id": 0}, "battery_percent": 100.0, "location": {"t": {"sec": 1598, "nanosec": 184999999}, "x": 11.553672790527344, "y": -11.317496299743652, "yaw": -1.599777340888977, "level_name": "L1", "index": 0}, "path": []}, {"name": "tinyRobot2", "model": "", "task_id": "", "seq": 3191, "mode": {"mode": 1, "mode_request_id": 0}, "battery_percent": 100.0, "location": {"t": {"sec": 1598, "nanosec": 685999999}, "x": 15.157517433166504, "y": -11.228611946105957, "yaw": -1.5839587450027466, "level_name": "L1", "index": 0}, "path": []}]}\n', - "stream": "stdout", -} - -mock_task_summary = { - "log": 'INFO:app.BookKeeper.task_summary:{"fleet_name": "tinyRobot", "task_id": "Loop0", "task_profile": {"task_id": "Loop0", "submission_time": {"sec": 131, "nanosec": 553000000}, "description": {"start_time": {"sec": 1623383402, "nanosec": 0}, "priority": {"value": 0}, "task_type": {"type": 1}, "station": {"task_id": "", "robot_type": "", "place_name": ""}, "loop": {"task_id": "", "robot_type": "", "num_loops": 1, "start_name": "supplies", "finish_name": "coe"}, "delivery": {"task_id": "", "items": [], "pickup_place_name": "", "pickup_dispenser": "", "pickup_behavior": {"name": "", "parameters": []}, "dropoff_place_name": "", "dropoff_ingestor": "", "dropoff_behavior": {"name": "", "parameters": []}}, "clean": {"start_waypoint": ""}}}, "state": 0, "status": "", "submission_time": {"sec": 0, "nanosec": 0}, "start_time": {"sec": 1623383362, "nanosec": 348338289}, "end_time": {"sec": 1623383449, "nanosec": 79154833}, "robot_name": "tinyRobot2"}\n', - "stream": "stdout", -} - -mock_ingestor_state = { - "log": 'INFO:app.BookKeeper.ingestor_state:{"time": {"sec": 1600, "nanosec": 0}, "guid": "coke_ingestor", "mode": 0, "request_guid_queue": [], "seconds_remaining": 0.0}\n', - "stream": "stdout", -} - -mock_lift_state = { - "log": 'INFO:app.BookKeeper.lift_state: {"lift_name": "test_lift", "lift_time": 0, "available_floors": ["L1", "L2"], "current_floor": "L1", "destination_floor": "L2", "door_state": 0, "motion_state": 0, "available_modes": [0], "current_mode": 0, "session_id": "test_session"}\n', - "stream": "stdout", -} - - -# Health -mock_door_health = { - "log": 'INFO:app.BookKeeper.door_health:{"id": "hardware_door", "health_status": "HealthStatus.HEALTHY", "health_message": null}\n', - "stream": "stdout", -} - -# Keycloak -mock_keycloak_login_error = { - "log": '20:41:54,721 INFO [org.keycloak.events] (default task-2) JSON_EVENT::{"type":"LOGIN_ERROR","realmId":"579ce396-83c7-4094-964d-7ea07553089f","clientId":"reporting","ipAddress":"192.168.49.1","error":"user_not_found","auth_method":"openid-connect","auth_type":"code","redirect_uri":"https://example.com/reporting","code_id":"f813403c-2732-4062-9911-cf65b89a2278","username":"test"}', - "stream": "stdout", - "kubernetes": { - "container_name": "app-that-writes-logs", - }, -} - -mock_keycloak_login = { - "log": '19:47:08,004 INFO [org.keycloak.events] (default task-3) JSON_EVENT::{"type":"LOGIN","realmId":"master","clientId":"security-admin-console","userId":"7d2f3cdd-9778-4847-ab9d-db68f70f043f","ipAddress":"172.22.0.1","auth_method":"openid-connect","auth_type":"code","redirect_uri":"http://localhost:8080/auth/admin/master/console/","consent":"no_consent_required","code_id":"ac8c82d7-45ac-4227-86d3-e167b176e26f","username":"admin"}', - "stream": "stdout", -} - -mock_keycloak_logout = { - "log": '19:47:20,649 INFO [org.keycloak.events] (default task-6) JSON_EVENT::{"type":"LOGOUT","realmId":"master","userId":"7d2f3cdd-9778-4847-ab9d-db68f70f043f","ipAddress":"172.22.0.1","redirect_uri":"http://localhost:8080/auth/admin/master/console/#/realms/master"}', - "stream": "stdout", -} diff --git a/packages/reporting-server/rest_server/app.py b/packages/reporting-server/rest_server/app.py deleted file mode 100644 index 4c38d2172..000000000 --- a/packages/reporting-server/rest_server/app.py +++ /dev/null @@ -1,53 +0,0 @@ -# conflicts with isort because of local non-relative import -# pylint: disable=wrong-import-order - -import logging -import os -import sys - -from dependencies import auth_scheme, logger -from fastapi import Depends, FastAPI -from fastapi.middleware.cors import CORSMiddleware -from rest_server.database import setup_database -from rest_server.routers import log_router, report_router - -from .app_config import SystemMode - -logger = logging.getLogger("rest_app") -handler = logging.StreamHandler(sys.stdout) -handler.setFormatter(logging.Formatter(logging.BASIC_FORMAT)) -logger.addHandler(handler) -if "RMF_REST_SERVER_DEBUG" in os.environ: - logger.setLevel(logging.DEBUG) -else: - logger.setLevel(logging.INFO) - -logger.info("started app") - - -def get_app(run_config=SystemMode.ALL): - - app = FastAPI() - - app.add_middleware( - CORSMiddleware, - allow_origins=["*"], - allow_credentials=False, - allow_methods=["*"], - allow_headers=["*"], - ) - - if run_config in (SystemMode.ALL, SystemMode.FLUENTD): - app.include_router(log_router, prefix="/log", tags=["log"]) - - if run_config in (SystemMode.ALL, SystemMode.REPORT): - app.include_router( - report_router, - prefix="/report", - tags=["report"], - dependencies=[Depends(auth_scheme)], - ) - - setup_database(app) - - return app diff --git a/packages/reporting-server/rest_server/app_config.py b/packages/reporting-server/rest_server/app_config.py deleted file mode 100644 index 7524d79de..000000000 --- a/packages/reporting-server/rest_server/app_config.py +++ /dev/null @@ -1,46 +0,0 @@ -import importlib.util -import os -import urllib.parse -from dataclasses import dataclass -from enum import Enum -from typing import Optional - - -class SystemMode(Enum): - ALL = 1 - REPORT = 2 - FLUENTD = 3 - - -@dataclass -class AppConfig: - host: str - port: int - port_fluentd: int - db_url: str - public_url: urllib.parse.ParseResult - static_directory: str - log_level: str - jwt_public_key: Optional[str] - oidc_url: Optional[str] - aud: str - iss: Optional[str] - log_storage_time: int - - def __post_init__(self): - self.public_url = urllib.parse.urlparse(self.public_url) - - -def _load_config() -> AppConfig: - if "RMF_REPORT_REST_SERVER_CONFIG" in os.environ: - config_file = os.environ["RMF_REPORT_REST_SERVER_CONFIG"] - else: - config_file = f"{os.path.dirname(__file__)}/default_config.py" - - spec = importlib.util.spec_from_file_location("config", config_file) - module = importlib.util.module_from_spec(spec) - spec.loader.exec_module(module) - return AppConfig(**module.config) - - -app_config = _load_config() diff --git a/packages/reporting-server/rest_server/authenticator.py b/packages/reporting-server/rest_server/authenticator.py deleted file mode 100644 index 5d24f4cd3..000000000 --- a/packages/reporting-server/rest_server/authenticator.py +++ /dev/null @@ -1,30 +0,0 @@ -import jwt - -from .app_config import app_config - - -class AuthenticationError(Exception): - pass - - -class JwtAuthenticator: - def __init__(self, pem_file: str): - """ - Authenticates with a JWT token, the client must send an auth params with - a "token" key. - :param pem_file: path to a pem encoded certificate used to verify a token. - """ - with open(pem_file, "br") as f: - self._public_key = f.read() - - def verify_token(self, token: str): - try: - jwt.decode( - token, - self._public_key, - algorithms=["RS256"], - audience=app_config.aud, - issuer=app_config.iss, - ) - except jwt.InvalidTokenError as e: - raise AuthenticationError(str(e)) from e diff --git a/packages/reporting-server/rest_server/clean_logs.py b/packages/reporting-server/rest_server/clean_logs.py deleted file mode 100644 index 04daf2d2f..000000000 --- a/packages/reporting-server/rest_server/clean_logs.py +++ /dev/null @@ -1,85 +0,0 @@ -# conflicts with isort because of local non-relative import -# pylint: disable=wrong-import-order - - -import logging -import os -import sys -from datetime import datetime, timedelta - -from dependencies import logger -from models.tortoise_models import ( - AuthEvents, - DispenserState, - DoorState, - FleetState, - HealthStatus, - IngestorState, - LiftState, - RawLog, - TaskSummary, -) -from tortoise import Tortoise, run_async - -from .app_config import app_config - -logger = logging.getLogger("clean_script") -handler = logging.StreamHandler(sys.stdout) -handler.setFormatter(logging.Formatter(logging.BASIC_FORMAT)) -logger.addHandler(handler) -if "RMF_REST_SERVER_DEBUG" in os.environ: - logger.setLevel(logging.DEBUG) -else: - logger.setLevel(logging.INFO) - -logger.info("started clean up") - -reportingModels = [ - AuthEvents, - DispenserState, - DoorState, - FleetState, - HealthStatus, - IngestorState, - LiftState, - RawLog, - TaskSummary, -] - -number_of_days_to_keep_logs = app_config.log_storage_time + 60 - -logger.info( - "You are about to delete all the logs older than %s days", - str(app_config.log_storage_time), -) - - -async def delete_logs(): - - for model in reportingModels: - rows = await model.filter( - created__lt=datetime.now() - timedelta(days=number_of_days_to_keep_logs) - ) - - logger.info("%s has %s rows > 7 days", str(model.__module__), str(len(rows))) - - await model.filter( - created__lt=datetime.now() - timedelta(days=number_of_days_to_keep_logs) - ).delete() - - -async def run(): - await Tortoise.init( - db_url=app_config.db_url, - modules={"models": ["models.tortoise_models"]}, - ) - await Tortoise.generate_schemas() - await delete_logs() - - -def main(): - run_async(run()) - - -if __name__ == "__main__": - main() diff --git a/packages/reporting-server/rest_server/database.py b/packages/reporting-server/rest_server/database.py deleted file mode 100644 index b55b6e61f..000000000 --- a/packages/reporting-server/rest_server/database.py +++ /dev/null @@ -1,18 +0,0 @@ -from fastapi import FastAPI -from tortoise import Tortoise -from tortoise.contrib.fastapi import register_tortoise - -from .app_config import app_config - - -def setup_database(app: FastAPI): - register_tortoise( - app, - db_url=app_config.db_url, - modules={"models": ["models.tortoise_models"]}, - generate_schemas=True, - add_exception_handlers=True, - ) - - -Tortoise.init_models(["models.tortoise_models"], "models") diff --git a/packages/reporting-server/rest_server/default_config.py b/packages/reporting-server/rest_server/default_config.py deleted file mode 100644 index f843f5875..000000000 --- a/packages/reporting-server/rest_server/default_config.py +++ /dev/null @@ -1,30 +0,0 @@ -# pylint: disable=line-too-long -config = { - "host": "127.0.0.1", # ip or hostname to bind the socket to - "port": 8002, - "port_fluentd": 8003, - "db_url": "sqlite://:memory:", - # url that reporting-server is being served on. - # When being a proxy, this must be the url that reporting-server is mounted on. - # E.g. https://example.com/logserver/api/v1 - "public_url": "http://localhost:8002", - # The directory where static files should be stored. - "static_directory": "static", - "log_level": "INFO", # https://docs.python.org/3.8/library/logging.html#levels - # path to a PEM encoded RSA public key which is used to verify JWT tokens, if the path is relative, it is based on the working dir. - "jwt_public_key": None, - # url to the oidc endpoint, used to authenticate rest requests, it should point to the well known endpoint, e.g. - # http://localhost:8080/auth/realms/rmf-web/.well-known/openid-configuration. - # NOTE: This is ONLY used for documentation purposes, the "jwt_public_key" will be the - # only key used to verify a token. - "oidc_url": None, - # Audience the access token is meant for. Can also be an array. - # Used to verify the "aud" claim. - "aud": "localhost", - # url or string that identifies the entity that issued the jwt token - # Used to verify the "iss" claim - # If iss is set to None, it means that authentication should be disabled - "iss": None, - # Number of days to store logs - "log_storage_time": 7, -} diff --git a/packages/reporting-server/rest_server/repositories/__init__.py b/packages/reporting-server/rest_server/repositories/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/packages/reporting-server/rest_server/repositories/log_creation_handler.py b/packages/reporting-server/rest_server/repositories/log_creation_handler.py deleted file mode 100644 index 441bfc08c..000000000 --- a/packages/reporting-server/rest_server/repositories/log_creation_handler.py +++ /dev/null @@ -1,101 +0,0 @@ -import logging - -from dependencies import logger -from models.tortoise_models.auth_events import AuthEvents -from models.tortoise_models.container import Container -from models.tortoise_models.raw_log import RawLog -from parsers.auth_event_parser import auth_event_parser -from parsers.log_type_parser import get_log_type - -# Function that receives all the logs and store them on the database - -logger = logging.getLogger("rest_app:log_creation_handler") - - -""" -Formats support: - -1. [{log:"text or json", kubernetes:{...} },{log:{...}, kubernetes:{...}] - -2. ['text','text','text'] - -""" - - -class RawLogHandler: - @staticmethod - def _is_valid_request(logs: list): - if len(logs) == 0: - return False - return True - - @staticmethod - async def _dict_raw_log_handler(log: dict): - log_level = get_log_type(log["log"], log["stream"]) - - if "kubernetes" in log and "container_name" in log["kubernetes"]: - container = await Container.get_or_create( - name=log["kubernetes"]["container_name"] - ) - - await RawLog.create( - level=log_level, - message=log["log"], - container=container[0], - ) - else: - await RawLog.create(level=log_level, message=log["log"]) - - @staticmethod - async def _text_raw_log_handler(log: str): - log_level = get_log_type(log) - await RawLog.create(level=log_level, message=log) - - @staticmethod - async def create_raw_log(logs: list): - if not RawLogHandler._is_valid_request(logs): - return "No valid data" - - error_logs = [] - - for log in logs: - try: - if isinstance(log, dict): - if "log" not in log: - error_msg = ( - "Error: format not supported. Failed to create this log " - + str(log) - ) - logger.error(error_msg) - error_logs.append(error_msg) - continue - await RawLogHandler._dict_raw_log_handler(log) - - elif isinstance(log, str): - if log.isspace(): - continue - await RawLogHandler._text_raw_log_handler(log) - - except (SyntaxError, ValueError, KeyError) as e: - error_logs.append("Error:" + str(e) + "Log:" + str(log)) - - return error_logs if len(error_logs) > 0 else "Logs were saved correctly" - - -async def create_keycloak_log(logs: list): - if len(logs) == 0: - return "No data received" - error_logs = [] - - for log in logs: - try: - # If it not data app, we will skip it because the create_raw_log in theory will register that log - if "JSON_EVENT::" not in log["log"]: - continue - auth_event = await auth_event_parser(log["log"]) - await AuthEvents.create(**auth_event) - - except (SyntaxError, ValueError, KeyError) as e: - error_logs.append("Error:" + str(e) + "Log:" + str(log)) - - return error_logs if len(error_logs) > 0 else "Logs were saved correctly" diff --git a/packages/reporting-server/rest_server/repositories/parser_dispatcher.py b/packages/reporting-server/rest_server/repositories/parser_dispatcher.py deleted file mode 100644 index 15ecc6683..000000000 --- a/packages/reporting-server/rest_server/repositories/parser_dispatcher.py +++ /dev/null @@ -1,103 +0,0 @@ -from models.tortoise_models.dispenser_state import DispenserState -from models.tortoise_models.door import Door -from models.tortoise_models.door_state import DoorState -from models.tortoise_models.fleet import Fleet, Robot -from models.tortoise_models.fleet_state import FleetState -from models.tortoise_models.health import Device, HealthStatus -from models.tortoise_models.ingestor_state import IngestorState -from models.tortoise_models.lift import Lift -from models.tortoise_models.lift_state import LiftState -from models.tortoise_models.task_summary import TaskSummary -from parsers.dispenser_state_parser import dispenser_state_parser -from parsers.doors_state_parser import doors_state_parser -from parsers.fleet_state_parser import fleet_state_parser -from parsers.health_parser import health_status_parser -from parsers.ingestor_state_parser import ingestor_state_parser -from parsers.lift_state_parser import lift_state_parser -from parsers.task_summary_parser import task_summary_parser - - -async def create_health_status(data): - device = await Device.get_or_create(actor=data["actor_id"], type=data["device"]) - await HealthStatus.create( - device=device[0], - health_status=data["health_status"], - health_message=data["health_message"], - ) - - -# This function dispatchs to the correct handler dependending on the text content. - - -async def log_model_dispatcher(fullstring: str): - if "dispenser_state:" in fullstring.lower(): - data = await dispenser_state_parser(fullstring) - await DispenserState.create(**data) - - elif "door_state:" in fullstring.lower(): - data = await doors_state_parser(fullstring) - door = await Door.get_or_create(name=data["name"]) - await DoorState.create(state=data["state"], door=door[0]) - - elif "fleet_state:" in fullstring.lower(): - robot_states = await fleet_state_parser(fullstring) - - if len(robot_states) == 0: - return - - for robot_state in robot_states: - fleet = await Fleet.get_or_create(name=robot_state["fleet_name"]) - robot = await Robot.get_or_create( - name=robot_state["robot_name"], model=robot_state["robot_model"] - ) - del robot_state["fleet_name"] - del robot_state["robot_name"] - del robot_state["robot_model"] - - await FleetState.create( - fleet=fleet[0], - robot=robot[0], - **robot_state, - ) - - elif "lift_state:" in fullstring.lower(): - data = await lift_state_parser(fullstring) - lift = await Lift.get_or_create(name=data["name"]) - del data["name"] - await LiftState.create(lift=lift[0], **data) - - elif "ingestor_state:" in fullstring.lower(): - data = await ingestor_state_parser(fullstring) - await IngestorState.create(**data) - - elif "task_summary:" in fullstring.lower(): - data = await task_summary_parser(fullstring) - fleet = await Fleet.get_or_create(name=data["fleet_name"]) - robot = await Robot.get_or_create( - name=data["robot_name"], - ) - del data["fleet_name"] - del data["robot_name"] - await TaskSummary.create(fleet=fleet[0], robot=robot[0], **data) - - # Health - elif "dispenser_health:" in fullstring.lower(): - data = await health_status_parser(fullstring, "dispenser_health") - await create_health_status(data) - - elif "door_health:" in fullstring.lower(): - data = await health_status_parser(fullstring, "door_health") - - await create_health_status(data) - - elif "ingestor_health:" in fullstring.lower(): - data = await health_status_parser(fullstring, "ingestor_health") - await create_health_status(data) - - elif "lift_health:" in fullstring.lower(): - data = await health_status_parser(fullstring, "lift_health") - await create_health_status(data) - - elif "robot_health:" in fullstring.lower(): - data = await health_status_parser(fullstring, "robot_health") - await create_health_status(data) diff --git a/packages/reporting-server/rest_server/repositories/report/__init__.py b/packages/reporting-server/rest_server/repositories/report/__init__.py deleted file mode 100644 index 59716c060..000000000 --- a/packages/reporting-server/rest_server/repositories/report/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -from .auth_event_report import ( - get_user_login_failure_report, - get_user_login_report, - get_user_logout_report, -) -from .dispenser_state import get_dispenser_state -from .door_state import get_door_state -from .fleet_state import get_fleet_state -from .health import get_health -from .ingestor_state import get_ingestor_state -from .lift_state import get_lift_state -from .raw_log import get_all_raw_logs, get_containers -from .task_summary import get_task_summary diff --git a/packages/reporting-server/rest_server/repositories/report/auth_event_report.py b/packages/reporting-server/rest_server/repositories/report/auth_event_report.py deleted file mode 100644 index e028f965d..000000000 --- a/packages/reporting-server/rest_server/repositories/report/auth_event_report.py +++ /dev/null @@ -1,59 +0,0 @@ -from datetime import timezone -from typing import Optional - -from dateutil import parser -from models.pydantic_models import AuthEvents_Pydantic -from models.tortoise_models.auth_events import AuthEvents - - -async def get_user_login_report( - offset: int, - limit: int, - to_log_date: Optional[str] = None, - from_log_date: Optional[str] = None, -): - return await get_auth_events("LOGIN", offset, limit, to_log_date, from_log_date) - - -async def get_user_logout_report( - offset: int, - limit: int, - to_log_date: Optional[str] = None, - from_log_date: Optional[str] = None, -): - return await get_auth_events("LOGOUT", offset, limit, to_log_date, from_log_date) - - -async def get_user_login_failure_report( - offset: int, - limit: int, - to_log_date: Optional[str] = None, - from_log_date: Optional[str] = None, -): - return await get_auth_events( - "LOGIN_ERROR", offset, limit, to_log_date, from_log_date - ) - - -async def get_auth_events( - event_type: str, - offset: int, - limit: int, - to_log_date: Optional[str] = None, - from_log_date: Optional[str] = None, -): - query = {} - query["event_type"] = event_type - if from_log_date: - local_time = parser.parse(from_log_date) - utc_time = local_time.astimezone(timezone.utc) - query["created__gte"] = utc_time - - if to_log_date: - to_log_local_time = parser.parse(to_log_date) - to_log_utc_time = to_log_local_time.astimezone(timezone.utc) - query["created__lt"] = to_log_utc_time - - return await AuthEvents_Pydantic.from_queryset( - AuthEvents.filter(**query).offset(offset).limit(limit).order_by("-created") - ) diff --git a/packages/reporting-server/rest_server/repositories/report/dispenser_state.py b/packages/reporting-server/rest_server/repositories/report/dispenser_state.py deleted file mode 100644 index 32322b4ee..000000000 --- a/packages/reporting-server/rest_server/repositories/report/dispenser_state.py +++ /dev/null @@ -1,18 +0,0 @@ -from typing import Optional - -from models.pydantic_models import DispenserState_Pydantic -from models.tortoise_models.dispenser_state import DispenserState -from rest_server.repositories.report.utils import get_date_range_query - - -async def get_dispenser_state( - offset: int, - limit: int, - to_log_date: Optional[str] = None, - from_log_date: Optional[str] = None, -): - query = get_date_range_query(to_log_date, from_log_date) - - return await DispenserState_Pydantic.from_queryset( - DispenserState.filter(**query).offset(offset).limit(limit).order_by("-created") - ) diff --git a/packages/reporting-server/rest_server/repositories/report/door_state.py b/packages/reporting-server/rest_server/repositories/report/door_state.py deleted file mode 100644 index cb8c1ba99..000000000 --- a/packages/reporting-server/rest_server/repositories/report/door_state.py +++ /dev/null @@ -1,25 +0,0 @@ -from typing import Optional - -from models.pydantic_models import DoorState_Pydantic -from models.tortoise_models.door_state import DoorState -from rest_server.repositories.report.utils import get_date_range_query - - -async def get_door_state( - offset: int, - limit: int, - to_log_date: Optional[str] = None, - from_log_date: Optional[str] = None, -): - - query = get_date_range_query(to_log_date, from_log_date) - - queryset = ( - DoorState.filter(**query) - .prefetch_related("door") - .offset(offset) - .limit(limit) - .order_by("-created") - ) - - return await DoorState_Pydantic.from_queryset(queryset) diff --git a/packages/reporting-server/rest_server/repositories/report/fleet_state.py b/packages/reporting-server/rest_server/repositories/report/fleet_state.py deleted file mode 100644 index 1423e228c..000000000 --- a/packages/reporting-server/rest_server/repositories/report/fleet_state.py +++ /dev/null @@ -1,25 +0,0 @@ -from typing import Optional - -from models.pydantic_models import FleetState_Pydantic -from models.tortoise_models.fleet_state import FleetState -from rest_server.repositories.report.utils import get_date_range_query - - -async def get_fleet_state( - offset: int, - limit: int, - to_log_date: Optional[str] = None, - from_log_date: Optional[str] = None, -): - query = get_date_range_query(to_log_date, from_log_date) - - queryset = ( - FleetState.filter(**query) - .prefetch_related("fleet") - .prefetch_related("robot") - .offset(offset) - .limit(limit) - .order_by("-created") - ) - - return await FleetState_Pydantic.from_queryset(queryset) diff --git a/packages/reporting-server/rest_server/repositories/report/health.py b/packages/reporting-server/rest_server/repositories/report/health.py deleted file mode 100644 index 5d3ca2ca3..000000000 --- a/packages/reporting-server/rest_server/repositories/report/health.py +++ /dev/null @@ -1,24 +0,0 @@ -from typing import Optional - -from models.pydantic_models import HealthStatus_Pydantic -from models.tortoise_models.health import HealthStatus -from rest_server.repositories.report.utils import get_date_range_query - - -async def get_health( - offset: int, - limit: int, - to_log_date: Optional[str] = None, - from_log_date: Optional[str] = None, -): - query = get_date_range_query(to_log_date, from_log_date) - - queryset = ( - HealthStatus.filter(**query) - .prefetch_related("device") - .offset(offset) - .limit(limit) - .order_by("-created") - ) - - return await HealthStatus_Pydantic.from_queryset(queryset) diff --git a/packages/reporting-server/rest_server/repositories/report/ingestor_state.py b/packages/reporting-server/rest_server/repositories/report/ingestor_state.py deleted file mode 100644 index 6c6050bef..000000000 --- a/packages/reporting-server/rest_server/repositories/report/ingestor_state.py +++ /dev/null @@ -1,18 +0,0 @@ -from typing import Optional - -from models.pydantic_models import IngestorState_Pydantic -from models.tortoise_models.ingestor_state import IngestorState -from rest_server.repositories.report.utils import get_date_range_query - - -async def get_ingestor_state( - offset: int, - limit: int, - to_log_date: Optional[str] = None, - from_log_date: Optional[str] = None, -): - query = get_date_range_query(to_log_date, from_log_date) - - return await IngestorState_Pydantic.from_queryset( - IngestorState.filter(**query).offset(offset).limit(limit).order_by("-created") - ) diff --git a/packages/reporting-server/rest_server/repositories/report/lift_state.py b/packages/reporting-server/rest_server/repositories/report/lift_state.py deleted file mode 100644 index e221816c5..000000000 --- a/packages/reporting-server/rest_server/repositories/report/lift_state.py +++ /dev/null @@ -1,25 +0,0 @@ -from typing import Optional - -from models.pydantic_models import LiftState_Pydantic -from models.tortoise_models.lift_state import LiftState -from rest_server.repositories.report.utils import get_date_range_query - - -async def get_lift_state( - offset: int, - limit: int, - to_log_date: Optional[str] = None, - from_log_date: Optional[str] = None, -): - - query = get_date_range_query(to_log_date, from_log_date) - - queryset = ( - LiftState.filter(**query) - .prefetch_related("lift") - .offset(offset) - .limit(limit) - .order_by("-created") - ) - - return await LiftState_Pydantic.from_queryset(queryset) diff --git a/packages/reporting-server/rest_server/repositories/report/raw_log.py b/packages/reporting-server/rest_server/repositories/report/raw_log.py deleted file mode 100644 index ec563c622..000000000 --- a/packages/reporting-server/rest_server/repositories/report/raw_log.py +++ /dev/null @@ -1,49 +0,0 @@ -from datetime import timezone -from typing import Optional - -from dateutil import parser -from models.pydantic_models import RawLog_Pydantic -from models.tortoise_models.container import Container -from models.tortoise_models.raw_log import RawLog - - -async def get_all_raw_logs( - offset: int, - limit: int, - to_log_date: Optional[str] = None, - from_log_date: Optional[str] = None, - log_level: Optional[str] = None, - container_label: Optional[str] = None, -): - query = {} - - if from_log_date: - local_time = parser.parse(from_log_date) - utc_time = local_time.astimezone(timezone.utc) - query["created__gte"] = utc_time - - if to_log_date: - to_log_local_time = parser.parse(to_log_date) - to_log_utc_time = to_log_local_time.astimezone(timezone.utc) - query["created__lt"] = to_log_utc_time - - if container_label and container_label != "all": - query["container__name__iexact"] = container_label - - if log_level and log_level != "all": - query["level__iexact"] = log_level - - rawlog_queryset = ( - RawLog.filter(**query) - .prefetch_related("container") - .offset(offset) - .limit(limit) - .order_by("-created") - ) - - return await RawLog_Pydantic.from_queryset(rawlog_queryset) - - -async def get_containers(): - raw_containers = await Container.all() - return [x.name for x in raw_containers] diff --git a/packages/reporting-server/rest_server/repositories/report/task_summary.py b/packages/reporting-server/rest_server/repositories/report/task_summary.py deleted file mode 100644 index 609b4c5f9..000000000 --- a/packages/reporting-server/rest_server/repositories/report/task_summary.py +++ /dev/null @@ -1,25 +0,0 @@ -from typing import Optional - -from models.pydantic_models import TaskSummary_Pydantic -from models.tortoise_models.task_summary import TaskSummary -from rest_server.repositories.report.utils import get_date_range_query - - -async def get_task_summary( - offset: int, - limit: int, - to_log_date: Optional[str] = None, - from_log_date: Optional[str] = None, -): - query = get_date_range_query(to_log_date, from_log_date) - - queryset = ( - TaskSummary.filter(**query) - .prefetch_related("fleet") - .prefetch_related("robot") - .offset(offset) - .limit(limit) - .order_by("-created") - ) - - return await TaskSummary_Pydantic.from_queryset(queryset) diff --git a/packages/reporting-server/rest_server/repositories/report/test_auth_event_report.py b/packages/reporting-server/rest_server/repositories/report/test_auth_event_report.py deleted file mode 100644 index 1045dd354..000000000 --- a/packages/reporting-server/rest_server/repositories/report/test_auth_event_report.py +++ /dev/null @@ -1,75 +0,0 @@ -# conflicts with isort because of local non-relative import -# pylint: disable=wrong-import-order -import unittest - -from fastapi.testclient import TestClient -from models.tortoise_models.auth_events import AuthEvents -from rest_server.app import get_app -from rest_server.repositories.report.auth_event_report import ( - get_auth_events, - get_user_login_failure_report, - get_user_login_report, - get_user_logout_report, -) -from rest_server.test_utils import start_test_database -from tortoise import Tortoise - -app = get_app() - - -class TestReportAuthEventServerLogRoute(unittest.IsolatedAsyncioTestCase): - async def asyncSetUp(self): - await start_test_database() - self.client = TestClient(app) - - await AuthEvents.create( - username="test_user", - user_keycloak_id="test_id", - event_type="LOGIN_ERROR", - realm_id="test", - client_id="test", - ) - - await AuthEvents.create( - username="test_user", - user_keycloak_id="test_id", - event_type="LOGIN", - realm_id="test", - client_id="test", - ) - - await AuthEvents.create( - username="test_user", - user_keycloak_id="test_id", - event_type="LOGOUT", - realm_id="test", - client_id="test", - ) - - async def asyncTearDown(self): - await Tortoise.close_connections() - - async def test_get_user_login_report(self): - login_list = await get_user_login_report(0, 10) - self.assertEqual(len(login_list), 1) - - async def test_get_user_logout_report(self): - logout_list = await get_user_logout_report(0, 10) - self.assertEqual(len(logout_list), 1) - - async def test_get_user_login_failure_report(self): - login_error_list = await get_user_login_failure_report(0, 10) - self.assertEqual(len(login_error_list), 1) - - async def test_get_auth_events(self): - login_list = await get_auth_events("LOGIN", 0, 10) - login_error_list = await get_auth_events("LOGIN_ERROR", 0, 10) - logout_list = await get_auth_events("LOGOUT", 0, 10) - - self.assertEqual(len(login_error_list), 1) - self.assertEqual(len(logout_list), 1) - self.assertEqual(len(login_list), 1) - - self.assertEqual(login_list[0].event_type, "LOGIN") - self.assertEqual(login_error_list[0].event_type, "LOGIN_ERROR") - self.assertEqual(logout_list[0].event_type, "LOGOUT") diff --git a/packages/reporting-server/rest_server/repositories/report/test_dispenser_state.py b/packages/reporting-server/rest_server/repositories/report/test_dispenser_state.py deleted file mode 100644 index 615ffd4da..000000000 --- a/packages/reporting-server/rest_server/repositories/report/test_dispenser_state.py +++ /dev/null @@ -1,35 +0,0 @@ -# conflicts with isort because of local non-relative import -# pylint: disable=wrong-import-order -import unittest - -from fastapi.testclient import TestClient -from models.tortoise_models.dispenser_state import DispenserState, DispenserStateEnum -from rest_server.app import get_app -from rest_server.repositories.report.dispenser_state import get_dispenser_state -from rest_server.test_utils import start_test_database -from tortoise import Tortoise - -app = get_app() - - -class TestReportDispenserState(unittest.IsolatedAsyncioTestCase): - async def asyncSetUp(self): - await start_test_database() - self.client = TestClient(app) - - await DispenserState.create( - guid="guid1", - state=DispenserStateEnum.IDLE, - ) - - await DispenserState.create( - guid="guid2", - state=DispenserStateEnum.IDLE, - ) - - async def asyncTearDown(self): - await Tortoise.close_connections() - - async def test_get_dispenser_states(self): - dispenser_list = await get_dispenser_state(0, 10) - self.assertEqual(len(dispenser_list), 2) diff --git a/packages/reporting-server/rest_server/repositories/report/test_door_state.py b/packages/reporting-server/rest_server/repositories/report/test_door_state.py deleted file mode 100644 index f4f81fbb3..000000000 --- a/packages/reporting-server/rest_server/repositories/report/test_door_state.py +++ /dev/null @@ -1,31 +0,0 @@ -# conflicts with isort because of local non-relative import -# pylint: disable=wrong-import-order -import unittest - -from fastapi.testclient import TestClient -from models.tortoise_models.door import Door -from models.tortoise_models.door_state import DoorState, DoorStateEnum -from rest_server.app import get_app -from rest_server.repositories.report.door_state import get_door_state -from rest_server.test_utils import start_test_database -from tortoise import Tortoise - -app = get_app() - - -class TestReportDoorState(unittest.IsolatedAsyncioTestCase): - async def asyncSetUp(self): - await start_test_database() - self.client = TestClient(app) - - door = await Door.create(name="Door 1") - - await DoorState.create(door=door, state=DoorStateEnum.CLOSED) - await DoorState.create(door=door, state=DoorStateEnum.CLOSED) - - async def asyncTearDown(self): - await Tortoise.close_connections() - - async def test_get_door_states(self): - door_list = await get_door_state(0, 10) - self.assertEqual(len(door_list), 2) diff --git a/packages/reporting-server/rest_server/repositories/report/test_fleet_state.py b/packages/reporting-server/rest_server/repositories/report/test_fleet_state.py deleted file mode 100644 index 7f31ac699..000000000 --- a/packages/reporting-server/rest_server/repositories/report/test_fleet_state.py +++ /dev/null @@ -1,48 +0,0 @@ -# conflicts with isort because of local non-relative import -# pylint: disable=wrong-import-order -import unittest - -from fastapi.testclient import TestClient -from models.tortoise_models.fleet import Fleet, Robot -from models.tortoise_models.fleet_state import FleetState, RobotStateEnum -from rest_server.app import get_app -from rest_server.repositories.report.fleet_state import get_fleet_state -from rest_server.test_utils import start_test_database -from tortoise import Tortoise - -app = get_app() - - -class TestReportFleetState(unittest.IsolatedAsyncioTestCase): - async def asyncSetUp(self): - await start_test_database() - self.client = TestClient(app) - - robot = await Robot.create(name="Robot 1") - fleet = await Fleet.create(name="Fleet 1") - - await FleetState.create( - fleet=fleet, - robot=robot, - robot_battery_percent="100", - robot_location="1", - robot_mode=RobotStateEnum.MODE_WAITING, - robot_seq=1, - robot_task_id="test", - ) - await FleetState.create( - fleet=fleet, - robot=robot, - robot_battery_percent="100", - robot_location="1", - robot_mode=RobotStateEnum.MODE_WAITING, - robot_seq=2, - robot_task_id="test", - ) - - async def asyncTearDown(self): - await Tortoise.close_connections() - - async def test_get_fleet_states(self): - fleet_list = await get_fleet_state(0, 10) - self.assertEqual(len(fleet_list), 2) diff --git a/packages/reporting-server/rest_server/repositories/report/test_health.py b/packages/reporting-server/rest_server/repositories/report/test_health.py deleted file mode 100644 index 67ebb4d40..000000000 --- a/packages/reporting-server/rest_server/repositories/report/test_health.py +++ /dev/null @@ -1,36 +0,0 @@ -# conflicts with isort because of local non-relative import -# pylint: disable=wrong-import-order -import unittest - -from fastapi.testclient import TestClient -from models.tortoise_models.health import Device, HealthStatus, HealthStatusEmun -from rest_server.app import get_app -from rest_server.repositories.report.health import get_health -from rest_server.test_utils import start_test_database -from tortoise import Tortoise - -app = get_app() - - -class TestReportHealth(unittest.IsolatedAsyncioTestCase): - async def asyncSetUp(self): - await start_test_database() - self.client = TestClient(app) - - device1 = await Device.create(type="door") - device2 = await Device.create(type="lift") - await HealthStatus.create( - device=device1, - health_status=HealthStatusEmun.HEALTHY, - ) - await HealthStatus.create( - device=device2, - health_status=HealthStatusEmun.HEALTHY, - ) - - async def asyncTearDown(self): - await Tortoise.close_connections() - - async def test_get_health_status(self): - health_list = await get_health(0, 10) - self.assertEqual(len(health_list), 2) diff --git a/packages/reporting-server/rest_server/repositories/report/test_ingestor_state.py b/packages/reporting-server/rest_server/repositories/report/test_ingestor_state.py deleted file mode 100644 index 369926be6..000000000 --- a/packages/reporting-server/rest_server/repositories/report/test_ingestor_state.py +++ /dev/null @@ -1,35 +0,0 @@ -# conflicts with isort because of local non-relative import -# pylint: disable=wrong-import-order -import unittest - -from fastapi.testclient import TestClient -from models.tortoise_models.ingestor_state import IngestorState, IngestorStateEnum -from rest_server.app import get_app -from rest_server.repositories.report.ingestor_state import get_ingestor_state -from rest_server.test_utils import start_test_database -from tortoise import Tortoise - -app = get_app() - - -class TestReportIngestorState(unittest.IsolatedAsyncioTestCase): - async def asyncSetUp(self): - await start_test_database() - self.client = TestClient(app) - - await IngestorState.create( - guid="guid1", - state=IngestorStateEnum.IDLE, - ) - - await IngestorState.create( - guid="guid2", - state=IngestorStateEnum.IDLE, - ) - - async def asyncTearDown(self): - await Tortoise.close_connections() - - async def test_get_ingestor_states(self): - ingestor_list = await get_ingestor_state(0, 10) - self.assertEqual(len(ingestor_list), 2) diff --git a/packages/reporting-server/rest_server/repositories/report/test_lift_state.py b/packages/reporting-server/rest_server/repositories/report/test_lift_state.py deleted file mode 100644 index ffeaa4b86..000000000 --- a/packages/reporting-server/rest_server/repositories/report/test_lift_state.py +++ /dev/null @@ -1,55 +0,0 @@ -# conflicts with isort because of local non-relative import -# pylint: disable=wrong-import-order - -import unittest - -from fastapi.testclient import TestClient -from models.tortoise_models.lift import Lift -from models.tortoise_models.lift_state import ( - LiftDoorStateEmun, - LiftMotionStateEnum, - LiftState, - LiftStateEnum, -) -from rest_server.app import get_app -from rest_server.repositories.report.lift_state import get_lift_state -from rest_server.test_utils import start_test_database -from tortoise import Tortoise - -app = get_app() - - -class TestReportHealth(unittest.IsolatedAsyncioTestCase): - async def asyncSetUp(self): - await start_test_database() - self.client = TestClient(app) - - lift1 = await Lift.create(name="1") - lift2 = await Lift.create(name="2") - - await LiftState.create( - lift=lift1, - door_state=LiftDoorStateEmun.DOOR_CLOSED, - state=LiftStateEnum.MODE_UNKNOWN, - destination_floor="L2", - motion_state=LiftMotionStateEnum.MOTION_STOPPED, - current_floor="L1", - session_id="123", - ) - - await LiftState.create( - lift=lift2, - door_state=LiftDoorStateEmun.DOOR_CLOSED, - state=LiftStateEnum.MODE_UNKNOWN, - destination_floor="L3", - motion_state=LiftMotionStateEnum.MOTION_STOPPED, - current_floor="L2", - session_id="123", - ) - - async def asyncTearDown(self): - await Tortoise.close_connections() - - async def test_get_lift_state(self): - lift_list = await get_lift_state(0, 10) - self.assertEqual(len(lift_list), 2) diff --git a/packages/reporting-server/rest_server/repositories/report/test_raw_log.py b/packages/reporting-server/rest_server/repositories/report/test_raw_log.py deleted file mode 100644 index 6b52a3c28..000000000 --- a/packages/reporting-server/rest_server/repositories/report/test_raw_log.py +++ /dev/null @@ -1,45 +0,0 @@ -# conflicts with isort because of local non-relative import -# pylint: disable=wrong-import-order -import unittest - -from fastapi.testclient import TestClient -from rest_server.app import get_app -from rest_server.repositories.log_creation_handler import RawLogHandler -from rest_server.test_utils import start_test_database -from tortoise import Tortoise - -from .raw_log import get_containers - -app = get_app() - - -class TestRmfServerLogRoute(unittest.IsolatedAsyncioTestCase): - async def asyncSetUp(self): - await start_test_database() - self.client = TestClient(app) - - async def asyncTearDown(self): - await Tortoise.close_connections() - - async def test_raw_log_handle_creation_of_logs_with_container_name(self): - data = [ - { - "log": 'INFO:app.BookKeeper.dispenser_state:{"time": {"sec": 1600, "nanosec": 0}, "guid": "coke_dispenser", "mode": 0, "request_guid_queue": [], "seconds_remaining": 0.0}\n', - "stream": "stdout", - "kubernetes": {"container_name": "container1"}, - }, - { - "log": 'INFO:app.BookKeeper.dispenser_state:{"time": {"sec": 1600, "nanosec": 0}, "guid": "coke_dispenser", "mode": 0, "request_guid_queue": [], "seconds_remaining": 0.0}\n', - "stream": "stdout", - "kubernetes": {"container_name": "container2"}, - }, - { - "log": 'INFO:app.BookKeeper.dispenser_state:{"time": {"sec": 1600, "nanosec": 0}, "guid": "coke_dispenser", "mode": 0, "request_guid_queue": [], "seconds_remaining": 0.0}\n', - "stream": "stdout", - "kubernetes": {"container_name": "container2"}, - }, - ] - - await RawLogHandler.create_raw_log(data) - containers = await get_containers() - self.assertEqual(len(containers), 2) diff --git a/packages/reporting-server/rest_server/repositories/report/test_utils.py b/packages/reporting-server/rest_server/repositories/report/test_utils.py deleted file mode 100644 index 708c739d7..000000000 --- a/packages/reporting-server/rest_server/repositories/report/test_utils.py +++ /dev/null @@ -1,39 +0,0 @@ -from datetime import timezone -from unittest import TestCase - -from dateutil import parser -from rest_server.repositories.report.utils import get_date_range_query - - -class TestCaseUtils(TestCase): - def setUp(self): - self.raw_from_log_date = "2018-01-01" - self.raw_to_log_date = "2018-02-01" - - self.from_log_date = parser.parse(self.raw_from_log_date).astimezone( - timezone.utc - ) - self.to_log_date = parser.parse(self.raw_to_log_date).astimezone(timezone.utc) - - def test_get_date_range_query_with_no_date_range(self): - self.assertEqual(get_date_range_query(), {}) - - def test_get_date_range_query(self): - self.assertEqual( - get_date_range_query( - to_log_date=self.raw_to_log_date, from_log_date=self.raw_from_log_date - ), - {"created__gte": self.from_log_date, "created__lt": self.to_log_date}, - ) - - def test_get_date_range_with_to_log_date(self): - self.assertEqual( - get_date_range_query(to_log_date=self.raw_to_log_date), - {"created__lt": self.to_log_date}, - ) - - def test_get_date_range_with_from_log_date(self): - self.assertEqual( - get_date_range_query(from_log_date=self.raw_from_log_date), - {"created__gte": self.from_log_date}, - ) diff --git a/packages/reporting-server/rest_server/repositories/report/utils.py b/packages/reporting-server/rest_server/repositories/report/utils.py deleted file mode 100644 index a40a67a40..000000000 --- a/packages/reporting-server/rest_server/repositories/report/utils.py +++ /dev/null @@ -1,23 +0,0 @@ -from datetime import timezone -from typing import Optional - -from dateutil import parser - - -def get_date_range_query( - to_log_date: Optional[str] = None, from_log_date: Optional[str] = None -): - - query = {} - - if from_log_date: - local_time = parser.parse(from_log_date) - utc_time = local_time.astimezone(timezone.utc) - query["created__gte"] = utc_time - - if to_log_date: - to_log_local_time = parser.parse(to_log_date) - to_log_utc_time = to_log_local_time.astimezone(timezone.utc) - query["created__lt"] = to_log_utc_time - - return query diff --git a/packages/reporting-server/rest_server/repositories/rmf_log_creation_handler.py b/packages/reporting-server/rest_server/repositories/rmf_log_creation_handler.py deleted file mode 100644 index 574b46095..000000000 --- a/packages/reporting-server/rest_server/repositories/rmf_log_creation_handler.py +++ /dev/null @@ -1,28 +0,0 @@ -import logging - -from .parser_dispatcher import log_model_dispatcher - -# Function that receives all the logs and store them on the database - -logger = logging.getLogger("rest_app:log_creation_handler") - - -# We want to grab specific data from this list of strings, so we need to preprocess -# this information -async def create_rmf_server_log(logs: list): - if len(logs) == 0: - return "No data received" - error_logs = [] - - for log in logs: - try: - # If it not data app, we will skip it because the create_raw_log in theory will register that log - if "INFO:app.BookKeeper." not in log["log"]: - continue - modified_log = log["log"].replace("INFO:app.BookKeeper.", "") - await log_model_dispatcher(modified_log) - - except (SyntaxError, ValueError, KeyError) as e: - error_logs.append("Error:" + str(e) + "Log:" + str(log)) - - return error_logs if len(error_logs) > 0 else "Logs were saved correctly" diff --git a/packages/reporting-server/rest_server/repositories/test_log_creation_handler.py b/packages/reporting-server/rest_server/repositories/test_log_creation_handler.py deleted file mode 100644 index 668586a75..000000000 --- a/packages/reporting-server/rest_server/repositories/test_log_creation_handler.py +++ /dev/null @@ -1,75 +0,0 @@ -# conflicts with isort because of local non-relative import -# pylint: disable=wrong-import-order -import unittest - -from fastapi.testclient import TestClient -from models.tortoise_models import RawLog -from models.tortoise_models.auth_events import AuthEvents -from rest_server.__mocks__ import raw_data -from rest_server.app import get_app -from rest_server.test_utils import start_test_database -from tortoise import Tortoise - -from .log_creation_handler import RawLogHandler, create_keycloak_log - -app = get_app() - - -class TestCaseRawLogCreationRepository(unittest.IsolatedAsyncioTestCase): - async def asyncSetUp(self): - await start_test_database() - self.client = TestClient(app) - self.RawLogHandler = RawLogHandler() - - async def asyncTearDown(self): - await Tortoise.close_connections() - - async def test_no_data_sent_to_rawlog(self): - response = await RawLogHandler.create_raw_log([]) - self.assertEqual(response, "No valid data") - - async def test_create_a_raw_log_correctly(self): - data = [ - raw_data.mock_dispenser_state, - raw_data.mock_door_state, - "this is a test", - ] - response = await RawLogHandler.create_raw_log(data) - self.assertEqual(response, "Logs were saved correctly") - - async def test_raw_log_handle_creation_of_logs(self): - data = [ - raw_data.mock_dispenser_state, - raw_data.mock_door_state, - "this is a test", - ] - await RawLogHandler.create_raw_log(data) - dispenser = await RawLog.all() - self.assertEqual(len(dispenser), 3) - - async def test_raw_log_handle_creation_of_logs_with_container_name(self): - await RawLogHandler.create_raw_log([raw_data.mock_dispenser_state]) - log = await RawLog.first().prefetch_related("container") - self.assertEqual(log.container.name, "app-that-writes-logs") - - -class TestCaseKeycloakCreationRepository(unittest.IsolatedAsyncioTestCase): - async def asyncSetUp(self): - await start_test_database() - self.client = TestClient(app) - self.RawLogHandler = RawLogHandler() - - async def asyncTearDown(self): - await Tortoise.close_connections() - - async def test_keycloak_log_creation(self): - await create_keycloak_log( - [raw_data.mock_keycloak_login_error, raw_data.mock_keycloak_login] - ) - logs = await AuthEvents.all() - self.assertEqual(len(logs), 2) - - async def test_keycloak_logout_creation(self): - await create_keycloak_log([raw_data.mock_keycloak_logout]) - logs = await AuthEvents.all() - self.assertEqual(len(logs), 1) diff --git a/packages/reporting-server/rest_server/repositories/test_parser_dispatcher.py b/packages/reporting-server/rest_server/repositories/test_parser_dispatcher.py deleted file mode 100644 index 10d135834..000000000 --- a/packages/reporting-server/rest_server/repositories/test_parser_dispatcher.py +++ /dev/null @@ -1,120 +0,0 @@ -import unittest - -# Husky is sorting tortoise in a way that tortoise goes after our custom packages. -# and because of that the lint is failing -import tortoise -from fastapi.testclient import TestClient -from models.tortoise_models import ( - Device, - DispenserState, - Door, - DoorState, - FleetState, - HealthStatus, - IngestorState, - Lift, - LiftState, - TaskSummary, -) -from rest_server.__mocks__ import parsed_data -from rest_server.app import get_app -from rest_server.test_utils import start_test_database - -from .parser_dispatcher import log_model_dispatcher - -app = get_app() - - -class TestCaseLogParserDispatcher(unittest.IsolatedAsyncioTestCase): - async def asyncSetUp(self): - await start_test_database() - self.client = TestClient(app) - - async def asyncTearDown(self): - await tortoise.Tortoise.close_connections() - - async def test_dispenser_state_created(self): - await log_model_dispatcher(parsed_data.mock_dispenser_state) - instance = await DispenserState.first() - self.assertEqual(instance.guid, "coke_dispenser") - - async def test_door_state_created(self): - await log_model_dispatcher(parsed_data.mock_door_state) - door = await Door.first() - status = await DoorState.first() - self.assertEqual(door.name, "hardware_door") - self.assertIsNotNone(status) - - async def test_fleet_state_created(self): - await log_model_dispatcher(parsed_data.mock_fleet_state) - instance = await FleetState.all().prefetch_related("robot", "fleet") - self.assertEqual(len(instance), 2) - - async def test_fleet_state_fk_created(self): - await log_model_dispatcher(parsed_data.mock_fleet_state) - instance = await FleetState.all().prefetch_related("robot", "fleet") - self.assertEqual(len(instance), 2) - - self.assertEqual(instance[0].fleet.name, "tinyRobot") - self.assertEqual(instance[0].robot.name, "tinyRobot1") - - self.assertEqual(instance[1].fleet.name, "tinyRobot") - self.assertEqual(instance[1].robot.name, "tinyRobot2") - - async def test_task_summary_created(self): - await log_model_dispatcher(parsed_data.mock_task_summary) - instance = await TaskSummary.first() - self.assertEqual(instance.task_id, "Loop0") - self.assertEqual(instance.status, None) - - async def test_ingestor_state_created(self): - await log_model_dispatcher(parsed_data.mock_ingestor_state) - instance = await IngestorState.first() - self.assertEqual(instance.guid, "coke_ingestor") - - async def test_lift_state_created(self): - await log_model_dispatcher(parsed_data.mock_lift_state) - lift = await Lift.first() - self.assertIsNotNone(lift.name, "test_lift") - instance = await LiftState.first() - self.assertIsNotNone(instance) - self.assertEqual(instance.current_floor, "L1") - - -class TestCaseHealthParserDispatcher(unittest.IsolatedAsyncioTestCase): - async def asyncSetUp(self): - await start_test_database() - self.client = TestClient(app) - - async def asyncTearDown(self): - await tortoise.Tortoise.close_connections() - - async def test_door_health_created(self): - data = 'door_health:{"id": "hardware_door", "health_status": "HealthStatus.HEALTHY", "health_message": null}\n' - await log_model_dispatcher(data) - instance = await Device.first() - self.assertEqual(instance.actor, "hardware_door") - self.assertEqual(instance.type, "door_health") - - health_instance = await HealthStatus.first() - self.assertIsNotNone(health_instance) - - async def test_robot_health_created(self): - data = 'robot_health:{"id": "robot1", "health_status": "HealthStatus.HEALTHY", "health_message": null}\n' - await log_model_dispatcher(data) - instance = await Device.first() - self.assertEqual(instance.actor, "robot1") - self.assertEqual(instance.type, "robot_health") - - health_instance = await HealthStatus.first() - self.assertIsNotNone(health_instance) - - async def test_lift_health_created(self): - data = 'lift_health:{"id": "lift1", "health_status": "HealthStatus.HEALTHY", "health_message": null}\n' - await log_model_dispatcher(data) - instance = await Device.first() - self.assertEqual(instance.actor, "lift1") - self.assertEqual(instance.type, "lift_health") - - health_instance = await HealthStatus.first() - self.assertIsNotNone(health_instance) diff --git a/packages/reporting-server/rest_server/repositories/test_rmf_log_creation_handler.py b/packages/reporting-server/rest_server/repositories/test_rmf_log_creation_handler.py deleted file mode 100644 index 2a519daef..000000000 --- a/packages/reporting-server/rest_server/repositories/test_rmf_log_creation_handler.py +++ /dev/null @@ -1,54 +0,0 @@ -# conflicts with isort because of local non-relative import -# pylint: disable=wrong-import-order -import unittest - -from fastapi.testclient import TestClient -from models.tortoise_models import DispenserState, Door, DoorState -from rest_server.__mocks__ import raw_data -from rest_server.app import get_app -from rest_server.repositories.rmf_log_creation_handler import create_rmf_server_log -from rest_server.test_utils import start_test_database -from tortoise import Tortoise - -app = get_app() - - -class TestCaseLogRMFServerCreationRepository(unittest.IsolatedAsyncioTestCase): - async def asyncSetUp(self): - await start_test_database() - self.client = TestClient(app) - - async def asyncTearDown(self): - await Tortoise.close_connections() - - async def test_no_data_sent_to_rmfserver(self): - response = await create_rmf_server_log([]) - self.assertEqual(response, "No data received") - - async def test_create_a_rmfserver_log_correctly(self): - data = [raw_data.mock_dispenser_state, raw_data.mock_door_state] - response = await create_rmf_server_log(data) - self.assertEqual(response, "Logs were saved correctly") - - async def test_rmfserver_handle_and_return_error(self): - data = [ - { - "log2": 'INFO:app.BookKeeper.dispenser_state:{"time": {"sec": 1600, "nanosec": 0}, "guid": "coke_dispenser", "mode": 0, "request_guid_queue": [], "seconds_remaining": 0.0}\n', - "stream": "stdout", - }, - raw_data.mock_door_state, - ] - - response = await create_rmf_server_log(data) - self.assertEqual(len(response), 1) - - async def test_rmfserver_handle_creation_of_logs(self): - await create_rmf_server_log( - [raw_data.mock_dispenser_state, raw_data.mock_door_state] - ) - dispenser_state = await DispenserState.first() - door_state = await DoorState.first() - door = await Door.first() - self.assertEqual(dispenser_state.guid, "coke_dispenser") - self.assertEqual(door.name, "hardware_door") - self.assertIsNotNone(door_state) diff --git a/packages/reporting-server/rest_server/routers/__init__.py b/packages/reporting-server/rest_server/routers/__init__.py deleted file mode 100644 index abe2d9853..000000000 --- a/packages/reporting-server/rest_server/routers/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -from .log import router as log_router -from .report import router as report_router diff --git a/packages/reporting-server/rest_server/routers/log.py b/packages/reporting-server/rest_server/routers/log.py deleted file mode 100644 index 80a8b29b0..000000000 --- a/packages/reporting-server/rest_server/routers/log.py +++ /dev/null @@ -1,50 +0,0 @@ -from fastapi import APIRouter, HTTPException, status -from rest_server.repositories.log_creation_handler import ( - RawLogHandler, - create_keycloak_log, -) -from rest_server.repositories.rmf_log_creation_handler import create_rmf_server_log - -router = APIRouter() - - -# This will receive information from different sources -@router.post("/all/", tags=["all_logs"], status_code=status.HTTP_201_CREATED) -async def write_logs(body: list): - try: - return await RawLogHandler.create_raw_log(body) - except Exception as e: - print(e) - raise HTTPException(503, "cannot create the log" + str(e)) from e - - -# Will receive information from rmf-server only -@router.post( - "/rmfserver/", tags=["rmfserver_logs"], status_code=status.HTTP_201_CREATED -) -async def write_rmf_server_logs(body: list): - try: - response = await create_rmf_server_log(body) - if not isinstance(response, str): - raise HTTPException(503, "Error creating some logs" + str(response)) - - return response - - except Exception as e: - print(e) - raise HTTPException(503, "cannot create the rmfserver log" + str(e)) from e - - -# Will receive information from keycloak only -@router.post("/keycloak/", tags=["keycloak_logs"], status_code=status.HTTP_201_CREATED) -async def write_keycloak_logs(body: list): - try: - response = await create_keycloak_log(body) - if not isinstance(response, str): - raise HTTPException(503, "Error creating some logs" + str(response)) - - return response - - except Exception as e: - print(e) - raise HTTPException(503, "cannot create the keycloak log" + str(e)) from e diff --git a/packages/reporting-server/rest_server/routers/report.py b/packages/reporting-server/rest_server/routers/report.py deleted file mode 100644 index 544efd7c7..000000000 --- a/packages/reporting-server/rest_server/routers/report.py +++ /dev/null @@ -1,187 +0,0 @@ -from typing import Any, List, Optional - -from fastapi import APIRouter -from models.pydantic_models import ( - AuthEvents_Pydantic, - DispenserState_Pydantic, - DoorState_Pydantic, - FleetState_Pydantic, - HealthStatus_Pydantic, - IngestorState_Pydantic, - LiftState_Pydantic, - TaskSummary_Pydantic, -) -from rest_server.repositories.report import ( - get_all_raw_logs, - get_containers, - get_dispenser_state, - get_door_state, - get_fleet_state, - get_health, - get_ingestor_state, - get_lift_state, - get_task_summary, -) -from rest_server.repositories.report.auth_event_report import ( - get_user_login_failure_report, - get_user_login_report, - get_user_logout_report, -) - -router = APIRouter() - -LIMIT = 500 - - -@router.get("/raw_logs/", tags=["raw_logs"], response_model=List[Any]) -async def raw_logs_report( - toLogDate: Optional[str] = None, - fromLogDate: Optional[str] = None, - logLevel: Optional[str] = None, - containerLabel: Optional[str] = None, - offset: Optional[int] = 0, - limit: Optional[int] = LIMIT, -): - - return await get_all_raw_logs( - offset, limit, toLogDate, fromLogDate, logLevel, containerLabel - ) - - -@router.get("/raw_logs/containers", tags=["raw_logs_get_containers"]) -async def raw_logs_get_containers(): - return await get_containers() - - -@router.get( - "/door_state/", tags=["door_state"], response_model=List[DoorState_Pydantic] -) -async def door_state_report( - toLogDate: Optional[str] = None, - fromLogDate: Optional[str] = None, - offset: Optional[int] = 0, - limit: Optional[int] = LIMIT, -): - - return await get_door_state(offset, limit, toLogDate, fromLogDate) - - -@router.get( - "/fleet_state/", tags=["fleet_state"], response_model=List[FleetState_Pydantic] -) -async def fleet_state_report( - toLogDate: Optional[str] = None, - fromLogDate: Optional[str] = None, - offset: Optional[int] = 0, - limit: Optional[int] = LIMIT, -): - - return await get_fleet_state(offset, limit, toLogDate, fromLogDate) - - -@router.get( - "/dispenser_state/", - tags=["dispenser_state"], - response_model=List[DispenserState_Pydantic], -) -async def dispenser_state_report( - toLogDate: Optional[str] = None, - fromLogDate: Optional[str] = None, - offset: Optional[int] = 0, - limit: Optional[int] = LIMIT, -): - - return await get_dispenser_state(offset, limit, toLogDate, fromLogDate) - - -@router.get( - "/ingestor_state/", - tags=["ingestor_state"], - response_model=List[IngestorState_Pydantic], -) -async def ingestor_state_report( - toLogDate: Optional[str] = None, - fromLogDate: Optional[str] = None, - offset: Optional[int] = 0, - limit: Optional[int] = LIMIT, -): - - return await get_ingestor_state(offset, limit, toLogDate, fromLogDate) - - -@router.get( - "/lift_state/", tags=["lift_state"], response_model=List[LiftState_Pydantic] -) -async def lift_state_report( - toLogDate: Optional[str] = None, - fromLogDate: Optional[str] = None, - offset: Optional[int] = 0, - limit: Optional[int] = LIMIT, -): - - return await get_lift_state(offset, limit, toLogDate, fromLogDate) - - -@router.get("/health/", tags=["health"], response_model=List[HealthStatus_Pydantic]) -async def health_report( - toLogDate: Optional[str] = None, - fromLogDate: Optional[str] = None, - offset: Optional[int] = 0, - limit: Optional[int] = LIMIT, -): - - return await get_health(offset, limit, toLogDate, fromLogDate) - - -@router.get( - "/task_summary/", tags=["task_summary"], response_model=List[TaskSummary_Pydantic] -) -async def task_report( - toLogDate: Optional[str] = None, - fromLogDate: Optional[str] = None, - offset: Optional[int] = 0, - limit: Optional[int] = LIMIT, -): - - return await get_task_summary(offset, limit, toLogDate, fromLogDate) - - -@router.get( - "/user/login/", tags=["user_login"], response_model=List[AuthEvents_Pydantic] -) -async def user_login_report( - toLogDate: Optional[str] = None, - fromLogDate: Optional[str] = None, - offset: Optional[int] = 0, - limit: Optional[int] = LIMIT, -): - - return await get_user_login_report(offset, limit, toLogDate, fromLogDate) - - -@router.get( - "/user/logout/", tags=["user_logout"], response_model=List[AuthEvents_Pydantic] -) -async def user_logout_report( - toLogDate: Optional[str] = None, - fromLogDate: Optional[str] = None, - offset: Optional[int] = 0, - limit: Optional[int] = LIMIT, -): - - return await get_user_logout_report(offset, limit, toLogDate, fromLogDate) - - -@router.get( - "/user/loginfailure/", - tags=["user_login_failure"], - response_model=List[AuthEvents_Pydantic], -) -async def user_login_failure_report( - toLogDate: Optional[str] = None, - fromLogDate: Optional[str] = None, - offset: Optional[int] = 0, - limit: Optional[int] = LIMIT, -): - - return await get_user_login_failure_report(offset, limit, toLogDate, fromLogDate) diff --git a/packages/reporting-server/rest_server/routers/test_log.py b/packages/reporting-server/rest_server/routers/test_log.py deleted file mode 100644 index dca380efd..000000000 --- a/packages/reporting-server/rest_server/routers/test_log.py +++ /dev/null @@ -1,109 +0,0 @@ -# conflicts with isort because of local non-relative import -# pylint: disable=wrong-import-order - -import unittest - -from fastapi.testclient import TestClient -from rest_server.__mocks__.raw_data import mock_keycloak_login_error -from rest_server.test_utils import start_test_database -from tortoise import Tortoise - -from ..app import get_app - -app = get_app() - - -class TestRawLogRoute(unittest.IsolatedAsyncioTestCase): - async def asyncSetUp(self): - await start_test_database() - self.client = TestClient(app) - - async def asyncTearDown(self): - await Tortoise.close_connections() - - def test_log_raw_creation(self): - response = self.client.post( - "/log/all/", - json=[{"log": "test"}], - ) - assert response.status_code == 201 - - def test_error_on_bad_body_without_list(self): - self.client = TestClient(app) - response = self.client.post( - "/log/all/", - json={"log343": "test"}, - ) - assert response.status_code == 422 - - -class TestRmfServerLogRoute(unittest.IsolatedAsyncioTestCase): - async def asyncSetUp(self): - await start_test_database() - self.client = TestClient(app) - - async def asyncTearDown(self): - await Tortoise.close_connections() - - def test_log_rmfserver_creation(self): - response = self.client.post( - "/log/rmfserver/", - json=[{"log": "test"}], - ) - assert response.status_code == 201 - - def test_error_on_bad_body(self): - self.client = TestClient(app) - response = self.client.post( - "/log/rmfserver/", - json=[{"log343": "test"}], - ) - assert response.status_code == 503 - - def test_handle_error(self): - self.client = TestClient(app) - response = self.client.post( - "/log/rmfserver/", - json=[ - {"log343": "test"}, - { - "log": 'INFO:app.BookKeeper.dispenser_state:{"time": {"sec": 1600, "nanosec": 0}, "guid": "coke_dispenser", "mode": 0, "request_guid_queue": [], "seconds_remaining": 0.0}\n', - "stream": "stdout", - }, - ], - ) - - assert response.status_code == 503 - - -class TestKeycloakRoute(unittest.IsolatedAsyncioTestCase): - async def asyncSetUp(self): - await start_test_database() - self.client = TestClient(app) - - async def asyncTearDown(self): - await Tortoise.close_connections() - - def test_log_rmfserver_creation(self): - response = self.client.post( - "/log/keycloak/", - json=[{"log": "test"}], - ) - assert response.status_code == 201 - - def test_error_on_bad_body(self): - self.client = TestClient(app) - response = self.client.post( - "/log/keycloak/", - json=[{"log343": "test"}], - ) - assert response.status_code == 503 - - def test_handle_error(self): - self.client = TestClient(app) - response = self.client.post( - "/log/keycloak/", - json=[{"log343": "test"}, mock_keycloak_login_error], - ) - - assert response.status_code == 503 diff --git a/packages/reporting-server/rest_server/routers/test_report.py b/packages/reporting-server/rest_server/routers/test_report.py deleted file mode 100644 index fdc4d5e5f..000000000 --- a/packages/reporting-server/rest_server/routers/test_report.py +++ /dev/null @@ -1,28 +0,0 @@ -# conflicts with isort because of local non-relative import -# pylint: disable=wrong-import-order - -import unittest - -from models.tortoise_models.container import Container -from rest_server.test_utils import start_test_database -from starlette.testclient import TestClient -from tortoise import Tortoise - -from ..app import get_app - -app = get_app() - - -class TestContainerRoute(unittest.IsolatedAsyncioTestCase): - async def asyncSetUp(self): - await start_test_database() - self.client = TestClient(app) - await Container.create(name="test") - - async def asyncTearDown(self): - await Tortoise.close_connections() - - def test_get_containers(self): - response = self.client.get("report/raw_logs/containers/") - self.assertEqual(response.status_code, 200) - self.assertEqual(response.json(), ["test"]) diff --git a/packages/reporting-server/rest_server/test_utils.py b/packages/reporting-server/rest_server/test_utils.py deleted file mode 100644 index 1180d9c58..000000000 --- a/packages/reporting-server/rest_server/test_utils.py +++ /dev/null @@ -1,9 +0,0 @@ -from tortoise import Tortoise - - -async def start_test_database(): - await Tortoise.init( - db_url="sqlite://:memory:", - modules={"models": ["models.tortoise_models"]}, - ) - await Tortoise.generate_schemas() diff --git a/packages/reporting-server/setup.py b/packages/reporting-server/setup.py deleted file mode 100644 index 30ea28796..000000000 --- a/packages/reporting-server/setup.py +++ /dev/null @@ -1,39 +0,0 @@ -from setuptools import find_packages, setup - -package_name = "reporting_server" - -setup( - name=package_name, - description="RMF reporting server", - version="0.0.0", - packages=find_packages(exclude=["tests"]), - author="Matias Bavera", - author_email="matiasbavera@gmail.com", - keywords=["RMF", "reporting"], - classifiers=[ - "Intended Audience :: Developers", - "License :: OSI Approved :: Apache Software License", - "Programming Language :: Python", - "Topic :: Software Development", - ], - install_requires=[ - "fastapi~=0.65.2", - "uvicorn[standard]~=0.13.4", - "tortoise-orm~=0.17.4", - "pyjwt[crypto]~=2.0", - "pydantic~=1.8", - "py-dateutil~=2.2", - ], - extras_require={ - "postgres": ["asyncpg~=0.22.0"], - "mysql": ["aiomysql~=0.0.21"], - "maria": ["aiomysql~=0.0.21"], - }, - entry_points={ - "console_scripts": [ - "reporting_server=rest_server.__main__:main", - "reporting_server_clean_logs=rest_server.clean_logs:main", - ], - }, - license="Apache License, Version 2.0", -) diff --git a/packages/reporting/.env b/packages/reporting/.env deleted file mode 100644 index a8f819259..000000000 --- a/packages/reporting/.env +++ /dev/null @@ -1,5 +0,0 @@ -# because we are a monorepo, some create-react-app dependencies may be installed in the root directory, -# it complains if it find such packages so we need to disable the check. -SKIP_PREFLIGHT_CHECK=true - -REACT_APP_REPORTING_SERVER=http://localhost:8000 diff --git a/packages/reporting/.gitignore b/packages/reporting/.gitignore deleted file mode 100644 index a13cc8c46..000000000 --- a/packages/reporting/.gitignore +++ /dev/null @@ -1,32 +0,0 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies -/public/assets/icons/ -/node_modules -/.pnp -.pnp.js -.resources.json - -# testing -/coverage - -# production -/build -/storybook-static -# misc -.DS_Store -.env.local -.env.development.local -.env.test.local -.env.production.local -.eslintcache - -npm-debug.log* -web_server.log -yarn-debug.log* -yarn-error.log* - -# test artifacts -/e2e/artifacts - -/.rmf diff --git a/packages/reporting/.storybook/main.js b/packages/reporting/.storybook/main.js deleted file mode 100644 index 195fee260..000000000 --- a/packages/reporting/.storybook/main.js +++ /dev/null @@ -1,8 +0,0 @@ -module.exports = { - stories: ['../src/**/*.stories.tsx'], - addons: [ - '@storybook/preset-create-react-app', - '@storybook/addon-actions', - '@storybook/addon-links', - ], -}; diff --git a/packages/reporting/package.json b/packages/reporting/package.json deleted file mode 100644 index 5d0c22ced..000000000 --- a/packages/reporting/package.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "name": "reporting", - "version": "0.0.1", - "description": "Interface in charge of displaying reports to end users", - "main": "index.js", - "private": true, - "scripts": { - "start": "react-scripts start", - "build": "../../scripts/nws.sh build -d && react-scripts build", - "test": "../../scripts/nws.sh build -d && react-scripts test", - "test:coverage": "npm run test -- --coverage --watchAll=false", - "test:e2e": "cd e2e && npm test", - "test:e2e:dev": "cd e2e && npm run test:dev", - "eject": "react-scripts eject", - "storybook": "start-storybook -p 9009 -s public -s src/stories/static", - "build:storybook": "../../scripts/nws.sh build -d && build-storybook -s public -s src/stories" - }, - "keywords": [ - "reporting" - ], - "author": "matiasbavera@gmail.com", - "license": "Apache-2.0", - "eslintConfig": { - "extends": "react-app" - }, - "browserslist": { - "production": [ - ">0.2%", - "not dead", - "not op_mini all" - ], - "development": [ - "last 1 chrome version", - "last 1 firefox version", - "last 1 safari version" - ] - }, - "devDependencies": { - "@fontsource/roboto": "^4.3.0", - "@storybook/addon-actions": "^6.3.7", - "@storybook/addon-essentials": "^6.3.7", - "@storybook/addon-links": "^6.3.7", - "@storybook/react": "^6.3.7", - "@testing-library/jest-dom": "^5.11.4", - "@testing-library/react": "^11.2.3", - "@testing-library/react-hooks": "^5.0.3", - "@testing-library/user-event": "^12.1.9", - "@types/debug": "^4.1.5", - "@types/jest": "^26.0.13", - "@types/react": "^17.0.19", - "@types/react-dom": "^17.0.9", - "@types/react-router": "^5.1.7", - "@types/react-router-dom": "^5.1.7", - "axios": "^0.21.1", - "clsx": "^1.1.1", - "jest-canvas-mock": "^2.3.1", - "react": "^17.0.2", - "react-components": "file:../react-components", - "react-router": "^5.2.0", - "react-router-dom": "^5.2.0", - "react-scripts": "^4.0.3", - "reporting-server": "file:../reporting-server", - "rmf-auth": "file:../rmf-auth", - "ts-node": "^9.1.1", - "typescript": "~4.4.4" - }, - "jest": { - "collectCoverageFrom": [ - "src/**/*.{js,jsx,ts,tsx}", - "!src/index.tsx", - "!src/app-config.ts", - "!src/serviceWorker.ts", - "!src/react-app-env.d.ts", - "!src/components/reporter-side-bar-structure.tsx", - "!**/stories/**", - "!**/tests/**" - ] - } -} diff --git a/packages/reporting/public/assets/aron-visuals-3jBU9TbKW7o-unsplash.jpg b/packages/reporting/public/assets/aron-visuals-3jBU9TbKW7o-unsplash.jpg deleted file mode 100644 index 8ef59a28d..000000000 Binary files a/packages/reporting/public/assets/aron-visuals-3jBU9TbKW7o-unsplash.jpg and /dev/null differ diff --git a/packages/reporting/public/assets/ros-health.png b/packages/reporting/public/assets/ros-health.png deleted file mode 100644 index f531098b6..000000000 Binary files a/packages/reporting/public/assets/ros-health.png and /dev/null differ diff --git a/packages/reporting/public/fast_forward-24px.svg b/packages/reporting/public/fast_forward-24px.svg deleted file mode 100644 index d97f12fd1..000000000 --- a/packages/reporting/public/fast_forward-24px.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/reporting/public/fast_rewind-24px.svg b/packages/reporting/public/fast_rewind-24px.svg deleted file mode 100644 index c8279d579..000000000 --- a/packages/reporting/public/fast_rewind-24px.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/reporting/public/favicon.ico b/packages/reporting/public/favicon.ico deleted file mode 100644 index 6c149bf7f..000000000 Binary files a/packages/reporting/public/favicon.ico and /dev/null differ diff --git a/packages/reporting/public/index.html b/packages/reporting/public/index.html deleted file mode 100644 index 7007d690d..000000000 --- a/packages/reporting/public/index.html +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - - - - - - - - RoMi-H - - - -
- - - diff --git a/packages/reporting/public/logo192.png b/packages/reporting/public/logo192.png deleted file mode 100644 index fa313abf5..000000000 Binary files a/packages/reporting/public/logo192.png and /dev/null differ diff --git a/packages/reporting/public/logo512.png b/packages/reporting/public/logo512.png deleted file mode 100644 index bd5d4b5e2..000000000 Binary files a/packages/reporting/public/logo512.png and /dev/null differ diff --git a/packages/reporting/public/manifest.json b/packages/reporting/public/manifest.json deleted file mode 100644 index 080d6c77a..000000000 --- a/packages/reporting/public/manifest.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "short_name": "React App", - "name": "Create React App Sample", - "icons": [ - { - "src": "favicon.ico", - "sizes": "64x64 32x32 24x24 16x16", - "type": "image/x-icon" - }, - { - "src": "logo192.png", - "type": "image/png", - "sizes": "192x192" - }, - { - "src": "logo512.png", - "type": "image/png", - "sizes": "512x512" - } - ], - "start_url": ".", - "display": "standalone", - "theme_color": "#000000", - "background_color": "#ffffff" -} diff --git a/packages/reporting/public/pause-24px.svg b/packages/reporting/public/pause-24px.svg deleted file mode 100644 index 5bf80490a..000000000 --- a/packages/reporting/public/pause-24px.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/reporting/public/play_arrow-24px.svg b/packages/reporting/public/play_arrow-24px.svg deleted file mode 100644 index b882cee81..000000000 --- a/packages/reporting/public/play_arrow-24px.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/reporting/public/robots.txt b/packages/reporting/public/robots.txt deleted file mode 100644 index 01b0f9a10..000000000 --- a/packages/reporting/public/robots.txt +++ /dev/null @@ -1,2 +0,0 @@ -# https://www.robotstxt.org/robotstxt.html -User-agent: * diff --git a/packages/reporting/public/silent-check-sso.html b/packages/reporting/public/silent-check-sso.html deleted file mode 100644 index 20ad2098d..000000000 --- a/packages/reporting/public/silent-check-sso.html +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/packages/reporting/src/app-config.ts b/packages/reporting/src/app-config.ts deleted file mode 100644 index 2cabf9ee4..000000000 --- a/packages/reporting/src/app-config.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { Authenticator, KeycloakAuthenticator, StubAuthenticator } from 'rmf-auth'; -export interface AppConfig { - authenticator: Authenticator; - reportingServerUrl: string; -} - -export const appConfig: AppConfig = (() => { - const authenticator = (() => { - if (!process.env.REACT_APP_AUTH_PROVIDER) { - return new StubAuthenticator(); - } - // it is important that we do not do any processing on REACT_APP_AUTH_PROVIDER so that webpack - // can remove dead code, we DO NOT want the output to have the stub authenticator even if - // it is not used. - const provider = process.env.REACT_APP_AUTH_PROVIDER; - switch (provider) { - case 'keycloak': - if (!process.env.REACT_APP_KEYCLOAK_CONFIG) { - throw new Error('missing REACT_APP_KEYCLOAK_CONFIG'); - } - return new KeycloakAuthenticator(JSON.parse(process.env.REACT_APP_KEYCLOAK_CONFIG)); - case 'stub': - return new StubAuthenticator(); - default: - throw new Error(`unknown auth provider "${provider}"`); - } - })(); - - if (!process.env.REACT_APP_REPORTING_SERVER) { - throw new Error('REACT_APP_REPORTING_SERVER is required'); - } - - return { - authenticator, - reportingServerUrl: process.env.REACT_APP_REPORTING_SERVER, - }; -})(); - -export default appConfig; diff --git a/packages/reporting/src/components/app-contexts.tsx b/packages/reporting/src/components/app-contexts.tsx deleted file mode 100644 index d395fb66d..000000000 --- a/packages/reporting/src/components/app-contexts.tsx +++ /dev/null @@ -1,4 +0,0 @@ -import React from 'react'; -import appConfig, { AppConfig } from '../app-config'; - -export const AppConfigContext = React.createContext(appConfig); diff --git a/packages/reporting/src/components/app.tsx b/packages/reporting/src/components/app.tsx deleted file mode 100644 index 4dcdb833a..000000000 --- a/packages/reporting/src/components/app.tsx +++ /dev/null @@ -1,84 +0,0 @@ -import '@fontsource/roboto/300.css'; -import '@fontsource/roboto/400.css'; -import '@fontsource/roboto/500.css'; -import '@fontsource/roboto/700.css'; -import { ThemeProvider } from '@mui/material'; -import React from 'react'; -import { rmfLight } from 'react-components'; -import { BrowserRouter, Link, Redirect, Route, Switch } from 'react-router-dom'; -import { LoginPage, PrivateRoute } from 'rmf-auth'; -import appConfig from '../app-config'; -import { DASHBOARD_ROUTE, LOGIN_ROUTE } from '../util/url'; -import { AppConfigContext } from './app-contexts'; -import { AuthenticatorContext, UserContext } from './auth-contexts'; -import Dashboard from './dashboard'; -import { NotFoundPage } from './page-not-found'; - -export default function App(): JSX.Element | null { - const authenticator = appConfig.authenticator; - const [authInitialized, setAuthInitialized] = React.useState(!!authenticator.user); - const [user, setUser] = React.useState(authenticator.user || null); - const appRoutes = [DASHBOARD_ROUTE]; - - React.useEffect(() => { - if (user) { - return; - } - const onUserChanged = (newUser: string | null) => setUser(newUser); - authenticator.on('userChanged', onUserChanged); - (async () => { - await authenticator.init(); - setUser(authenticator.user || null); - setAuthInitialized(true); - })(); - return () => { - authenticator.off('userChanged', onUserChanged); - }; - }, [authenticator, user]); - - const loginRedirect = React.useMemo(() => , []); - - return authInitialized ? ( - - - - - - - - - authenticator.login(`${window.location.origin}${DASHBOARD_ROUTE}`) - } - /> - - - - - - - - - - Go to Login} /> - - - - - - - - ) : null; -} diff --git a/packages/reporting/src/components/auth-contexts.tsx b/packages/reporting/src/components/auth-contexts.tsx deleted file mode 100644 index 91c7f312f..000000000 --- a/packages/reporting/src/components/auth-contexts.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import React from 'react'; -import appConfig from '../app-config'; - -export const AuthenticatorContext = React.createContext(appConfig.authenticator); -export const UserContext = React.createContext(null); diff --git a/packages/reporting/src/components/auth/__mocks__/fake-authenticator.ts b/packages/reporting/src/components/auth/__mocks__/fake-authenticator.ts deleted file mode 100644 index 128989cca..000000000 --- a/packages/reporting/src/components/auth/__mocks__/fake-authenticator.ts +++ /dev/null @@ -1,31 +0,0 @@ -import EventEmitter from 'eventemitter3'; -import { Authenticator, AuthenticatorEventType } from 'rmf-auth'; - -export class FakeAuthenticator - extends EventEmitter - implements Authenticator { - user?: string; - - constructor(user?: string) { - super(); - this.user = user; - } - - async init(): Promise { - // It is required to call this before using any of the authenticator functions. - } - - login(): Promise { - throw new Error('Method not implemented.'); - } - - logout(): Promise { - throw new Error('Method not implemented.'); - } - - refreshToken(): Promise { - return Promise.resolve(); - } -} - -export default FakeAuthenticator; diff --git a/packages/reporting/src/components/dashboard.tsx b/packages/reporting/src/components/dashboard.tsx deleted file mode 100644 index 369f8fe65..000000000 --- a/packages/reporting/src/components/dashboard.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; -import { ReportDashboard } from './report-dashboard'; -import { buildReportMenuStructure } from './reporter-side-bar-structure'; - -function Dashboard() { - return ( -
- -
- ); -} - -export default Dashboard; diff --git a/packages/reporting/src/components/page-not-found.tsx b/packages/reporting/src/components/page-not-found.tsx deleted file mode 100644 index d34bffc41..000000000 --- a/packages/reporting/src/components/page-not-found.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import React from 'react'; -import { styled, Typography } from '@mui/material'; -import type { Link } from 'react-router-dom'; - -export interface NotFoundPageProps { - /** - * The link to redirect user to the correct page. - */ - linkComponent: React.ReactElement; -} - -const prefix = 'page-not-found'; -const classes = { - div: `${prefix}-root`, - img: `${prefix}-img`, - text: `${prefix}-text`, - author: `${prefix}-author`, -}; - -const StyledDiv = styled('div')(() => ({ - [`&.${classes.div}`]: { - width: '100%', - height: '100%', - overflowX: 'hidden', - overflowY: 'hidden', - }, - [`& .${classes.img}`]: { - width: '100%', - height: '100%', - }, - [`& .${classes.text}`]: { - position: 'fixed', - left: '20%', - top: '2%', - color: 'white', - fontWeight: 'bold', - }, - [`& .${classes.author}`]: { - position: 'fixed', - right: '5%', - bottom: '2%', - color: 'white', - }, -})); - -export const NotFoundPage = (props: NotFoundPageProps): React.ReactElement => { - const { linkComponent: routeLinkComponent } = props; - console.warn('Photo by Aron Visuals on Unsplash'); - return ( - - 404 Not Found - - Are you lost? {routeLinkComponent} - - Photo by Aron Visuals on Unsplash - - ); -}; - -export default NotFoundPage; diff --git a/packages/reporting/src/components/report-dashboard.tsx b/packages/reporting/src/components/report-dashboard.tsx deleted file mode 100644 index 75519371e..000000000 --- a/packages/reporting/src/components/report-dashboard.tsx +++ /dev/null @@ -1,299 +0,0 @@ -import React from 'react'; -import { ReportConfigProps } from 'react-components'; -import clsx from 'clsx'; -import { styled, useTheme } from '@mui/material/styles'; -import Drawer from '@mui/material/Drawer'; -import CssBaseline from '@mui/material/CssBaseline'; -import AppBar from '@mui/material/AppBar'; -import Toolbar from '@mui/material/Toolbar'; -import Typography from '@mui/material/Typography'; -import Divider from '@mui/material/Divider'; -import IconButton from '@mui/material/IconButton'; -import MenuIcon from '@mui/icons-material/Menu'; -import ChevronLeftIcon from '@mui/icons-material/ChevronLeft'; -import ChevronRightIcon from '@mui/icons-material/ChevronRight'; -import { ExpandableMultilevelMenuProps, MultiLevelMenu } from 'react-components'; -import { Menu, MenuItem } from '@mui/material'; -import { AuthenticatorContext } from './auth-contexts'; -import AccountCircleIcon from '@mui/icons-material/AccountCircle'; - -import { Reports } from './report-list'; -import { BuildMenuType } from './reporter-side-bar-structure'; -import AllLogsReport from './reports/all-logs-report'; -import DispenserStateReportConfig from './reports/dispenser-state-report'; -import DoorStateReportConfig from './reports/door-state-report'; -import FleetStateReportConfig from './reports/fleet-state-report'; -import HealthReportConfig from './reports/health-report'; -import IngestorStateReportConfig from './reports/ingestor-state-report'; -import LiftStateReportConfig from './reports/lift-state-report'; -import TaskSummaryReportConfig from './reports/task-summary-report'; -import UserLoginFailureReportConfig from './reports/user-login-failure-report'; -import UserLoginReportConfig from './reports/user-login-report'; -import UserLogoutReportConfig from './reports/user-logout-report'; - -const drawerWidth = 240; - -const prefix = 'report-dashboard'; -const classes = { - root: `${prefix}-root`, - appBar: `${prefix}-appbar`, - appBarShift: `${prefix}-appbar-shift`, - menuButton: `${prefix}-menu-button`, - hide: `${prefix}-hide`, - drawer: `${prefix}-drawer`, - drawerPaper: `${prefix}-drawer-paper`, - drawerHeader: `${prefix}-drawer-header`, - content: `${prefix}-content`, - contentShift: `${prefix}-content-shift`, - toolbarTitle: `${prefix}-toolbar-title`, -}; - -const StyledDiv = styled('div')(({ theme }) => ({ - [`&.${classes.root}`]: { - display: 'flex', - }, - [`& .${classes.appBar}`]: { - transition: theme.transitions.create(['margin', 'width'], { - easing: theme.transitions.easing.sharp, - duration: theme.transitions.duration.leavingScreen, - }), - }, - [`& .${classes.appBarShift}`]: { - width: `calc(100% - ${drawerWidth}px)`, - marginLeft: drawerWidth, - transition: theme.transitions.create(['margin', 'width'], { - easing: theme.transitions.easing.easeOut, - duration: theme.transitions.duration.enteringScreen, - }), - }, - [`& .${classes.menuButton}`]: { - marginRight: theme.spacing(2), - }, - [`& .${classes.hide}`]: { - display: 'none', - }, - [`& .${classes.drawer}`]: { - width: drawerWidth, - flexShrink: 0, - }, - [`& .${classes.drawerPaper}`]: { - width: drawerWidth, - }, - [`& .${classes.drawerHeader}`]: { - display: 'flex', - alignItems: 'center', - padding: theme.spacing(0, 1), - // necessary for content to be below app bar - ...theme.mixins.toolbar, - justifyContent: 'flex-end', - }, - [`& .${classes.content}`]: { - flexGrow: 1, - padding: theme.spacing(3), - transition: theme.transitions.create('margin', { - easing: theme.transitions.easing.sharp, - duration: theme.transitions.duration.leavingScreen, - }), - marginLeft: -drawerWidth, - }, - [`& .${classes.contentShift}`]: { - transition: theme.transitions.create('margin', { - easing: theme.transitions.easing.easeOut, - duration: theme.transitions.duration.enteringScreen, - }), - marginLeft: 0, - }, - [`& .${classes.toolbarTitle}`]: { - flexGrow: 1, - }, -})); - -export interface ReportDashboardProps { - buildMenuReportStructure(setCurrentReport: BuildMenuType): ExpandableMultilevelMenuProps[]; -} - -export const ReportDashboard = (props: ReportDashboardProps) => { - const { buildMenuReportStructure } = props; - const theme = useTheme(); - const [open, setOpen] = React.useState(true); - const [currentReport, setCurrentReport] = React.useState(Reports.queryAllLogs); - const [anchorEl, setAnchorEl] = React.useState(null); - - const [fromLogDate, setFromLogDate] = React.useState(new Date()); - const [toLogDate, setToLogDate] = React.useState(new Date()); - - const handleFromLogDateChange = React.useCallback((date: any) => { - setFromLogDate(date); - }, []); - - const handleToLogDateChange = React.useCallback((date: any) => { - setToLogDate(date); - }, []); - - const itemConfig = (props: ReportConfigProps): JSX.Element | null => { - switch (currentReport) { - case Reports.queryAllLogs: - return ; - case Reports.showDispenserStateReport: - return ; - case Reports.showDoorStateReport: - return ; - case Reports.showFleetStateReport: - return ; - case Reports.showHealthReport: - return ; - case Reports.showIngestorStateReport: - return ; - case Reports.showLiftStateReport: - return ; - case Reports.showLoginsReport: - return ; - case Reports.showLogoutsReport: - return ; - case Reports.showLoginFailuresReport: - return ; - case Reports.showTasksReport: - return ; - default: - return null; - } - }; - - const headerTitle = React.useMemo(() => { - switch (currentReport) { - case Reports.queryAllLogs: - return 'All Logs'; - case Reports.showDispenserStateReport: - return 'Dispenser State Report'; - case Reports.showDoorStateReport: - return 'Door State Report'; - case Reports.showFleetStateReport: - return 'Fleet State Report'; - case Reports.showHealthReport: - return 'Health Report'; - case Reports.showIngestorStateReport: - return 'Ingestor State Report'; - case Reports.showLiftStateReport: - return 'Lift State Report'; - case Reports.showLoginsReport: - return 'Login Report'; - case Reports.showLogoutsReport: - return 'Logout Report'; - case Reports.showLoginFailuresReport: - return 'Login Failure Report'; - case Reports.showTasksReport: - return 'Task Report'; - default: - return ''; - } - }, [currentReport]); - - const handleDrawerOpen = () => { - setOpen(true); - }; - - const handleDrawerClose = () => { - setOpen(false); - }; - - const authenticator = React.useContext(AuthenticatorContext); - - async function handleLogout(): Promise { - try { - await authenticator.logout(); - } catch (e) { - console.error(`error logging out: ${(e as Error).message}`); - } - } - - return ( - - - - - - - - - Reports - {headerTitle} - - {authenticator.user && ( - <> - setAnchorEl(event.currentTarget)} - > - - - setAnchorEl(null)} - > - - Logout - - - - )} - - - - {open && ( - <> -
- - {theme.direction === 'ltr' ? : } - -
- - - - - )} -
-
-
- {itemConfig({ - fromLogDate, - toLogDate, - onSelectFromDate: handleFromLogDateChange, - onSelectToDate: handleToLogDateChange, - })} -
-
- ); -}; diff --git a/packages/reporting/src/components/report-list.tsx b/packages/reporting/src/components/report-list.tsx deleted file mode 100644 index 85f4e1e90..000000000 --- a/packages/reporting/src/components/report-list.tsx +++ /dev/null @@ -1,20 +0,0 @@ -export enum Reports { - queryAllLogs = 'queryAllLogs', - showChargerStateReport = 'showChargerStateReport', - showDispenserStateReport = 'showDispenserStateReport', - showDoorStateReport = 'showDoorStateReport', - showFleetStateReport = 'showFleetStateReport', - showHealthReport = 'showHealthReport', - showIngestorStateReport = 'showIngestorStateReport', - showLiftStateReport = 'showLiftStateReport', - showNegotiationsReport = 'showNegotiationsReport', - showRobotStateReport = 'showRobotStateReport', - showRobotMotionPlansReport = 'showRobotMotionPlansReport', - showRobotActionReport = 'showRobotActionReport', - showTasksReport = 'showTasksReport', - showUserActionsReport = 'showUserActionsReport', - showLoginsReport = 'showLoginsReport', - showLogoutsReport = 'showLogoutsReport', - showLoginFailuresReport = 'showLoginFailuresReport', - showWorkCellStatesReport = 'showWorkCellStatesReport', -} diff --git a/packages/reporting/src/components/reporter-side-bar-structure.tsx b/packages/reporting/src/components/reporter-side-bar-structure.tsx deleted file mode 100644 index 6755c12e8..000000000 --- a/packages/reporting/src/components/reporter-side-bar-structure.tsx +++ /dev/null @@ -1,89 +0,0 @@ -import React from 'react'; -import SearchIcon from '@mui/icons-material/Search'; -import AndroidIcon from '@mui/icons-material/Android'; -import ArrowDropUpIcon from '@mui/icons-material/ArrowDropUp'; -import KitchenIcon from '@mui/icons-material/Kitchen'; -import LocalHospitalIcon from '@mui/icons-material/LocalHospital'; -import ArrowForwardIcon from '@mui/icons-material/ArrowForward'; -import ArrowBackIcon from '@mui/icons-material/ArrowBack'; -import AccountCircleIcon from '@mui/icons-material/AccountCircle'; -import PlaylistAddCheckIcon from '@mui/icons-material/PlaylistAddCheck'; - -import { Reports } from './report-list'; -import { ExpandableMultilevelMenuProps } from 'react-components'; - -export type BuildMenuType = (report: Reports) => void; - -export const buildReportMenuStructure = ( - setCurrentReport: BuildMenuType, -): ExpandableMultilevelMenuProps[] => { - return [ - { - icon: , - title: 'All logs', - items: [], - onClick: () => setCurrentReport(Reports.queryAllLogs), - }, - { - icon: , - title: 'Dispensers', - items: [], - onClick: () => setCurrentReport(Reports.showDispenserStateReport), - }, - { - icon: , - title: 'Doors', - items: [], - onClick: () => setCurrentReport(Reports.showDoorStateReport), - }, - { - icon: , - title: 'Fleets', - items: [], - onClick: () => setCurrentReport(Reports.showFleetStateReport), - }, - { - icon: , - title: 'Health', - items: [], - onClick: () => setCurrentReport(Reports.showHealthReport), - }, - { - icon: , - title: 'Ingestor', - items: [], - onClick: () => setCurrentReport(Reports.showIngestorStateReport), - }, - { - icon: , - title: 'Lifts', - onClick: () => setCurrentReport(Reports.showLiftStateReport), - }, - { - icon: , - title: 'Tasks', - onClick: () => setCurrentReport(Reports.showTasksReport), - }, - { - icon: , - title: 'Users', - items: [ - { - title: 'Logins', - items: [], - onClick: () => setCurrentReport(Reports.showLoginsReport), - }, - { - title: 'Logouts', - items: [], - onClick: () => setCurrentReport(Reports.showLogoutsReport), - }, - { - title: 'Login failures', - items: [], - onClick: () => setCurrentReport(Reports.showLoginFailuresReport), - }, - ], - }, - ] as ExpandableMultilevelMenuProps[]; -}; diff --git a/packages/reporting/src/components/reports/all-logs-report.tsx b/packages/reporting/src/components/reports/all-logs-report.tsx deleted file mode 100644 index 8828da9a1..000000000 --- a/packages/reporting/src/components/reports/all-logs-report.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import React from 'react'; -import axios from 'axios'; -import { LogManagement, LogQueryPayload, LogRowsType } from 'react-components'; -import appConfig from '../../app-config'; -import { AuthenticatorContext } from '../auth-contexts'; - -const AllLogsReport = () => { - const authenticator = React.useContext(AuthenticatorContext); - const getLogs = async (params: LogQueryPayload): Promise => { - try { - const response = await axios.get(`${appConfig.reportingServerUrl}/report/raw_logs/`, { - params: { - toLogDate: params.toLogDate ? params.toLogDate : null, - fromLogDate: params.fromLogDate ? params.fromLogDate : null, - containerLabel: params.logLabel, - logLevel: params.logLevel, - offset: params.offset, - }, - headers: { - Authorization: 'Bearer ' + authenticator.token, - }, - }); - return response.data as LogRowsType; - } catch (error) { - console.error(error); - return []; - } - }; - - const getLogServerLabels = async (): Promise<{ label: string; value: string }[]> => { - try { - const response = await axios.get( - `${appConfig.reportingServerUrl}/report/raw_logs/containers`, - { - headers: { - Authorization: 'Bearer ' + authenticator.token, - }, - }, - ); - const labelsData = response.data as string[]; - const labels: { label: string; value: string }[] = []; - labelsData.forEach((element) => { - labels.push({ label: element, value: element }); - }); - // If we want to get results from all labels - labels.push({ label: 'All', value: 'all' }); - return labels; - } catch (error) { - console.error(error); - return []; - } - }; - - return ; -}; - -export default AllLogsReport; diff --git a/packages/reporting/src/components/reports/dispenser-state-report.tsx b/packages/reporting/src/components/reports/dispenser-state-report.tsx deleted file mode 100644 index bee02c1f2..000000000 --- a/packages/reporting/src/components/reports/dispenser-state-report.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import React from 'react'; -import { - DefaultReportQueryPayload, - DispenserStateReport, - DispenserStateRowsType, -} from 'react-components'; -import appConfig from '../../app-config'; -import { AuthenticatorContext } from '../auth-contexts'; -import { getLogData } from './utils'; -import { ReportConfigProps } from 'react-components'; - -const DispenserStateReportConfig = (props: ReportConfigProps) => { - const authenticator = React.useContext(AuthenticatorContext); - const getLogs = async (params: DefaultReportQueryPayload): Promise => { - return (await getLogData( - `${appConfig.reportingServerUrl}/report/dispenser_state/`, - params, - authenticator.token, - )) as DispenserStateRowsType; - }; - - return ; -}; - -export default DispenserStateReportConfig; diff --git a/packages/reporting/src/components/reports/door-state-report.tsx b/packages/reporting/src/components/reports/door-state-report.tsx deleted file mode 100644 index d482243e0..000000000 --- a/packages/reporting/src/components/reports/door-state-report.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import React from 'react'; -import { DefaultReportQueryPayload, DoorStateReport, DoorStateRowsType } from 'react-components'; -import appConfig from '../../app-config'; -import { AuthenticatorContext } from '../auth-contexts'; -import { getLogData } from './utils'; -import { ReportConfigProps } from 'react-components'; - -const DoorStateReportConfig = (props: ReportConfigProps) => { - const authenticator = React.useContext(AuthenticatorContext); - const getLogs = async (params: DefaultReportQueryPayload): Promise => { - return (await getLogData( - `${appConfig.reportingServerUrl}/report/door_state/`, - params, - authenticator.token, - )) as DoorStateRowsType; - }; - - return ; -}; - -export default DoorStateReportConfig; diff --git a/packages/reporting/src/components/reports/fleet-state-report.tsx b/packages/reporting/src/components/reports/fleet-state-report.tsx deleted file mode 100644 index 2ae580ae2..000000000 --- a/packages/reporting/src/components/reports/fleet-state-report.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import React from 'react'; -import { DefaultReportQueryPayload, FleetStateReport, FleetStateRowsType } from 'react-components'; -import appConfig from '../../app-config'; -import { AuthenticatorContext } from '../auth-contexts'; -import { getLogData } from './utils'; -import { ReportConfigProps } from 'react-components'; - -const FleetStateReportConfig = (props: ReportConfigProps) => { - const authenticator = React.useContext(AuthenticatorContext); - const getLogs = async (params: DefaultReportQueryPayload): Promise => { - return (await getLogData( - `${appConfig.reportingServerUrl}/report/fleet_state/`, - params, - authenticator.token, - )) as FleetStateRowsType; - }; - - return ; -}; - -export default FleetStateReportConfig; diff --git a/packages/reporting/src/components/reports/health-report.tsx b/packages/reporting/src/components/reports/health-report.tsx deleted file mode 100644 index c99ab6a6a..000000000 --- a/packages/reporting/src/components/reports/health-report.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import React from 'react'; -import { DefaultReportQueryPayload, HealthReport, HealthRowsType } from 'react-components'; -import appConfig from '../../app-config'; -import { AuthenticatorContext } from '../auth-contexts'; -import { getLogData } from './utils'; -import { ReportConfigProps } from 'react-components'; - -const HealthReportConfig = (props: ReportConfigProps) => { - const authenticator = React.useContext(AuthenticatorContext); - const getLogs = async (params: DefaultReportQueryPayload): Promise => { - return (await getLogData( - `${appConfig.reportingServerUrl}/report/health/`, - params, - authenticator.token, - )) as HealthRowsType; - }; - - return ; -}; - -export default HealthReportConfig; diff --git a/packages/reporting/src/components/reports/ingestor-state-report.tsx b/packages/reporting/src/components/reports/ingestor-state-report.tsx deleted file mode 100644 index 91670fc25..000000000 --- a/packages/reporting/src/components/reports/ingestor-state-report.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import React from 'react'; -import { - DefaultReportQueryPayload, - IngestorStateReport, - IngestorStateRowsType, -} from 'react-components'; -import appConfig from '../../app-config'; -import { AuthenticatorContext } from '../auth-contexts'; -import { getLogData } from './utils'; -import { ReportConfigProps } from 'react-components'; - -const IngestorStateReportConfig = (props: ReportConfigProps) => { - const authenticator = React.useContext(AuthenticatorContext); - const getLogs = async (params: DefaultReportQueryPayload): Promise => { - return (await getLogData( - `${appConfig.reportingServerUrl}/report/ingestor_state/`, - params, - authenticator.token, - )) as IngestorStateRowsType; - }; - - return ; -}; - -export default IngestorStateReportConfig; diff --git a/packages/reporting/src/components/reports/lift-state-report.tsx b/packages/reporting/src/components/reports/lift-state-report.tsx deleted file mode 100644 index 45963d176..000000000 --- a/packages/reporting/src/components/reports/lift-state-report.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import React from 'react'; -import { DefaultReportQueryPayload, LiftStateReport, LiftStateRowsType } from 'react-components'; -import appConfig from '../../app-config'; -import { getLogData } from './utils'; -import { AuthenticatorContext } from '../auth-contexts'; -import { ReportConfigProps } from 'react-components'; - -const LiftStateReportConfig = (props: ReportConfigProps) => { - const authenticator = React.useContext(AuthenticatorContext); - const getLogs = async (params: DefaultReportQueryPayload): Promise => { - return (await getLogData( - `${appConfig.reportingServerUrl}/report/lift_state/`, - params, - authenticator.token, - )) as LiftStateRowsType; - }; - - return ; -}; - -export default LiftStateReportConfig; diff --git a/packages/reporting/src/components/reports/task-summary-report.tsx b/packages/reporting/src/components/reports/task-summary-report.tsx deleted file mode 100644 index 80311a9bf..000000000 --- a/packages/reporting/src/components/reports/task-summary-report.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import React from 'react'; -import { - DefaultReportQueryPayload, - TaskSummaryReport, - TaskSummaryRowsType, -} from 'react-components'; -import appConfig from '../../app-config'; -import { AuthenticatorContext } from '../auth-contexts'; -import { getLogData } from './utils'; -import { ReportConfigProps } from 'react-components'; - -const TaskSummaryReportConfig = (props: ReportConfigProps) => { - const authenticator = React.useContext(AuthenticatorContext); - const getLogs = async (params: DefaultReportQueryPayload): Promise => { - return (await getLogData( - `${appConfig.reportingServerUrl}/report/task_summary/`, - params, - authenticator.token, - )) as TaskSummaryRowsType; - }; - - return ; -}; - -export default TaskSummaryReportConfig; diff --git a/packages/reporting/src/components/reports/user-login-failure-report.tsx b/packages/reporting/src/components/reports/user-login-failure-report.tsx deleted file mode 100644 index 809d67af1..000000000 --- a/packages/reporting/src/components/reports/user-login-failure-report.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import React from 'react'; -import { - DefaultReportQueryPayload, - UserLoginFailureReport, - UserLoginFailureRowsType, -} from 'react-components'; -import appConfig from '../../app-config'; -import { AuthenticatorContext } from '../auth-contexts'; -import { getLogData } from './utils'; -import { ReportConfigProps } from 'react-components'; - -const UserLoginFailureReportConfig = (props: ReportConfigProps) => { - const authenticator = React.useContext(AuthenticatorContext); - const getLogs = async (params: DefaultReportQueryPayload): Promise => { - return (await getLogData( - `${appConfig.reportingServerUrl}/report/user/loginfailure/`, - params, - authenticator.token, - )) as UserLoginFailureRowsType; - }; - - return ; -}; - -export default UserLoginFailureReportConfig; diff --git a/packages/reporting/src/components/reports/user-login-report.tsx b/packages/reporting/src/components/reports/user-login-report.tsx deleted file mode 100644 index c2d7262bb..000000000 --- a/packages/reporting/src/components/reports/user-login-report.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import React from 'react'; -import { DefaultReportQueryPayload, UserLoginReport, UserLoginRowsType } from 'react-components'; -import appConfig from '../../app-config'; -import { AuthenticatorContext } from '../auth-contexts'; -import { getLogData } from './utils'; -import { ReportConfigProps } from 'react-components'; - -const UserLoginReportConfig = (props: ReportConfigProps) => { - const authenticator = React.useContext(AuthenticatorContext); - const getLogs = async (params: DefaultReportQueryPayload): Promise => { - return (await getLogData( - `${appConfig.reportingServerUrl}/report/user/login/`, - params, - authenticator.token, - )) as UserLoginRowsType; - }; - - return ; -}; - -export default UserLoginReportConfig; diff --git a/packages/reporting/src/components/reports/user-logout-report.tsx b/packages/reporting/src/components/reports/user-logout-report.tsx deleted file mode 100644 index daef95d76..000000000 --- a/packages/reporting/src/components/reports/user-logout-report.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import React from 'react'; -import { DefaultReportQueryPayload, UserLogoutReport, UserLogoutRowsType } from 'react-components'; -import appConfig from '../../app-config'; -import { AuthenticatorContext } from '../auth-contexts'; -import { getLogData } from './utils'; -import { ReportConfigProps } from 'react-components'; - -const UserLogoutReportConfig = (props: ReportConfigProps) => { - const authenticator = React.useContext(AuthenticatorContext); - const getLogs = async (params: DefaultReportQueryPayload): Promise => { - return (await getLogData( - `${appConfig.reportingServerUrl}/report/user/logout/`, - params, - authenticator.token, - )) as UserLogoutRowsType; - }; - - return ; -}; - -export default UserLogoutReportConfig; diff --git a/packages/reporting/src/components/reports/utils.ts b/packages/reporting/src/components/reports/utils.ts deleted file mode 100644 index fc895df4d..000000000 --- a/packages/reporting/src/components/reports/utils.ts +++ /dev/null @@ -1,26 +0,0 @@ -import axios from 'axios'; -import { DefaultReportQueryPayload } from 'react-components'; - -export const getLogData = async ( - url: string, - params: DefaultReportQueryPayload, - token: string | undefined | null, -) => { - try { - const response = await axios.get(url, { - params: { - toLogDate: params.toLogDate ? params.toLogDate : null, - fromLogDate: params.fromLogDate ? params.fromLogDate : null, - offset: params.offset, - limit: params.limit, - }, - headers: { - Authorization: 'Bearer ' + token, - }, - }); - return response.data; - } catch (error) { - console.error(error); - return []; - } -}; diff --git a/packages/reporting/src/components/tests/page-not-found.test.tsx b/packages/reporting/src/components/tests/page-not-found.test.tsx deleted file mode 100644 index 62afdf3f4..000000000 --- a/packages/reporting/src/components/tests/page-not-found.test.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import { render } from '@testing-library/react'; -import React from 'react'; -import { BrowserRouter, Link } from 'react-router-dom'; -import NotFoundPage from '../page-not-found'; - -describe('PageNotFound', () => { - test('renders correctly', () => { - const root = render( - - Go to somewhere} /> - , - ); - expect(root.queryByAltText('404 Not Found')).toBeTruthy(); - expect(root.queryByText('Are you lost?')).toBeTruthy(); - }); -}); diff --git a/packages/reporting/src/index.tsx b/packages/reporting/src/index.tsx deleted file mode 100644 index 57645e1f1..000000000 --- a/packages/reporting/src/index.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import App from './components/app'; -import * as serviceWorker from './serviceWorker'; - -ReactDOM.render( - - - , - document.getElementById('root'), -); - -// If you want your app to work offline and load faster, you can change -// unregister() to register() below. Note this comes with some pitfalls. -// Learn more about service workers: https://bit.ly/CRA-PWA -serviceWorker.unregister(); diff --git a/packages/reporting/src/react-app-env.d.ts b/packages/reporting/src/react-app-env.d.ts deleted file mode 100644 index 6431bc5fc..000000000 --- a/packages/reporting/src/react-app-env.d.ts +++ /dev/null @@ -1 +0,0 @@ -/// diff --git a/packages/reporting/src/serviceWorker.ts b/packages/reporting/src/serviceWorker.ts deleted file mode 100644 index 0b72299f9..000000000 --- a/packages/reporting/src/serviceWorker.ts +++ /dev/null @@ -1,139 +0,0 @@ -// This optional code is used to register a service worker. -// register() is not called by default. - -// This lets the app load faster on subsequent visits in production, and gives -// it offline capabilities. However, it also means that developers (and users) -// will only see deployed updates on subsequent visits to a page, after all the -// existing tabs open on the page have been closed, since previously cached -// resources are updated in the background. - -// To learn more about the benefits of this model and instructions on how to -// opt-in, read https://bit.ly/CRA-PWA - -const isLocalhost = Boolean( - window.location.hostname === 'localhost' || - // [::1] is the IPv6 localhost address. - window.location.hostname === '[::1]' || - // 127.0.0.1/8 is considered localhost for IPv4. - window.location.hostname.match(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/), -); - -type Config = { - onSuccess?: (registration: ServiceWorkerRegistration) => void; - onUpdate?: (registration: ServiceWorkerRegistration) => void; -}; - -export function register(config?: Config) { - if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { - // The URL constructor is available in all browsers that support SW. - const publicUrl = new URL( - (process as { env: { [key: string]: string } }).env.PUBLIC_URL, - window.location.href, - ); - if (publicUrl.origin !== window.location.origin) { - // Our service worker won't work if PUBLIC_URL is on a different origin - // from what our page is served on. This might happen if a CDN is used to - // serve assets; see https://github.com/facebook/create-react-app/issues/2374 - return; - } - - window.addEventListener('load', () => { - const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; - - if (isLocalhost) { - // This is running on localhost. Let's check if a service worker still exists or not. - checkValidServiceWorker(swUrl, config); - - // Add some additional logging to localhost, pointing developers to the - // service worker/PWA documentation. - navigator.serviceWorker.ready.then(() => { - console.log( - 'This web app is being served cache-first by a service ' + - 'worker. To learn more, visit https://bit.ly/CRA-PWA', - ); - }); - } else { - // Is not localhost. Just register service worker - registerValidSW(swUrl, config); - } - }); - } -} - -function registerValidSW(swUrl: string, config?: Config) { - navigator.serviceWorker - .register(swUrl) - .then((registration) => { - registration.onupdatefound = () => { - const installingWorker = registration.installing; - if (installingWorker == null) { - return; - } - installingWorker.onstatechange = () => { - if (installingWorker.state === 'installed') { - if (navigator.serviceWorker.controller) { - // At this point, the updated precached content has been fetched, - // but the previous service worker will still serve the older - // content until all client tabs are closed. - console.log( - 'New content is available and will be used when all ' + - 'tabs for this page are closed. See https://bit.ly/CRA-PWA.', - ); - - // Execute callback - if (config && config.onUpdate) { - config.onUpdate(registration); - } - } else { - // At this point, everything has been precached. - // It's the perfect time to display a - // "Content is cached for offline use." message. - console.log('Content is cached for offline use.'); - - // Execute callback - if (config && config.onSuccess) { - config.onSuccess(registration); - } - } - } - }; - }; - }) - .catch((error) => { - console.error('Error during service worker registration:', error); - }); -} - -function checkValidServiceWorker(swUrl: string, config?: Config) { - // Check if the service worker can be found. If it can't reload the page. - fetch(swUrl) - .then((response) => { - // Ensure service worker exists, and that we really are getting a JS file. - const contentType = response.headers.get('content-type'); - if ( - response.status === 404 || - (contentType != null && contentType.indexOf('javascript') === -1) - ) { - // No service worker found. Probably a different app. Reload the page. - navigator.serviceWorker.ready.then((registration) => { - registration.unregister().then(() => { - window.location.reload(); - }); - }); - } else { - // Service worker found. Proceed as normal. - registerValidSW(swUrl, config); - } - }) - .catch(() => { - console.log('No internet connection found. App is running in offline mode.'); - }); -} - -export function unregister() { - if ('serviceWorker' in navigator) { - navigator.serviceWorker.ready.then((registration) => { - registration.unregister(); - }); - } -} diff --git a/packages/reporting/src/setupTests.ts b/packages/reporting/src/setupTests.ts deleted file mode 100644 index 1cbd5760d..000000000 --- a/packages/reporting/src/setupTests.ts +++ /dev/null @@ -1,7 +0,0 @@ -// jest-dom adds custom jest matchers for asserting on DOM nodes. -// allows you to do things like: -// expect(element).toHaveTextContent(/react/i) -// learn more: https://github.com/testing-library/jest-dom -import '@testing-library/jest-dom'; -// To fix `Error: Not implemented: HTMLCanvasElement.prototype.getContext (without installing the canvas npm package)` This happens after react-script versions >3.5 -import 'jest-canvas-mock'; diff --git a/packages/reporting/src/stories/reports/reporter-side-bar.stories.tsx b/packages/reporting/src/stories/reports/reporter-side-bar.stories.tsx deleted file mode 100644 index 839b25b0a..000000000 --- a/packages/reporting/src/stories/reports/reporter-side-bar.stories.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import React from 'react'; -import { ReportDashboard } from '../../components/report-dashboard'; -import { buildReportMenuStructure } from '../../components/reporter-side-bar-structure'; - -export default { - title: 'Reports', - component: ReportDashboard, -}; - -export const StaticReportsView = () => ( - -); diff --git a/packages/reporting/src/tests/components/app.test.tsx b/packages/reporting/src/tests/components/app.test.tsx deleted file mode 100644 index 7e71aa6d8..000000000 --- a/packages/reporting/src/tests/components/app.test.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import React from 'react'; -import { render } from '@testing-library/react'; -import App from '../../components/app'; - -test('smoke test', () => { - render(); -}); diff --git a/packages/reporting/src/tests/components/report-dashboard.test.tsx b/packages/reporting/src/tests/components/report-dashboard.test.tsx deleted file mode 100644 index 88e779d46..000000000 --- a/packages/reporting/src/tests/components/report-dashboard.test.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import { render, screen, cleanup } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import React from 'react'; -import { ReportDashboard } from '../../components/report-dashboard'; -import { buildReportMenuStructure } from '../../components/reporter-side-bar-structure'; - -describe('ReportDashboard', () => { - beforeEach(() => { - render(); - }); - - afterEach(() => cleanup()); - - it('shows the report picker side bar on start', () => { - expect(screen.getByText('All logs')).toBeTruthy(); - }); - - it('closes the side-bar correctly', async () => { - // To check that it's open - expect(screen.getByText('All logs')).toBeTruthy(); - userEvent.click(screen.getByLabelText('close drawer')); - expect(screen.queryByText('All logs')).toBeFalsy(); - }); - - it('it closes side-bar and opens it correctly', async () => { - // To check that it's open - expect(screen.getByText('All logs')).toBeTruthy(); - userEvent.click(screen.getByLabelText('close drawer')); - // To check that it's closed - expect(screen.queryByText('All logs')).toBeFalsy(); - - userEvent.click(screen.getByLabelText('open drawer')); - expect(screen.queryByText('All logs')).toBeTruthy(); - }); -}); - -it('picks a different report and renders correctly', () => { - render(); - userEvent.click(screen.getByText('Doors')); - expect(screen.getByText('Reports - Door State Report')); -}); diff --git a/packages/reporting/src/util/url.ts b/packages/reporting/src/util/url.ts deleted file mode 100644 index 328cdb5b1..000000000 --- a/packages/reporting/src/util/url.ts +++ /dev/null @@ -1,6 +0,0 @@ -export const BASE_PATH = - process.env.PUBLIC_URL === undefined || process.env.PUBLIC_URL === '/' - ? '' - : process.env.PUBLIC_URL; -export const DASHBOARD_ROUTE = BASE_PATH === '' ? '/' : BASE_PATH; -export const LOGIN_ROUTE = `${BASE_PATH}/login`; diff --git a/packages/reporting/tsconfig.json b/packages/reporting/tsconfig.json deleted file mode 100644 index e18c413eb..000000000 --- a/packages/reporting/tsconfig.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "compilerOptions": { - "target": "es5", - "lib": [ - "dom", - "dom.iterable", - "esnext" - ], - "allowJs": true, - "skipLibCheck": true, - "esModuleInterop": true, - "allowSyntheticDefaultImports": true, - "strict": true, - "forceConsistentCasingInFileNames": true, - "module": "esnext", - "moduleResolution": "node", - "resolveJsonModule": true, - "isolatedModules": true, - "noEmit": true, - "jsx": "react-jsx", - "noFallthroughCasesInSwitch": true - }, - "include": [ - "src" - ] -} diff --git a/scripts/generate-pylintrc.js b/scripts/generate-pylintrc.js index 24dd99f82..04893461a 100644 --- a/scripts/generate-pylintrc.js +++ b/scripts/generate-pylintrc.js @@ -7,10 +7,7 @@ const { execSync } = require('child_process'); const fs = require('fs'); fs.copyFileSync(`${__dirname}/base.pylintrc`, `${__dirname}/../packages/api-server/.pylintrc`); -fs.copyFileSync( - `${__dirname}/base.pylintrc`, - `${__dirname}/../packages/reporting-server/.pylintrc`, -); +fs.copyFileSync(`${__dirname}/base.pylintrc`); fs.copyFileSync(`${__dirname}/base.pylintrc`, `${__dirname}/../packages/ros-translator/.pylintrc`); let result = execSync( `pipenv run pylint --rcfile=${__dirname}/base.pylintrc --ignore=CVS,test --generate-rcfile`,