From 8805712ef50b44db421ff5a81bf672a40b13f5fc Mon Sep 17 00:00:00 2001 From: Martin Weise <martin.weise@tuwien.ac.at> Date: Mon, 29 Jul 2024 14:51:37 +0000 Subject: [PATCH] Master --- .docker/.env | 6 +- .docker/config/.gitkeep | 0 .docker/dist.tar.gz | Bin 0 -> 9911 bytes .docker/docker-compose.yml | 18 +- .gitignore | 1 + dbrepo-analyse-service/Pipfile.lock | 733 +++++++++--------- dbrepo-analyse-service/app.py | 8 +- dbrepo-analyse-service/determine_dt.py | 34 +- dbrepo-analyse-service/determine_pk.py | 2 +- .../lib/dbrepo-1.4.4-py3-none-any.whl | Bin 29907 -> 0 bytes .../lib/dbrepo-1.4.4.tar.gz | Bin 39490 -> 0 bytes .../at/tuwien/endpoints/SubsetEndpoint.java | 6 +- .../at/tuwien/endpoints/TableEndpoint.java | 5 +- .../endpoint/SubsetEndpointUnitTest.java | 28 +- .../service/TableServiceIntegrationTest.java | 9 +- .../gateway/impl/KeycloakGatewayImpl.java | 12 +- .../impl/MetadataServiceGatewayImpl.java | 1 + .../java/at/tuwien/mapper/MariaDbMapper.java | 8 +- .../java/at/tuwien/service/TableService.java | 4 +- .../service/impl/TableServiceMariaDbImpl.java | 17 +- .../{setup-schema.sql => 1_setup-schema.sql} | 0 .../{setup-data.sql => 2_setup-data.sql} | 0 .../api/database/table/TableCreateDto.java | 3 - .../api/database/table/TableStatisticDto.java | 16 +- .../table/internal/TableCreateDto.java | 5 - .../at/tuwien/entities/database/Database.java | 1 + .../tuwien/entities/database/table/Table.java | 2 + .../tuwien/service/TableServiceUnitTest.java | 26 +- .../auth/InternalRequestInterceptor.java | 1 - .../tuwien/service/impl/TableServiceImpl.java | 27 +- .../main/java/at/tuwien/test/BaseTest.java | 6 +- dbrepo-search-service/init/app.py | 11 +- .../components/database/DatabaseToolbar.vue | 19 +- dbrepo-ui/components/dialogs/EditTuple.vue | 21 +- dbrepo-ui/components/subset/Builder.vue | 33 +- dbrepo-ui/components/table/TableImport.vue | 287 ++++--- dbrepo-ui/components/table/TableSchema.vue | 101 +-- dbrepo-ui/components/table/TableToolbar.vue | 10 +- dbrepo-ui/locales/de-AT.json | 180 +++-- dbrepo-ui/locales/en-US.json | 65 +- dbrepo-ui/nuxt.config.ts | 280 +++---- .../[database_id]/table/[table_id]/import.vue | 11 +- .../[database_id]/table/[table_id]/info.vue | 30 +- .../table/{import.vue => create/dataset.vue} | 93 ++- .../table/{create.vue => create/schema.vue} | 24 +- dbrepo-ui/pages/signup.vue | 9 +- dbrepo-ui/utils/index.ts | 5 +- docker-compose.yml | 6 +- install.sh | 15 +- make/dev.mk | 12 + 50 files changed, 1144 insertions(+), 1047 deletions(-) create mode 100644 .docker/config/.gitkeep create mode 100644 .docker/dist.tar.gz delete mode 100644 dbrepo-analyse-service/lib/dbrepo-1.4.4-py3-none-any.whl delete mode 100644 dbrepo-analyse-service/lib/dbrepo-1.4.4.tar.gz rename dbrepo-metadata-db/{setup-schema.sql => 1_setup-schema.sql} (100%) rename dbrepo-metadata-db/{setup-data.sql => 2_setup-data.sql} (100%) rename dbrepo-ui/pages/database/[database_id]/table/{import.vue => create/dataset.vue} (85%) rename dbrepo-ui/pages/database/[database_id]/table/{create.vue => create/schema.vue} (93%) diff --git a/.docker/.env b/.docker/.env index 0cba4f0b8c..16b95b4b46 100644 --- a/.docker/.env +++ b/.docker/.env @@ -1,6 +1,6 @@ # general -BASE_URL=https://example.com -ADMIN_EMAIL=support@example.com +BASE_URL=http://localhost +ADMIN_EMAIL=support@localhost # password for the identity service admin user IDENTITY_SERVICE_ADMIN_PASSWORD=admin # password for the auth service admin user @@ -9,7 +9,7 @@ AUTH_SERVICE_ADMIN_PASSWORD=admin METADATA_DB_PASSWORD=dbrepo DATA_DB_PASSWORD=dbrepo AUTH_DB_PASSWORD=dbrepo -SEARCH_DB_PASSWORD=dbrepo +SEARCH_DB_PASSWORD=admin # storage service S3_SECRET_ACCESS_KEY=seaweedfsadmin # internal admin user, requires a change of the value of auth_ldap.dn_lookup_bind.password in dist/rabbitmq.conf diff --git a/.docker/config/.gitkeep b/.docker/config/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/.docker/dist.tar.gz b/.docker/dist.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..8de17c93ea61448bb57c66d16d836b7340fa000b GIT binary patch literal 9911 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 DA$wvy literal 0 HcmV?d00001 diff --git a/.docker/docker-compose.yml b/.docker/docker-compose.yml index 2b11669278..167523e721 100644 --- a/.docker/docker-compose.yml +++ b/.docker/docker-compose.yml @@ -1,5 +1,3 @@ -version: "3.6" - volumes: metadata-db-data: data-db-data: @@ -18,8 +16,8 @@ services: image: docker.io/bitnami/mariadb:11.1.3-debian-11-r6 volumes: - metadata-db-data:/bitnami/mariadb - - ./dist/setup-schema.sql:/docker-entrypoint-initdb.d/1_setup-schema.sql - - ./dist/setup-data.sql:/docker-entrypoint-initdb.d/2_setup-data.sql + - ./config/1_setup-schema.sql:/docker-entrypoint-initdb.d/1_setup-schema.sql + - ./config/2_setup-data.sql:/docker-entrypoint-initdb.d/2_setup-data.sql ports: - "3306:3306" environment: @@ -195,10 +193,10 @@ services: ports: - 5672:5672 volumes: - - ./dist/rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf - - ./dist/advanced.config:/etc/rabbitmq/advanced.config - - ./dist/enabled_plugins:/etc/rabbitmq/enabled_plugins - - ./dist/definitions.json:/app/definitions.json + - ./config/rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf + - ./config/advanced.config:/etc/rabbitmq/advanced.config + - ./config/enabled_plugins:/etc/rabbitmq/enabled_plugins + - ./config/definitions.json:/app/definitions.json - broker-service-data:/bitnami/rabbitmq/mnesia depends_on: dbrepo-identity-service: @@ -300,7 +298,7 @@ services: - "80:80" - "443:443" volumes: - - ./dist/dbrepo.conf:/etc/nginx/conf.d/default.conf + - ./config/dbrepo.conf:/etc/nginx/conf.d/default.conf depends_on: dbrepo-analyse-service: condition: service_healthy @@ -363,7 +361,7 @@ services: image: docker.io/chrislusf/seaweedfs:3.59 command: [ "server", "-dir=/data", "-s3", "-s3.port=9000", "-s3.config=/app/s3_config.json", "-metricsPort=9091" ] volumes: - - ./dist/s3_config.json:/app/s3_config.json + - ./config/s3_config.json:/app/s3_config.json - storage-service-data:/data ports: - "9000:9000" diff --git a/.gitignore b/.gitignore index 78a833095c..b8d8c03487 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ build/ tmp.yaml .docs/.swagger/api-* .scannerwork/ +.docker/config/* # docs .docs/.swagger/dist/ diff --git a/dbrepo-analyse-service/Pipfile.lock b/dbrepo-analyse-service/Pipfile.lock index a34914b5d7..a47ad241be 100644 --- a/dbrepo-analyse-service/Pipfile.lock +++ b/dbrepo-analyse-service/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "81259bb4fb1f303f1bfc9f643c6210d3b5f8de902725e4134cdab680d3fdfbb3" + "sha256": "a445bc066f06e174431552d155daabfdb4271e7f6ba781b61611e816ad8bf48c" }, "pipfile-spec": 6, "requires": { @@ -167,27 +167,28 @@ }, "boto3": { "hashes": [ - "sha256:0314e6598f59ee0f34eb4e6d1a0f69fa65c146d2b88a6e837a527a9956ec2731", - "sha256:d41037e2c680ab8d6c61a0a4ee6bf1fdd9e857f43996672830a95d62d6f6fa79" + "sha256:11edeeacdd517bda3b7615b754d8440820cdc9ddd66794cc995a9693ddeaa3be", + "sha256:f4e6489ba9dc7fb37d53e0e82dbc97f2cb0a4969ef3970e2c88b8f94023ae81a" ], "index": "pypi", - "version": "==1.34.136" + "markers": "python_version >= '3.8'", + "version": "==1.34.149" }, "botocore": { "hashes": [ - "sha256:7f7135178692b39143c8f152a618d2a3b71065a317569a7102d2306d4946f42f", - "sha256:c63fe9032091fb9e9477706a3ebfa4d0c109b807907051d892ed574f9b573e61" + "sha256:2e1eb5ef40102a3d796bb3dd05f2ac5e8fb43fe1ff114b4f6d33153437f5a372", + "sha256:ae6c4be52eeee96f68c116b27d252bab069cd046d61a17cfe8e9da411cf22906" ], "markers": "python_version >= '3.8'", - "version": "==1.34.136" + "version": "==1.34.149" }, "certifi": { "hashes": [ - "sha256:3cd43f1c6fa7dedc5899d69d3ad0398fd018ad1a17fba83ddaf78aa46c747516", - "sha256:ddc6c8ce995e6987e7faf5e3f1b02b302836a0e5d98ece18392cb1a36c72ad56" + "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b", + "sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90" ], "markers": "python_version >= '3.6'", - "version": "==2024.6.2" + "version": "==2024.7.4" }, "cffi": { "hashes": [ @@ -247,6 +248,15 @@ "markers": "platform_python_implementation != 'PyPy'", "version": "==1.16.0" }, + "chardet": { + "hashes": [ + "sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7", + "sha256:e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==5.2.0" + }, "charset-normalizer": { "hashes": [ "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027", @@ -353,50 +363,44 @@ }, "cryptography": { "hashes": [ - "sha256:013629ae70b40af70c9a7a5db40abe5d9054e6f4380e50ce769947b73bf3caad", - "sha256:2346b911eb349ab547076f47f2e035fc8ff2c02380a7cbbf8d87114fa0f1c583", - "sha256:2f66d9cd9147ee495a8374a45ca445819f8929a3efcd2e3df6428e46c3cbb10b", - "sha256:2f88d197e66c65be5e42cd72e5c18afbfae3f741742070e3019ac8f4ac57262c", - "sha256:31f721658a29331f895a5a54e7e82075554ccfb8b163a18719d342f5ffe5ecb1", - "sha256:343728aac38decfdeecf55ecab3264b015be68fc2816ca800db649607aeee648", - "sha256:5226d5d21ab681f432a9c1cf8b658c0cb02533eece706b155e5fbd8a0cdd3949", - "sha256:57080dee41209e556a9a4ce60d229244f7a66ef52750f813bfbe18959770cfba", - "sha256:5a94eccb2a81a309806027e1670a358b99b8fe8bfe9f8d329f27d72c094dde8c", - "sha256:6b7c4f03ce01afd3b76cf69a5455caa9cfa3de8c8f493e0d3ab7d20611c8dae9", - "sha256:7016f837e15b0a1c119d27ecd89b3515f01f90a8615ed5e9427e30d9cdbfed3d", - "sha256:81884c4d096c272f00aeb1f11cf62ccd39763581645b0812e99a91505fa48e0c", - "sha256:81d8a521705787afe7a18d5bfb47ea9d9cc068206270aad0b96a725022e18d2e", - "sha256:8d09d05439ce7baa8e9e95b07ec5b6c886f548deb7e0f69ef25f64b3bce842f2", - "sha256:961e61cefdcb06e0c6d7e3a1b22ebe8b996eb2bf50614e89384be54c48c6b63d", - "sha256:9c0c1716c8447ee7dbf08d6db2e5c41c688544c61074b54fc4564196f55c25a7", - "sha256:a0608251135d0e03111152e41f0cc2392d1e74e35703960d4190b2e0f4ca9c70", - "sha256:a0c5b2b0585b6af82d7e385f55a8bc568abff8923af147ee3c07bd8b42cda8b2", - "sha256:ad803773e9df0b92e0a817d22fd8a3675493f690b96130a5e24f1b8fabbea9c7", - "sha256:b297f90c5723d04bcc8265fc2a0f86d4ea2e0f7ab4b6994459548d3a6b992a14", - "sha256:ba4f0a211697362e89ad822e667d8d340b4d8d55fae72cdd619389fb5912eefe", - "sha256:c4783183f7cb757b73b2ae9aed6599b96338eb957233c58ca8f49a49cc32fd5e", - "sha256:c9bb2ae11bfbab395bdd072985abde58ea9860ed84e59dbc0463a5d0159f5b71", - "sha256:cafb92b2bc622cd1aa6a1dce4b93307792633f4c5fe1f46c6b97cf67073ec961", - "sha256:d45b940883a03e19e944456a558b67a41160e367a719833c53de6911cabba2b7", - "sha256:dc0fdf6787f37b1c6b08e6dfc892d9d068b5bdb671198c72072828b80bd5fe4c", - "sha256:dea567d1b0e8bc5764b9443858b673b734100c2871dc93163f58c46a97a83d28", - "sha256:dec9b018df185f08483f294cae6ccac29e7a6e0678996587363dc352dc65c842", - "sha256:e3ec3672626e1b9e55afd0df6d774ff0e953452886e06e0f1eb7eb0c832e8902", - "sha256:e599b53fd95357d92304510fb7bda8523ed1f79ca98dce2f43c115950aa78801", - "sha256:fa76fbb7596cc5839320000cdd5d0955313696d9511debab7ee7278fc8b5c84a", - "sha256:fff12c88a672ab9c9c1cf7b0c80e3ad9e2ebd9d828d955c126be4fd3e5578c9e" + "sha256:0663585d02f76929792470451a5ba64424acc3cd5227b03921dab0e2f27b1709", + "sha256:08a24a7070b2b6804c1940ff0f910ff728932a9d0e80e7814234269f9d46d069", + "sha256:232ce02943a579095a339ac4b390fbbe97f5b5d5d107f8a08260ea2768be8cc2", + "sha256:2905ccf93a8a2a416f3ec01b1a7911c3fe4073ef35640e7ee5296754e30b762b", + "sha256:299d3da8e00b7e2b54bb02ef58d73cd5f55fb31f33ebbf33bd00d9aa6807df7e", + "sha256:2c6d112bf61c5ef44042c253e4859b3cbbb50df2f78fa8fae6747a7814484a70", + "sha256:31e44a986ceccec3d0498e16f3d27b2ee5fdf69ce2ab89b52eaad1d2f33d8778", + "sha256:3d9a1eca329405219b605fac09ecfc09ac09e595d6def650a437523fcd08dd22", + "sha256:3dcdedae5c7710b9f97ac6bba7e1052b95c7083c9d0e9df96e02a1932e777895", + "sha256:47ca71115e545954e6c1d207dd13461ab81f4eccfcb1345eac874828b5e3eaaf", + "sha256:4a997df8c1c2aae1e1e5ac49c2e4f610ad037fc5a3aadc7b64e39dea42249431", + "sha256:51956cf8730665e2bdf8ddb8da0056f699c1a5715648c1b0144670c1ba00b48f", + "sha256:5bcb8a5620008a8034d39bce21dc3e23735dfdb6a33a06974739bfa04f853947", + "sha256:64c3f16e2a4fc51c0d06af28441881f98c5d91009b8caaff40cf3548089e9c74", + "sha256:6e2b11c55d260d03a8cf29ac9b5e0608d35f08077d8c087be96287f43af3ccdc", + "sha256:7b3f5fe74a5ca32d4d0f302ffe6680fcc5c28f8ef0dc0ae8f40c0f3a1b4fca66", + "sha256:844b6d608374e7d08f4f6e6f9f7b951f9256db41421917dfb2d003dde4cd6b66", + "sha256:9a8d6802e0825767476f62aafed40532bd435e8a5f7d23bd8b4f5fd04cc80ecf", + "sha256:aae4d918f6b180a8ab8bf6511a419473d107df4dbb4225c7b48c5c9602c38c7f", + "sha256:ac1955ce000cb29ab40def14fd1bbfa7af2017cca696ee696925615cafd0dce5", + "sha256:b88075ada2d51aa9f18283532c9f60e72170041bba88d7f37e49cbb10275299e", + "sha256:cb013933d4c127349b3948aa8aaf2f12c0353ad0eccd715ca789c8a0f671646f", + "sha256:cc70b4b581f28d0a254d006f26949245e3657d40d8857066c2ae22a61222ef55", + "sha256:e9c5266c432a1e23738d178e51c2c7a5e2ddf790f248be939448c0ba2021f9d1", + "sha256:ea9e57f8ea880eeea38ab5abf9fbe39f923544d7884228ec67d666abd60f5a47", + "sha256:ee0c405832ade84d4de74b9029bedb7b31200600fa524d218fc29bfa371e97f5", + "sha256:fdcb265de28585de5b859ae13e3846a8e805268a823a12a4da2597f1f5afc9f0" ], "markers": "python_version >= '3.7'", - "version": "==42.0.8" + "version": "==43.0.0" }, "dbrepo": { "hashes": [ - "sha256:2506475fc8fb3f4fdd722e3e92f8e6ad28d0707023c3d8ea5d6d076cef71f395", - "sha256:2bdb48c70b4c99b5044fbfc12aa653c1e9281ca8913a433cc08a1e14cb4bd2ef", - "sha256:dccfaec20a3972a578313206678a119db3d6f898604aab4b694aa2ac37a20629" + "sha256:0a04b67204de6dc969ec68fb21aaead898156077e8a5b6f1e03bb5ab0e124a61", + "sha256:454a182b772cb777d27a22bb334bf059ce68d4e6b5fecae802678fabfdf3f934" ], - "path": "./lib/dbrepo-1.4.5.tar.gz", - "version": "==1.4.5" + "markers": "python_version >= '3.11'", + "path": "./lib/dbrepo-1.4.5.tar.gz" }, "events": { "hashes": [ @@ -406,11 +410,12 @@ }, "exceptiongroup": { "hashes": [ - "sha256:5258b9ed329c5bbdd31a309f53cbfb0b155341807f6ff7606a1e801a891b29ad", - "sha256:a4785e48b045528f5bfe627b6ad554ff32def154f42372786903b7abcfe1aa16" + "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", + "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc" ], "index": "pypi", - "version": "==1.2.1" + "markers": "python_version >= '3.7'", + "version": "==1.2.2" }, "flasgger": { "hashes": [ @@ -425,6 +430,7 @@ "sha256:ceb27b0af3823ea2737928a4d99d125a06175b8512c445cbd9a9ce200ef76842" ], "index": "pypi", + "markers": "python_version >= '3.8'", "version": "==3.0.3" }, "flask-cors": { @@ -449,6 +455,7 @@ "sha256:9215d05a9413d3855764bcd67035e75819d23af2fafb6b55197eb5a3313fdfb2" ], "index": "pypi", + "markers": "python_version >= '3.7' and python_version < '4'", "version": "==4.6.0" }, "frozenlist": { @@ -579,6 +586,7 @@ "sha256:fbfdce91239fe306772faab57597186710d5699213f4df099d1612da7320d682" ], "index": "pypi", + "markers": "python_version >= '3.8'", "version": "==24.2.1" }, "greenlet": { @@ -643,6 +651,7 @@ "sha256:fe754d231288e1e64323cfad462fcee8f0288654c10bdf4f603a39ed923bef33" ], "index": "pypi", + "markers": "python_version >= '3.7'", "version": "==3.0.3" }, "gunicorn": { @@ -651,6 +660,7 @@ "sha256:4a0b436239ff76fb33f11c07a16482c521a7e09c1ce3cc293c2330afe01bec63" ], "index": "pypi", + "markers": "python_version >= '3.7'", "version": "==22.0.0" }, "idna": { @@ -687,11 +697,11 @@ }, "jsonschema": { "hashes": [ - "sha256:5b22d434a45935119af990552c862e5d6d564e8f6601206b305a61fdf661a2b7", - "sha256:ff4cfd6b1367a40e7bc6411caec72effadd3db0bbe5017de188f2d6108335802" + "sha256:d71497fef26351a33265337fa77ffeb82423f3ea21283cd9467bb03999266bc4", + "sha256:fbadb6f8b144a8f8cf9f0b89ba94501d143e50411a1278633f56a7acf7fd5566" ], "markers": "python_version >= '3.8'", - "version": "==4.22.0" + "version": "==4.23.0" }, "jsonschema-specifications": { "hashes": [ @@ -706,6 +716,7 @@ "sha256:61c9170f92e736b530655e75374681d4fcca9cfa8763ab42be57353b2b203494" ], "index": "pypi", + "markers": "python_version >= '3.6'", "version": "==1.3.1" }, "markupsafe": { @@ -888,54 +899,55 @@ }, "numpy": { "hashes": [ - "sha256:04494f6ec467ccb5369d1808570ae55f6ed9b5809d7f035059000a37b8d7e86f", - "sha256:0a43f0974d501842866cc83471bdb0116ba0dffdbaac33ec05e6afed5b615238", - "sha256:0e50842b2295ba8414c8c1d9d957083d5dfe9e16828b37de883f51fc53c4016f", - "sha256:0ec84b9ba0654f3b962802edc91424331f423dcf5d5f926676e0150789cb3d95", - "sha256:17067d097ed036636fa79f6a869ac26df7db1ba22039d962422506640314933a", - "sha256:1cde1753efe513705a0c6d28f5884e22bdc30438bf0085c5c486cdaff40cd67a", - "sha256:1e72728e7501a450288fc8e1f9ebc73d90cfd4671ebbd631f3e7857c39bd16f2", - "sha256:2635dbd200c2d6faf2ef9a0d04f0ecc6b13b3cad54f7c67c61155138835515d2", - "sha256:2ce46fd0b8a0c947ae047d222f7136fc4d55538741373107574271bc00e20e8f", - "sha256:34f003cb88b1ba38cb9a9a4a3161c1604973d7f9d5552c38bc2f04f829536609", - "sha256:354f373279768fa5a584bac997de6a6c9bc535c482592d7a813bb0c09be6c76f", - "sha256:38ecb5b0582cd125f67a629072fed6f83562d9dd04d7e03256c9829bdec027ad", - "sha256:3e8e01233d57639b2e30966c63d36fcea099d17c53bf424d77f088b0f4babd86", - "sha256:3f6bed7f840d44c08ebdb73b1825282b801799e325bcbdfa6bc5c370e5aecc65", - "sha256:4554eb96f0fd263041baf16cf0881b3f5dafae7a59b1049acb9540c4d57bc8cb", - "sha256:46e161722e0f619749d1cd892167039015b2c2817296104487cd03ed4a955995", - "sha256:49d9f7d256fbc804391a7f72d4a617302b1afac1112fac19b6c6cec63fe7fe8a", - "sha256:4d2f62e55a4cd9c58c1d9a1c9edaedcd857a73cb6fda875bf79093f9d9086f85", - "sha256:5f64641b42b2429f56ee08b4f427a4d2daf916ec59686061de751a55aafa22e4", - "sha256:63b92c512d9dbcc37f9d81b123dec99fdb318ba38c8059afc78086fe73820275", - "sha256:6d7696c615765091cc5093f76fd1fa069870304beaccfd58b5dcc69e55ef49c1", - "sha256:79e843d186c8fb1b102bef3e2bc35ef81160ffef3194646a7fdd6a73c6b97196", - "sha256:821eedb7165ead9eebdb569986968b541f9908979c2da8a4967ecac4439bae3d", - "sha256:84554fc53daa8f6abf8e8a66e076aff6ece62de68523d9f665f32d2fc50fd66e", - "sha256:8d83bb187fb647643bd56e1ae43f273c7f4dbcdf94550d7938cfc32566756514", - "sha256:903703372d46bce88b6920a0cd86c3ad82dae2dbef157b5fc01b70ea1cfc430f", - "sha256:9416a5c2e92ace094e9f0082c5fd473502c91651fb896bc17690d6fc475128d6", - "sha256:9a1712c015831da583b21c5bfe15e8684137097969c6d22e8316ba66b5baabe4", - "sha256:9c27f0946a3536403efb0e1c28def1ae6730a72cd0d5878db38824855e3afc44", - "sha256:a356364941fb0593bb899a1076b92dfa2029f6f5b8ba88a14fd0984aaf76d0df", - "sha256:a7039a136017eaa92c1848152827e1424701532ca8e8967fe480fe1569dae581", - "sha256:acd3a644e4807e73b4e1867b769fbf1ce8c5d80e7caaef0d90dcdc640dfc9787", - "sha256:ad0c86f3455fbd0de6c31a3056eb822fc939f81b1618f10ff3406971893b62a5", - "sha256:b4c76e3d4c56f145d41b7b6751255feefae92edbc9a61e1758a98204200f30fc", - "sha256:b6f6a8f45d0313db07d6d1d37bd0b112f887e1369758a5419c0370ba915b3871", - "sha256:c5a59996dc61835133b56a32ebe4ef3740ea5bc19b3983ac60cc32be5a665d54", - "sha256:c73aafd1afca80afecb22718f8700b40ac7cab927b8abab3c3e337d70e10e5a2", - "sha256:cee6cc0584f71adefe2c908856ccc98702baf95ff80092e4ca46061538a2ba98", - "sha256:cef04d068f5fb0518a77857953193b6bb94809a806bd0a14983a8f12ada060c9", - "sha256:cf5d1c9e6837f8af9f92b6bd3e86d513cdc11f60fd62185cc49ec7d1aba34864", - "sha256:e61155fae27570692ad1d327e81c6cf27d535a5d7ef97648a17d922224b216de", - "sha256:e7f387600d424f91576af20518334df3d97bc76a300a755f9a8d6e4f5cadd289", - "sha256:ed08d2703b5972ec736451b818c2eb9da80d66c3e84aed1deeb0c345fefe461b", - "sha256:fbd6acc766814ea6443628f4e6751d0da6593dae29c08c0b2606164db026970c", - "sha256:feff59f27338135776f6d4e2ec7aeeac5d5f7a08a83e80869121ef8164b74af9" + "sha256:08458fbf403bff5e2b45f08eda195d4b0c9b35682311da5a5a0a0925b11b9bd8", + "sha256:0fbb536eac80e27a2793ffd787895242b7f18ef792563d742c2d673bfcb75134", + "sha256:12f5d865d60fb9734e60a60f1d5afa6d962d8d4467c120a1c0cda6eb2964437d", + "sha256:15eb4eca47d36ec3f78cde0a3a2ee24cf05ca7396ef808dda2c0ddad7c2bde67", + "sha256:173a00b9995f73b79eb0191129f2455f1e34c203f559dd118636858cc452a1bf", + "sha256:1b902ce0e0a5bb7704556a217c4f63a7974f8f43e090aff03fcf262e0b135e02", + "sha256:1f682ea61a88479d9498bf2091fdcd722b090724b08b31d63e022adc063bad59", + "sha256:1f87fec1f9bc1efd23f4227becff04bd0e979e23ca50cc92ec88b38489db3b55", + "sha256:24a0e1befbfa14615b49ba9659d3d8818a0f4d8a1c5822af8696706fbda7310c", + "sha256:2c3a346ae20cfd80b6cfd3e60dc179963ef2ea58da5ec074fd3d9e7a1e7ba97f", + "sha256:36d3a9405fd7c511804dc56fc32974fa5533bdeb3cd1604d6b8ff1d292b819c4", + "sha256:3fdabe3e2a52bc4eff8dc7a5044342f8bd9f11ef0934fcd3289a788c0eb10018", + "sha256:4127d4303b9ac9f94ca0441138acead39928938660ca58329fe156f84b9f3015", + "sha256:4658c398d65d1b25e1760de3157011a80375da861709abd7cef3bad65d6543f9", + "sha256:485b87235796410c3519a699cfe1faab097e509e90ebb05dcd098db2ae87e7b3", + "sha256:529af13c5f4b7a932fb0e1911d3a75da204eff023ee5e0e79c1751564221a5c8", + "sha256:5a3d94942c331dd4e0e1147f7a8699a4aa47dffc11bf8a1523c12af8b2e91bbe", + "sha256:5daab361be6ddeb299a918a7c0864fa8618af66019138263247af405018b04e1", + "sha256:61728fba1e464f789b11deb78a57805c70b2ed02343560456190d0501ba37b0f", + "sha256:6790654cb13eab303d8402354fabd47472b24635700f631f041bd0b65e37298a", + "sha256:69ff563d43c69b1baba77af455dd0a839df8d25e8590e79c90fcbe1499ebde42", + "sha256:6bf4e6f4a2a2e26655717a1983ef6324f2664d7011f6ef7482e8c0b3d51e82ac", + "sha256:6e4eeb6eb2fced786e32e6d8df9e755ce5be920d17f7ce00bc38fcde8ccdbf9e", + "sha256:72dc22e9ec8f6eaa206deb1b1355eb2e253899d7347f5e2fae5f0af613741d06", + "sha256:75b4e316c5902d8163ef9d423b1c3f2f6252226d1aa5cd8a0a03a7d01ffc6268", + "sha256:7b9853803278db3bdcc6cd5beca37815b133e9e77ff3d4733c247414e78eb8d1", + "sha256:7d6fddc5fe258d3328cd8e3d7d3e02234c5d70e01ebe377a6ab92adb14039cb4", + "sha256:81b0893a39bc5b865b8bf89e9ad7807e16717f19868e9d234bdaf9b1f1393868", + "sha256:8efc84f01c1cd7e34b3fb310183e72fcdf55293ee736d679b6d35b35d80bba26", + "sha256:8fae4ebbf95a179c1156fab0b142b74e4ba4204c87bde8d3d8b6f9c34c5825ef", + "sha256:99d0d92a5e3613c33a5f01db206a33f8fdf3d71f2912b0de1739894668b7a93b", + "sha256:9adbd9bb520c866e1bfd7e10e1880a1f7749f1f6e5017686a5fbb9b72cf69f82", + "sha256:a1e01dcaab205fbece13c1410253a9eea1b1c9b61d237b6fa59bcc46e8e89343", + "sha256:a8fc2de81ad835d999113ddf87d1ea2b0f4704cbd947c948d2f5513deafe5a7b", + "sha256:b83e16a5511d1b1f8a88cbabb1a6f6a499f82c062a4251892d9ad5d609863fb7", + "sha256:bb2124fdc6e62baae159ebcfa368708867eb56806804d005860b6007388df171", + "sha256:bfc085b28d62ff4009364e7ca34b80a9a080cbd97c2c0630bb5f7f770dae9414", + "sha256:cbab9fc9c391700e3e1287666dfd82d8666d10e69a6c4a09ab97574c0b7ee0a7", + "sha256:e5eeca8067ad04bc8a2a8731183d51d7cbaac66d86085d5f4766ee6bf19c7f87", + "sha256:e9e81fa9017eaa416c056e5d9e71be93d05e2c3c2ab308d23307a8bc4443c368", + "sha256:ea2326a4dca88e4a274ba3a4405eb6c6467d3ffbd8c7d38632502eaae3820587", + "sha256:eacf3291e263d5a67d8c1a581a8ebbcfd6447204ef58828caf69a5e3e8c75990", + "sha256:ec87f5f8aca726117a1c9b7083e7656a9d0d606eec7299cc067bb83d26f16e0c", + "sha256:f1659887361a7151f89e79b276ed8dff3d75877df906328f14d8bb40bb4f5101", + "sha256:f9cf5ea551aec449206954b075db819f52adc1638d46a6738253a712d553c7b4" ], "index": "pypi", - "version": "==2.0.0" + "markers": "python_version >= '3.9'", + "version": "==2.0.1" }, "opensearch-py": { "hashes": [ @@ -943,6 +955,7 @@ "sha256:b6e78b685dd4e9c016d7a4299cf1de69e299c88322e3f81c716e6e23fe5683c1" ], "index": "pypi", + "markers": "python_version >= '3.8' and python_version < '4'", "version": "==2.6.0" }, "packaging": { @@ -986,6 +999,7 @@ "sha256:eee3a87076c0756de40b05c5e9a6069c035ba43e8dd71c379e68cab2c20f16ad" ], "index": "pypi", + "markers": "python_version >= '3.9'", "version": "==2.2.2" }, "pika": { @@ -1006,11 +1020,11 @@ }, "prometheus-flask-exporter": { "hashes": [ - "sha256:7a026b4fdd54ebeddb77589333efe3a1ec43c7c717468825b0b3e9b6c33f7e9e", - "sha256:e4e6beb1b8e1e164da6d70fe1edefc95ef184f113b5047f66f4b7262233da9c0" + "sha256:587c770a1061e93d72c5cbcdefbd7b633fb764e39dffd7dd16932c9124559244", + "sha256:ab49b2c40b57cd35cd51e91e59b3c306b3754477095c4f3cf679034c5122398c" ], "index": "pypi", - "version": "==0.23.0" + "version": "==0.23.1" }, "pycparser": { "hashes": [ @@ -1060,96 +1074,107 @@ }, "pydantic": { "hashes": [ - "sha256:0c84efd9548d545f63ac0060c1e4d39bb9b14db8b3c0652338aecc07b5adec52", - "sha256:ee8538d41ccb9c0a9ad3e0e5f07bf15ed8015b481ced539a1759d8cc89ae90d0" + "sha256:6f62c13d067b0755ad1c21a34bdd06c0c12625a22b0fc09c6b149816604f7c2a", + "sha256:73ee9fddd406dc318b885c7a2eab8a6472b68b8fb5ba8150949fc3db939f23c8" ], "index": "pypi", - "version": "==2.7.4" + "markers": "python_version >= '3.8'", + "version": "==2.8.2" }, "pydantic-core": { "hashes": [ - "sha256:01dd777215e2aa86dfd664daed5957704b769e726626393438f9c87690ce78c3", - "sha256:0eb2a4f660fcd8e2b1c90ad566db2b98d7f3f4717c64fe0a83e0adb39766d5b8", - "sha256:0fbbdc827fe5e42e4d196c746b890b3d72876bdbf160b0eafe9f0334525119c8", - "sha256:123c3cec203e3f5ac7b000bd82235f1a3eced8665b63d18be751f115588fea30", - "sha256:14601cdb733d741b8958224030e2bfe21a4a881fb3dd6fbb21f071cabd48fa0a", - "sha256:18f469a3d2a2fdafe99296a87e8a4c37748b5080a26b806a707f25a902c040a8", - "sha256:19894b95aacfa98e7cb093cd7881a0c76f55731efad31073db4521e2b6ff5b7d", - "sha256:1b4de2e51bbcb61fdebd0ab86ef28062704f62c82bbf4addc4e37fa4b00b7cbc", - "sha256:1d886dc848e60cb7666f771e406acae54ab279b9f1e4143babc9c2258213daa2", - "sha256:1f4d26ceb5eb9eed4af91bebeae4b06c3fb28966ca3a8fb765208cf6b51102ab", - "sha256:21a5e440dbe315ab9825fcd459b8814bb92b27c974cbc23c3e8baa2b76890077", - "sha256:293afe532740370aba8c060882f7d26cfd00c94cae32fd2e212a3a6e3b7bc15e", - "sha256:2f5966897e5461f818e136b8451d0551a2e77259eb0f73a837027b47dc95dab9", - "sha256:2fd41f6eff4c20778d717af1cc50eca52f5afe7805ee530a4fbd0bae284f16e9", - "sha256:2fdf2156aa3d017fddf8aea5adfba9f777db1d6022d392b682d2a8329e087cef", - "sha256:3c40d4eaad41f78e3bbda31b89edc46a3f3dc6e171bf0ecf097ff7a0ffff7cb1", - "sha256:43d447dd2ae072a0065389092a231283f62d960030ecd27565672bd40746c507", - "sha256:44a688331d4a4e2129140a8118479443bd6f1905231138971372fcde37e43528", - "sha256:44c7486a4228413c317952e9d89598bcdfb06399735e49e0f8df643e1ccd0558", - "sha256:44cd83ab6a51da80fb5adbd9560e26018e2ac7826f9626bc06ca3dc074cd198b", - "sha256:46387e38bd641b3ee5ce247563b60c5ca098da9c56c75c157a05eaa0933ed154", - "sha256:4701b19f7e3a06ea655513f7938de6f108123bf7c86bbebb1196eb9bd35cf724", - "sha256:4748321b5078216070b151d5271ef3e7cc905ab170bbfd27d5c83ee3ec436695", - "sha256:4b06beb3b3f1479d32befd1f3079cc47b34fa2da62457cdf6c963393340b56e9", - "sha256:4d0dcc59664fcb8974b356fe0a18a672d6d7cf9f54746c05f43275fc48636851", - "sha256:4e99bc050fe65c450344421017f98298a97cefc18c53bb2f7b3531eb39bc7805", - "sha256:509daade3b8649f80d4e5ff21aa5673e4ebe58590b25fe42fac5f0f52c6f034a", - "sha256:51991a89639a912c17bef4b45c87bd83593aee0437d8102556af4885811d59f5", - "sha256:53db086f9f6ab2b4061958d9c276d1dbe3690e8dd727d6abf2321d6cce37fa94", - "sha256:564d7922e4b13a16b98772441879fcdcbe82ff50daa622d681dd682175ea918c", - "sha256:574d92eac874f7f4db0ca653514d823a0d22e2354359d0759e3f6a406db5d55d", - "sha256:578e24f761f3b425834f297b9935e1ce2e30f51400964ce4801002435a1b41ef", - "sha256:59ff3e89f4eaf14050c8022011862df275b552caef8082e37b542b066ce1ff26", - "sha256:5f09baa656c904807e832cf9cce799c6460c450c4ad80803517032da0cd062e2", - "sha256:6891a2ae0e8692679c07728819b6e2b822fb30ca7445f67bbf6509b25a96332c", - "sha256:6a750aec7bf431517a9fd78cb93c97b9b0c496090fee84a47a0d23668976b4b0", - "sha256:6f5c4d41b2771c730ea1c34e458e781b18cc668d194958e0112455fff4e402b2", - "sha256:77450e6d20016ec41f43ca4a6c63e9fdde03f0ae3fe90e7c27bdbeaece8b1ed4", - "sha256:81b5efb2f126454586d0f40c4d834010979cb80785173d1586df845a632e4e6d", - "sha256:823be1deb01793da05ecb0484d6c9e20baebb39bd42b5d72636ae9cf8350dbd2", - "sha256:834b5230b5dfc0c1ec37b2fda433b271cbbc0e507560b5d1588e2cc1148cf1ce", - "sha256:847a35c4d58721c5dc3dba599878ebbdfd96784f3fb8bb2c356e123bdcd73f34", - "sha256:86110d7e1907ab36691f80b33eb2da87d780f4739ae773e5fc83fb272f88825f", - "sha256:8951eee36c57cd128f779e641e21eb40bc5073eb28b2d23f33eb0ef14ffb3f5d", - "sha256:8a7164fe2005d03c64fd3b85649891cd4953a8de53107940bf272500ba8a788b", - "sha256:8b8bab4c97248095ae0c4455b5a1cd1cdd96e4e4769306ab19dda135ea4cdb07", - "sha256:90afc12421df2b1b4dcc975f814e21bc1754640d502a2fbcc6d41e77af5ec312", - "sha256:938cb21650855054dc54dfd9120a851c974f95450f00683399006aa6e8abb057", - "sha256:942ba11e7dfb66dc70f9ae66b33452f51ac7bb90676da39a7345e99ffb55402d", - "sha256:972658f4a72d02b8abfa2581d92d59f59897d2e9f7e708fdabe922f9087773af", - "sha256:97736815b9cc893b2b7f663628e63f436018b75f44854c8027040e05230eeddb", - "sha256:98906207f29bc2c459ff64fa007afd10a8c8ac080f7e4d5beff4c97086a3dabd", - "sha256:99457f184ad90235cfe8461c4d70ab7dd2680e28821c29eca00252ba90308c78", - "sha256:a0d829524aaefdebccb869eed855e2d04c21d2d7479b6cada7ace5448416597b", - "sha256:a2fdd81edd64342c85ac7cf2753ccae0b79bf2dfa063785503cb85a7d3593223", - "sha256:a55b5b16c839df1070bc113c1f7f94a0af4433fcfa1b41799ce7606e5c79ce0a", - "sha256:a642295cd0c8df1b86fc3dced1d067874c353a188dc8e0f744626d49e9aa51c4", - "sha256:ab86ce7c8f9bea87b9d12c7f0af71102acbf5ecbc66c17796cff45dae54ef9a5", - "sha256:abc267fa9837245cc28ea6929f19fa335f3dc330a35d2e45509b6566dc18be23", - "sha256:ae1d6df168efb88d7d522664693607b80b4080be6750c913eefb77e34c12c71a", - "sha256:b2ebef0e0b4454320274f5e83a41844c63438fdc874ea40a8b5b4ecb7693f1c4", - "sha256:b48ece5bde2e768197a2d0f6e925f9d7e3e826f0ad2271120f8144a9db18d5c8", - "sha256:b7cdf28938ac6b8b49ae5e92f2735056a7ba99c9b110a474473fd71185c1af5d", - "sha256:bb4462bd43c2460774914b8525f79b00f8f407c945d50881568f294c1d9b4443", - "sha256:bc4ff9805858bd54d1a20efff925ccd89c9d2e7cf4986144b30802bf78091c3e", - "sha256:c1322d7dd74713dcc157a2b7898a564ab091ca6c58302d5c7b4c07296e3fd00f", - "sha256:c67598100338d5d985db1b3d21f3619ef392e185e71b8d52bceacc4a7771ea7e", - "sha256:ca26a1e73c48cfc54c4a76ff78df3727b9d9f4ccc8dbee4ae3f73306a591676d", - "sha256:d323a01da91851a4f17bf592faf46149c9169d68430b3146dcba2bb5e5719abc", - "sha256:dc1803ac5c32ec324c5261c7209e8f8ce88e83254c4e1aebdc8b0a39f9ddb443", - "sha256:e00a3f196329e08e43d99b79b286d60ce46bed10f2280d25a1718399457e06be", - "sha256:e85637bc8fe81ddb73fda9e56bab24560bdddfa98aa64f87aaa4e4b6730c23d2", - "sha256:e858ac0a25074ba4bce653f9b5d0a85b7456eaddadc0ce82d3878c22489fa4ee", - "sha256:eae237477a873ab46e8dd748e515c72c0c804fb380fbe6c85533c7de51f23a8f", - "sha256:ebef0dd9bf9b812bf75bda96743f2a6c5734a02092ae7f721c048d156d5fabae", - "sha256:ec3beeada09ff865c344ff3bc2f427f5e6c26401cc6113d77e372c3fdac73864", - "sha256:f76d0ad001edd426b92233d45c746fd08f467d56100fd8f30e9ace4b005266e4", - "sha256:f85d05aa0918283cf29a30b547b4df2fbb56b45b135f9e35b6807cb28bc47951", - "sha256:f9899c94762343f2cc2fc64c13e7cae4c3cc65cdfc87dd810a31654c9b7358cc" + "sha256:035ede2e16da7281041f0e626459bcae33ed998cca6a0a007a5ebb73414ac72d", + "sha256:04024d270cf63f586ad41fff13fde4311c4fc13ea74676962c876d9577bcc78f", + "sha256:0827505a5c87e8aa285dc31e9ec7f4a17c81a813d45f70b1d9164e03a813a686", + "sha256:084659fac3c83fd674596612aeff6041a18402f1e1bc19ca39e417d554468482", + "sha256:10d4204d8ca33146e761c79f83cc861df20e7ae9f6487ca290a97702daf56006", + "sha256:11b71d67b4725e7e2a9f6e9c0ac1239bbc0c48cce3dc59f98635efc57d6dac83", + "sha256:150906b40ff188a3260cbee25380e7494ee85048584998c1e66df0c7a11c17a6", + "sha256:175873691124f3d0da55aeea1d90660a6ea7a3cfea137c38afa0a5ffabe37b88", + "sha256:177f55a886d74f1808763976ac4efd29b7ed15c69f4d838bbd74d9d09cf6fa86", + "sha256:19c0fa39fa154e7e0b7f82f88ef85faa2a4c23cc65aae2f5aea625e3c13c735a", + "sha256:1eedfeb6089ed3fad42e81a67755846ad4dcc14d73698c120a82e4ccf0f1f9f6", + "sha256:225b67a1f6d602de0ce7f6c1c3ae89a4aa25d3de9be857999e9124f15dab486a", + "sha256:242b8feb3c493ab78be289c034a1f659e8826e2233786e36f2893a950a719bb6", + "sha256:254ec27fdb5b1ee60684f91683be95e5133c994cc54e86a0b0963afa25c8f8a6", + "sha256:25e9185e2d06c16ee438ed39bf62935ec436474a6ac4f9358524220f1b236e43", + "sha256:26ab812fa0c845df815e506be30337e2df27e88399b985d0bb4e3ecfe72df31c", + "sha256:26ca695eeee5f9f1aeeb211ffc12f10bcb6f71e2989988fda61dabd65db878d4", + "sha256:26dc97754b57d2fd00ac2b24dfa341abffc380b823211994c4efac7f13b9e90e", + "sha256:270755f15174fb983890c49881e93f8f1b80f0b5e3a3cc1394a255706cabd203", + "sha256:2aafc5a503855ea5885559eae883978c9b6d8c8993d67766ee73d82e841300dd", + "sha256:2d036c7187b9422ae5b262badb87a20a49eb6c5238b2004e96d4da1231badef1", + "sha256:33499e85e739a4b60c9dac710c20a08dc73cb3240c9a0e22325e671b27b70d24", + "sha256:37eee5b638f0e0dcd18d21f59b679686bbd18917b87db0193ae36f9c23c355fc", + "sha256:38cf1c40a921d05c5edc61a785c0ddb4bed67827069f535d794ce6bcded919fc", + "sha256:3acae97ffd19bf091c72df4d726d552c473f3576409b2a7ca36b2f535ffff4a3", + "sha256:3c5ebac750d9d5f2706654c638c041635c385596caf68f81342011ddfa1e5598", + "sha256:3d482efec8b7dc6bfaedc0f166b2ce349df0011f5d2f1f25537ced4cfc34fd98", + "sha256:407653af5617f0757261ae249d3fba09504d7a71ab36ac057c938572d1bc9331", + "sha256:40a783fb7ee353c50bd3853e626f15677ea527ae556429453685ae32280c19c2", + "sha256:41e81317dd6a0127cabce83c0c9c3fbecceae981c8391e6f1dec88a77c8a569a", + "sha256:41f4c96227a67a013e7de5ff8f20fb496ce573893b7f4f2707d065907bffdbd6", + "sha256:469f29f9093c9d834432034d33f5fe45699e664f12a13bf38c04967ce233d688", + "sha256:4745f4ac52cc6686390c40eaa01d48b18997cb130833154801a442323cc78f91", + "sha256:4868f6bd7c9d98904b748a2653031fc9c2f85b6237009d475b1008bfaeb0a5aa", + "sha256:4aa223cd1e36b642092c326d694d8bf59b71ddddc94cdb752bbbb1c5c91d833b", + "sha256:4dd484681c15e6b9a977c785a345d3e378d72678fd5f1f3c0509608da24f2ac0", + "sha256:4f2790949cf385d985a31984907fecb3896999329103df4e4983a4a41e13e840", + "sha256:512ecfbefef6dac7bc5eaaf46177b2de58cdf7acac8793fe033b24ece0b9566c", + "sha256:516d9227919612425c8ef1c9b869bbbee249bc91912c8aaffb66116c0b447ebd", + "sha256:53e431da3fc53360db73eedf6f7124d1076e1b4ee4276b36fb25514544ceb4a3", + "sha256:595ba5be69b35777474fa07f80fc260ea71255656191adb22a8c53aba4479231", + "sha256:5b5ff4911aea936a47d9376fd3ab17e970cc543d1b68921886e7f64bd28308d1", + "sha256:5d41e6daee2813ecceea8eda38062d69e280b39df793f5a942fa515b8ed67953", + "sha256:5e999ba8dd90e93d57410c5e67ebb67ffcaadcea0ad973240fdfd3a135506250", + "sha256:5f239eb799a2081495ea659d8d4a43a8f42cd1fe9ff2e7e436295c38a10c286a", + "sha256:635fee4e041ab9c479e31edda27fcf966ea9614fff1317e280d99eb3e5ab6fe2", + "sha256:65db0f2eefcaad1a3950f498aabb4875c8890438bc80b19362cf633b87a8ab20", + "sha256:6b507132dcfc0dea440cce23ee2182c0ce7aba7054576efc65634f080dbe9434", + "sha256:6b9d9bb600328a1ce523ab4f454859e9d439150abb0906c5a1983c146580ebab", + "sha256:70c8daf4faca8da5a6d655f9af86faf6ec2e1768f4b8b9d0226c02f3d6209703", + "sha256:77bf3ac639c1ff567ae3b47f8d4cc3dc20f9966a2a6dd2311dcc055d3d04fb8a", + "sha256:784c1214cb6dd1e3b15dd8b91b9a53852aed16671cc3fbe4786f4f1db07089e2", + "sha256:7eb6a0587eded33aeefea9f916899d42b1799b7b14b8f8ff2753c0ac1741edac", + "sha256:7ed1b0132f24beeec5a78b67d9388656d03e6a7c837394f99257e2d55b461611", + "sha256:8ad4aeb3e9a97286573c03df758fc7627aecdd02f1da04516a86dc159bf70121", + "sha256:964faa8a861d2664f0c7ab0c181af0bea66098b1919439815ca8803ef136fc4e", + "sha256:9dc1b507c12eb0481d071f3c1808f0529ad41dc415d0ca11f7ebfc666e66a18b", + "sha256:9ebfef07dbe1d93efb94b4700f2d278494e9162565a54f124c404a5656d7ff09", + "sha256:a45f84b09ac9c3d35dfcf6a27fd0634d30d183205230a0ebe8373a0e8cfa0906", + "sha256:a4f55095ad087474999ee28d3398bae183a66be4823f753cd7d67dd0153427c9", + "sha256:a6d511cc297ff0883bc3708b465ff82d7560193169a8b93260f74ecb0a5e08a7", + "sha256:a8ad4c766d3f33ba8fd692f9aa297c9058970530a32c728a2c4bfd2616d3358b", + "sha256:aa2f457b4af386254372dfa78a2eda2563680d982422641a85f271c859df1987", + "sha256:b03f7941783b4c4a26051846dea594628b38f6940a2fdc0df00b221aed39314c", + "sha256:b0dae11d8f5ded51699c74d9548dcc5938e0804cc8298ec0aa0da95c21fff57b", + "sha256:b91ced227c41aa29c672814f50dbb05ec93536abf8f43cd14ec9521ea09afe4e", + "sha256:bc633a9fe1eb87e250b5c57d389cf28998e4292336926b0b6cdaee353f89a237", + "sha256:bebb4d6715c814597f85297c332297c6ce81e29436125ca59d1159b07f423eb1", + "sha256:c336a6d235522a62fef872c6295a42ecb0c4e1d0f1a3e500fe949415761b8a19", + "sha256:c6514f963b023aeee506678a1cf821fe31159b925c4b76fe2afa94cc70b3222b", + "sha256:c693e916709c2465b02ca0ad7b387c4f8423d1db7b4649c551f27a529181c5ad", + "sha256:c81131869240e3e568916ef4c307f8b99583efaa60a8112ef27a366eefba8ef0", + "sha256:d02a72df14dfdbaf228424573a07af10637bd490f0901cee872c4f434a735b94", + "sha256:d2a8fa9d6d6f891f3deec72f5cc668e6f66b188ab14bb1ab52422fe8e644f312", + "sha256:d2b27e6af28f07e2f195552b37d7d66b150adbaa39a6d327766ffd695799780f", + "sha256:d2fe69c5434391727efa54b47a1e7986bb0186e72a41b203df8f5b0a19a4f669", + "sha256:d3f3ed29cd9f978c604708511a1f9c2fdcb6c38b9aae36a51905b8811ee5cbf1", + "sha256:d573faf8eb7e6b1cbbcb4f5b247c60ca8be39fe2c674495df0eb4318303137fe", + "sha256:e0bbdd76ce9aa5d4209d65f2b27fc6e5ef1312ae6c5333c26db3f5ade53a1e99", + "sha256:e7c4ea22b6739b162c9ecaaa41d718dfad48a244909fe7ef4b54c0b530effc5a", + "sha256:e93e1a4b4b33daed65d781a57a522ff153dcf748dee70b40c7258c5861e1768a", + "sha256:e97fdf088d4b31ff4ba35db26d9cc472ac7ef4a2ff2badeabf8d727b3377fc52", + "sha256:e9fa4c9bf273ca41f940bceb86922a7667cd5bf90e95dbb157cbb8441008482c", + "sha256:eaad4ff2de1c3823fddf82f41121bdf453d922e9a238642b1dedb33c4e4f98ad", + "sha256:f1f62b2413c3a0e846c3b838b2ecd6c7a19ec6793b2a522745b0869e37ab5bc1", + "sha256:f6d6cff3538391e8486a431569b77921adfcdef14eb18fbf19b7c0a5294d4e6a", + "sha256:f9aa05d09ecf4c75157197f27cdc9cfaeb7c5f15021c6373932bf3e124af029f", + "sha256:fa2fddcb7107e0d1808086ca306dcade7df60a13a6c347a7acf1ec139aa6789a", + "sha256:faa6b09ee09433b87992fb5a2859efd1c264ddc37280d2dd5db502126d0e7f27" ], "markers": "python_version >= '3.8'", - "version": "==2.18.4" + "version": "==2.20.1" }, "pyjwt": { "hashes": [ @@ -1245,112 +1270,117 @@ "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6" ], "index": "pypi", + "markers": "python_version >= '3.8'", "version": "==2.32.3" }, "rpds-py": { "hashes": [ - "sha256:05f3d615099bd9b13ecf2fc9cf2d839ad3f20239c678f461c753e93755d629ee", - "sha256:06d218939e1bf2ca50e6b0ec700ffe755e5216a8230ab3e87c059ebb4ea06afc", - "sha256:07f2139741e5deb2c5154a7b9629bc5aa48c766b643c1a6750d16f865a82c5fc", - "sha256:08d74b184f9ab6289b87b19fe6a6d1a97fbfea84b8a3e745e87a5de3029bf944", - "sha256:0abeee75434e2ee2d142d650d1e54ac1f8b01e6e6abdde8ffd6eeac6e9c38e20", - "sha256:154bf5c93d79558b44e5b50cc354aa0459e518e83677791e6adb0b039b7aa6a7", - "sha256:17c6d2155e2423f7e79e3bb18151c686d40db42d8645e7977442170c360194d4", - "sha256:1805d5901779662d599d0e2e4159d8a82c0b05faa86ef9222bf974572286b2b6", - "sha256:19ba472b9606c36716062c023afa2484d1e4220548751bda14f725a7de17b4f6", - "sha256:19e515b78c3fc1039dd7da0a33c28c3154458f947f4dc198d3c72db2b6b5dc93", - "sha256:1d54f74f40b1f7aaa595a02ff42ef38ca654b1469bef7d52867da474243cc633", - "sha256:207c82978115baa1fd8d706d720b4a4d2b0913df1c78c85ba73fe6c5804505f0", - "sha256:2625f03b105328729f9450c8badda34d5243231eef6535f80064d57035738360", - "sha256:27bba383e8c5231cd559affe169ca0b96ec78d39909ffd817f28b166d7ddd4d8", - "sha256:2c3caec4ec5cd1d18e5dd6ae5194d24ed12785212a90b37f5f7f06b8bedd7139", - "sha256:2cc7c1a47f3a63282ab0f422d90ddac4aa3034e39fc66a559ab93041e6505da7", - "sha256:2fc24a329a717f9e2448f8cd1f960f9dac4e45b6224d60734edeb67499bab03a", - "sha256:312fe69b4fe1ffbe76520a7676b1e5ac06ddf7826d764cc10265c3b53f96dbe9", - "sha256:32b7daaa3e9389db3695964ce8e566e3413b0c43e3394c05e4b243a4cd7bef26", - "sha256:338dee44b0cef8b70fd2ef54b4e09bb1b97fc6c3a58fea5db6cc083fd9fc2724", - "sha256:352a88dc7892f1da66b6027af06a2e7e5d53fe05924cc2cfc56495b586a10b72", - "sha256:35b2b771b13eee8729a5049c976197ff58a27a3829c018a04341bcf1ae409b2b", - "sha256:38e14fb4e370885c4ecd734f093a2225ee52dc384b86fa55fe3f74638b2cfb09", - "sha256:3c20f05e8e3d4fc76875fc9cb8cf24b90a63f5a1b4c5b9273f0e8225e169b100", - "sha256:3dd3cd86e1db5aadd334e011eba4e29d37a104b403e8ca24dcd6703c68ca55b3", - "sha256:489bdfe1abd0406eba6b3bb4fdc87c7fa40f1031de073d0cfb744634cc8fa261", - "sha256:48c2faaa8adfacefcbfdb5f2e2e7bdad081e5ace8d182e5f4ade971f128e6bb3", - "sha256:4a98a1f0552b5f227a3d6422dbd61bc6f30db170939bd87ed14f3c339aa6c7c9", - "sha256:4adec039b8e2928983f885c53b7cc4cda8965b62b6596501a0308d2703f8af1b", - "sha256:4e0ee01ad8260184db21468a6e1c37afa0529acc12c3a697ee498d3c2c4dcaf3", - "sha256:51584acc5916212e1bf45edd17f3a6b05fe0cbb40482d25e619f824dccb679de", - "sha256:531796fb842b53f2695e94dc338929e9f9dbf473b64710c28af5a160b2a8927d", - "sha256:5463c47c08630007dc0fe99fb480ea4f34a89712410592380425a9b4e1611d8e", - "sha256:5c45a639e93a0c5d4b788b2613bd637468edd62f8f95ebc6fcc303d58ab3f0a8", - "sha256:6031b25fb1b06327b43d841f33842b383beba399884f8228a6bb3df3088485ff", - "sha256:607345bd5912aacc0c5a63d45a1f73fef29e697884f7e861094e443187c02be5", - "sha256:618916f5535784960f3ecf8111581f4ad31d347c3de66d02e728de460a46303c", - "sha256:636a15acc588f70fda1661234761f9ed9ad79ebed3f2125d44be0862708b666e", - "sha256:673fdbbf668dd958eff750e500495ef3f611e2ecc209464f661bc82e9838991e", - "sha256:6afd80f6c79893cfc0574956f78a0add8c76e3696f2d6a15bca2c66c415cf2d4", - "sha256:6b5ff7e1d63a8281654b5e2896d7f08799378e594f09cf3674e832ecaf396ce8", - "sha256:6c4c4c3f878df21faf5fac86eda32671c27889e13570645a9eea0a1abdd50922", - "sha256:6cd8098517c64a85e790657e7b1e509b9fe07487fd358e19431cb120f7d96338", - "sha256:6d1e42d2735d437e7e80bab4d78eb2e459af48c0a46e686ea35f690b93db792d", - "sha256:6e30ac5e329098903262dc5bdd7e2086e0256aa762cc8b744f9e7bf2a427d3f8", - "sha256:70a838f7754483bcdc830444952fd89645569e7452e3226de4a613a4c1793fb2", - "sha256:720edcb916df872d80f80a1cc5ea9058300b97721efda8651efcd938a9c70a72", - "sha256:732672fbc449bab754e0b15356c077cc31566df874964d4801ab14f71951ea80", - "sha256:740884bc62a5e2bbb31e584f5d23b32320fd75d79f916f15a788d527a5e83644", - "sha256:7700936ef9d006b7ef605dc53aa364da2de5a3aa65516a1f3ce73bf82ecfc7ae", - "sha256:7732770412bab81c5a9f6d20aeb60ae943a9b36dcd990d876a773526468e7163", - "sha256:7750569d9526199c5b97e5a9f8d96a13300950d910cf04a861d96f4273d5b104", - "sha256:7f1944ce16401aad1e3f7d312247b3d5de7981f634dc9dfe90da72b87d37887d", - "sha256:81c5196a790032e0fc2464c0b4ab95f8610f96f1f2fa3d4deacce6a79852da60", - "sha256:8352f48d511de5f973e4f2f9412736d7dea76c69faa6d36bcf885b50c758ab9a", - "sha256:8927638a4d4137a289e41d0fd631551e89fa346d6dbcfc31ad627557d03ceb6d", - "sha256:8c7672e9fba7425f79019db9945b16e308ed8bc89348c23d955c8c0540da0a07", - "sha256:8d2e182c9ee01135e11e9676e9a62dfad791a7a467738f06726872374a83db49", - "sha256:910e71711d1055b2768181efa0a17537b2622afeb0424116619817007f8a2b10", - "sha256:942695a206a58d2575033ff1e42b12b2aece98d6003c6bc739fbf33d1773b12f", - "sha256:9437ca26784120a279f3137ee080b0e717012c42921eb07861b412340f85bae2", - "sha256:967342e045564cef76dfcf1edb700b1e20838d83b1aa02ab313e6a497cf923b8", - "sha256:998125738de0158f088aef3cb264a34251908dd2e5d9966774fdab7402edfab7", - "sha256:9e6934d70dc50f9f8ea47081ceafdec09245fd9f6032669c3b45705dea096b88", - "sha256:a3d456ff2a6a4d2adcdf3c1c960a36f4fd2fec6e3b4902a42a384d17cf4e7a65", - "sha256:a7b28c5b066bca9a4eb4e2f2663012debe680f097979d880657f00e1c30875a0", - "sha256:a888e8bdb45916234b99da2d859566f1e8a1d2275a801bb8e4a9644e3c7e7909", - "sha256:aa3679e751408d75a0b4d8d26d6647b6d9326f5e35c00a7ccd82b78ef64f65f8", - "sha256:aaa71ee43a703c321906813bb252f69524f02aa05bf4eec85f0c41d5d62d0f4c", - "sha256:b646bf655b135ccf4522ed43d6902af37d3f5dbcf0da66c769a2b3938b9d8184", - "sha256:b906b5f58892813e5ba5c6056d6a5ad08f358ba49f046d910ad992196ea61397", - "sha256:b9bb1f182a97880f6078283b3505a707057c42bf55d8fca604f70dedfdc0772a", - "sha256:bd1105b50ede37461c1d51b9698c4f4be6e13e69a908ab7751e3807985fc0346", - "sha256:bf18932d0003c8c4d51a39f244231986ab23ee057d235a12b2684ea26a353590", - "sha256:c273e795e7a0f1fddd46e1e3cb8be15634c29ae8ff31c196debb620e1edb9333", - "sha256:c69882964516dc143083d3795cb508e806b09fc3800fd0d4cddc1df6c36e76bb", - "sha256:c827576e2fa017a081346dce87d532a5310241648eb3700af9a571a6e9fc7e74", - "sha256:cbfbea39ba64f5e53ae2915de36f130588bba71245b418060ec3330ebf85678e", - "sha256:ce0bb20e3a11bd04461324a6a798af34d503f8d6f1aa3d2aa8901ceaf039176d", - "sha256:d0cee71bc618cd93716f3c1bf56653740d2d13ddbd47673efa8bf41435a60daa", - "sha256:d21be4770ff4e08698e1e8e0bce06edb6ea0626e7c8f560bc08222880aca6a6f", - "sha256:d31dea506d718693b6b2cffc0648a8929bdc51c70a311b2770f09611caa10d53", - "sha256:d44607f98caa2961bab4fa3c4309724b185b464cdc3ba6f3d7340bac3ec97cc1", - "sha256:d58ad6317d188c43750cb76e9deacf6051d0f884d87dc6518e0280438648a9ac", - "sha256:d70129cef4a8d979caa37e7fe957202e7eee8ea02c5e16455bc9808a59c6b2f0", - "sha256:d85164315bd68c0806768dc6bb0429c6f95c354f87485ee3593c4f6b14def2bd", - "sha256:d960de62227635d2e61068f42a6cb6aae91a7fe00fca0e3aeed17667c8a34611", - "sha256:dc48b479d540770c811fbd1eb9ba2bb66951863e448efec2e2c102625328e92f", - "sha256:e1735502458621921cee039c47318cb90b51d532c2766593be6207eec53e5c4c", - "sha256:e2be6e9dd4111d5b31ba3b74d17da54a8319d8168890fbaea4b9e5c3de630ae5", - "sha256:e4c39ad2f512b4041343ea3c7894339e4ca7839ac38ca83d68a832fc8b3748ab", - "sha256:ed402d6153c5d519a0faf1bb69898e97fb31613b49da27a84a13935ea9164dfc", - "sha256:ee17cd26b97d537af8f33635ef38be873073d516fd425e80559f4585a7b90c43", - "sha256:f3027be483868c99b4985fda802a57a67fdf30c5d9a50338d9db646d590198da", - "sha256:f5bab211605d91db0e2995a17b5c6ee5edec1270e46223e513eaa20da20076ac", - "sha256:f6f8e3fecca256fefc91bb6765a693d96692459d7d4c644660a9fff32e517843", - "sha256:f7afbfee1157e0f9376c00bb232e80a60e59ed716e3211a80cb8506550671e6e", - "sha256:fa242ac1ff583e4ec7771141606aafc92b361cd90a05c30d93e343a0c2d82a89", - "sha256:fab6ce90574645a0d6c58890e9bcaac8d94dff54fb51c69e5522a7358b80ab64" + "sha256:01227f8b3e6c8961490d869aa65c99653df80d2f0a7fde8c64ebddab2b9b02fd", + "sha256:08ce9c95a0b093b7aec75676b356a27879901488abc27e9d029273d280438505", + "sha256:0b02dd77a2de6e49078c8937aadabe933ceac04b41c5dde5eca13a69f3cf144e", + "sha256:0d4b52811dcbc1aba08fd88d475f75b4f6db0984ba12275d9bed1a04b2cae9b5", + "sha256:13e6d4840897d4e4e6b2aa1443e3a8eca92b0402182aafc5f4ca1f5e24f9270a", + "sha256:1a129c02b42d46758c87faeea21a9f574e1c858b9f358b6dd0bbd71d17713175", + "sha256:1a8dfa125b60ec00c7c9baef945bb04abf8ac772d8ebefd79dae2a5f316d7850", + "sha256:1c32e41de995f39b6b315d66c27dea3ef7f7c937c06caab4c6a79a5e09e2c415", + "sha256:1d494887d40dc4dd0d5a71e9d07324e5c09c4383d93942d391727e7a40ff810b", + "sha256:1d4af2eb520d759f48f1073ad3caef997d1bfd910dc34e41261a595d3f038a94", + "sha256:1fb93d3486f793d54a094e2bfd9cd97031f63fcb5bc18faeb3dd4b49a1c06523", + "sha256:24f8ae92c7fae7c28d0fae9b52829235df83f34847aa8160a47eb229d9666c7b", + "sha256:24fc5a84777cb61692d17988989690d6f34f7f95968ac81398d67c0d0994a897", + "sha256:26ab43b6d65d25b1a333c8d1b1c2f8399385ff683a35ab5e274ba7b8bb7dc61c", + "sha256:271accf41b02687cef26367c775ab220372ee0f4925591c6796e7c148c50cab5", + "sha256:2ddd50f18ebc05ec29a0d9271e9dbe93997536da3546677f8ca00b76d477680c", + "sha256:31dd5794837f00b46f4096aa8ccaa5972f73a938982e32ed817bb520c465e520", + "sha256:31e450840f2f27699d014cfc8865cc747184286b26d945bcea6042bb6aa4d26e", + "sha256:32e0db3d6e4f45601b58e4ac75c6f24afbf99818c647cc2066f3e4b192dabb1f", + "sha256:346557f5b1d8fd9966059b7a748fd79ac59f5752cd0e9498d6a40e3ac1c1875f", + "sha256:34bca66e2e3eabc8a19e9afe0d3e77789733c702c7c43cd008e953d5d1463fde", + "sha256:3511f6baf8438326e351097cecd137eb45c5f019944fe0fd0ae2fea2fd26be39", + "sha256:35af5e4d5448fa179fd7fff0bba0fba51f876cd55212f96c8bbcecc5c684ae5c", + "sha256:3837c63dd6918a24de6c526277910e3766d8c2b1627c500b155f3eecad8fad65", + "sha256:39d67896f7235b2c886fb1ee77b1491b77049dcef6fbf0f401e7b4cbed86bbd4", + "sha256:3b823be829407393d84ee56dc849dbe3b31b6a326f388e171555b262e8456cc1", + "sha256:3c73254c256081704dba0a333457e2fb815364018788f9b501efe7c5e0ada401", + "sha256:3ddab996807c6b4227967fe1587febade4e48ac47bb0e2d3e7858bc621b1cace", + "sha256:3e1dc59a5e7bc7f44bd0c048681f5e05356e479c50be4f2c1a7089103f1621d5", + "sha256:4383beb4a29935b8fa28aca8fa84c956bf545cb0c46307b091b8d312a9150e6a", + "sha256:4cc4bc73e53af8e7a42c8fd7923bbe35babacfa7394ae9240b3430b5dcf16b2a", + "sha256:4dd02e29c8cbed21a1875330b07246b71121a1c08e29f0ee3db5b4cfe16980c4", + "sha256:4f580ae79d0b861dfd912494ab9d477bea535bfb4756a2269130b6607a21802e", + "sha256:53dbc35808c6faa2ce3e48571f8f74ef70802218554884787b86a30947842a14", + "sha256:56313be667a837ff1ea3508cebb1ef6681d418fa2913a0635386cf29cff35165", + "sha256:57863d16187995c10fe9cf911b897ed443ac68189179541734502353af33e693", + "sha256:5953391af1405f968eb5701ebbb577ebc5ced8d0041406f9052638bafe52209d", + "sha256:5beffdbe766cfe4fb04f30644d822a1080b5359df7db3a63d30fa928375b2720", + "sha256:5e360188b72f8080fefa3adfdcf3618604cc8173651c9754f189fece068d2a45", + "sha256:5e58b61dcbb483a442c6239c3836696b79f2cd8e7eec11e12155d3f6f2d886d1", + "sha256:69084fd29bfeff14816666c93a466e85414fe6b7d236cfc108a9c11afa6f7301", + "sha256:6d1d7539043b2b31307f2c6c72957a97c839a88b2629a348ebabe5aa8b626d6b", + "sha256:6d8b735c4d162dc7d86a9cf3d717f14b6c73637a1f9cd57fe7e61002d9cb1972", + "sha256:6ea961a674172ed2235d990d7edf85d15d8dfa23ab8575e48306371c070cda67", + "sha256:71157f9db7f6bc6599a852852f3389343bea34315b4e6f109e5cbc97c1fb2963", + "sha256:720f3108fb1bfa32e51db58b832898372eb5891e8472a8093008010911e324c5", + "sha256:74129d5ffc4cde992d89d345f7f7d6758320e5d44a369d74d83493429dad2de5", + "sha256:747251e428406b05fc86fee3904ee19550c4d2d19258cef274e2151f31ae9d38", + "sha256:75130df05aae7a7ac171b3b5b24714cffeabd054ad2ebc18870b3aa4526eba23", + "sha256:7b3661e6d4ba63a094138032c1356d557de5b3ea6fd3cca62a195f623e381c76", + "sha256:7d5c7e32f3ee42f77d8ff1a10384b5cdcc2d37035e2e3320ded909aa192d32c3", + "sha256:8124101e92c56827bebef084ff106e8ea11c743256149a95b9fd860d3a4f331f", + "sha256:81db2e7282cc0487f500d4db203edc57da81acde9e35f061d69ed983228ffe3b", + "sha256:840e18c38098221ea6201f091fc5d4de6128961d2930fbbc96806fb43f69aec1", + "sha256:89cc8921a4a5028d6dd388c399fcd2eef232e7040345af3d5b16c04b91cf3c7e", + "sha256:8b32cd4ab6db50c875001ba4f5a6b30c0f42151aa1fbf9c2e7e3674893fb1dc4", + "sha256:8df1c283e57c9cb4d271fdc1875f4a58a143a2d1698eb0d6b7c0d7d5f49c53a1", + "sha256:902cf4739458852fe917104365ec0efbea7d29a15e4276c96a8d33e6ed8ec137", + "sha256:97fbb77eaeb97591efdc654b8b5f3ccc066406ccfb3175b41382f221ecc216e8", + "sha256:9c7042488165f7251dc7894cd533a875d2875af6d3b0e09eda9c4b334627ad1c", + "sha256:9e318e6786b1e750a62f90c6f7fa8b542102bdcf97c7c4de2a48b50b61bd36ec", + "sha256:a9421b23c85f361a133aa7c5e8ec757668f70343f4ed8fdb5a4a14abd5437244", + "sha256:aaf71f95b21f9dc708123335df22e5a2fef6307e3e6f9ed773b2e0938cc4d491", + "sha256:afedc35fe4b9e30ab240b208bb9dc8938cb4afe9187589e8d8d085e1aacb8309", + "sha256:b5e28e56143750808c1c79c70a16519e9bc0a68b623197b96292b21b62d6055c", + "sha256:b82c9514c6d74b89a370c4060bdb80d2299bc6857e462e4a215b4ef7aa7b090e", + "sha256:b8f78398e67a7227aefa95f876481485403eb974b29e9dc38b307bb6eb2315ea", + "sha256:bbda75f245caecff8faa7e32ee94dfaa8312a3367397975527f29654cd17a6ed", + "sha256:bca34e913d27401bda2a6f390d0614049f5a95b3b11cd8eff80fe4ec340a1208", + "sha256:bd04d8cab16cab5b0a9ffc7d10f0779cf1120ab16c3925404428f74a0a43205a", + "sha256:c149a652aeac4902ecff2dd93c3b2681c608bd5208c793c4a99404b3e1afc87c", + "sha256:c2087dbb76a87ec2c619253e021e4fb20d1a72580feeaa6892b0b3d955175a71", + "sha256:c34f751bf67cab69638564eee34023909380ba3e0d8ee7f6fe473079bf93f09b", + "sha256:c6d20c8896c00775e6f62d8373aba32956aa0b850d02b5ec493f486c88e12859", + "sha256:c7af6f7b80f687b33a4cdb0a785a5d4de1fb027a44c9a049d8eb67d5bfe8a687", + "sha256:c7b07959866a6afb019abb9564d8a55046feb7a84506c74a6f197cbcdf8a208e", + "sha256:ca0dda0c5715efe2ab35bb83f813f681ebcd2840d8b1b92bfc6fe3ab382fae4a", + "sha256:cdb7eb3cf3deb3dd9e7b8749323b5d970052711f9e1e9f36364163627f96da58", + "sha256:ce757c7c90d35719b38fa3d4ca55654a76a40716ee299b0865f2de21c146801c", + "sha256:d1fa67ef839bad3815124f5f57e48cd50ff392f4911a9f3cf449d66fa3df62a5", + "sha256:d2dbd8f4990d4788cb122f63bf000357533f34860d269c1a8e90ae362090ff3a", + "sha256:d4ec0046facab83012d821b33cead742a35b54575c4edfb7ed7445f63441835f", + "sha256:dbceedcf4a9329cc665452db1aaf0845b85c666e4885b92ee0cddb1dbf7e052a", + "sha256:dc733d35f861f8d78abfaf54035461e10423422999b360966bf1c443cbc42705", + "sha256:dd635c2c4043222d80d80ca1ac4530a633102a9f2ad12252183bcf338c1b9474", + "sha256:de1f7cd5b6b351e1afd7568bdab94934d656abe273d66cda0ceea43bbc02a0c2", + "sha256:df7c841813f6265e636fe548a49664c77af31ddfa0085515326342a751a6ba51", + "sha256:e0f9d268b19e8f61bf42a1da48276bcd05f7ab5560311f541d22557f8227b866", + "sha256:e2d66eb41ffca6cc3c91d8387509d27ba73ad28371ef90255c50cb51f8953301", + "sha256:e429fc517a1c5e2a70d576077231538a98d59a45dfc552d1ac45a132844e6dfb", + "sha256:e4d2b88efe65544a7d5121b0c3b003ebba92bfede2ea3577ce548b69c5235185", + "sha256:e76c902d229a3aa9d5ceb813e1cbcc69bf5bda44c80d574ff1ac1fa3136dea71", + "sha256:ef07a0a1d254eeb16455d839cef6e8c2ed127f47f014bbda64a58b5482b6c836", + "sha256:f09529d2332264a902688031a83c19de8fda5eb5881e44233286b9c9ec91856d", + "sha256:f0a6d4a93d2a05daec7cb885157c97bbb0be4da739d6f9dfb02e101eb40921cd", + "sha256:f0cf2a0dbb5987da4bd92a7ca727eadb225581dd9681365beba9accbe5308f7d", + "sha256:f2671cb47e50a97f419a02cd1e0c339b31de017b033186358db92f4d8e2e17d8", + "sha256:f35b34a5184d5e0cc360b61664c1c06e866aab077b5a7c538a3e20c8fcdbf90b", + "sha256:f3d73022990ab0c8b172cce57c69fd9a89c24fd473a5e79cbce92df87e3d9c48", + "sha256:f5b8353ea1a4d7dfb59a7f45c04df66ecfd363bb5b35f33b11ea579111d4655f", + "sha256:f809a17cc78bd331e137caa25262b507225854073fd319e987bd216bed911b7c", + "sha256:f9bc4161bd3b970cd6a6fcda70583ad4afd10f2750609fb1f3ca9505050d4ef3", + "sha256:fdf4890cda3b59170009d012fca3294c00140e7f2abe1910e6a730809d0f3f9b" ], "markers": "python_version >= '3.8'", - "version": "==0.18.1" + "version": "==0.19.1" }, "s3transfer": { "hashes": [ @@ -1362,11 +1392,11 @@ }, "setuptools": { "hashes": [ - "sha256:937a48c7cdb7a21eb53cd7f9b59e525503aa8abaf3584c730dc5f7a5bec3a650", - "sha256:a58a8fde0541dab0419750bcc521fbdf8585f6e5cb41909df3a472ef7b81ca95" + "sha256:5a0d9c6a2f332881a0153f629d8000118efd33255cfa802757924c53312c76da", + "sha256:98b4d786a12fadd34eabf69e8d014b84e5fc655981e4ff419994700434ace132" ], "markers": "python_version >= '3.8'", - "version": "==70.1.1" + "version": "==72.0.0" }, "six": { "hashes": [ @@ -1609,11 +1639,11 @@ }, "certifi": { "hashes": [ - "sha256:3cd43f1c6fa7dedc5899d69d3ad0398fd018ad1a17fba83ddaf78aa46c747516", - "sha256:ddc6c8ce995e6987e7faf5e3f1b02b302836a0e5d98ece18392cb1a36c72ad56" + "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b", + "sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90" ], "markers": "python_version >= '3.6'", - "version": "==2024.6.2" + "version": "==2024.7.4" }, "cffi": { "hashes": [ @@ -1771,61 +1801,62 @@ }, "coverage": { "hashes": [ - "sha256:018a12985185038a5b2bcafab04ab833a9a0f2c59995b3cec07e10074c78635f", - "sha256:02ff6e898197cc1e9fa375581382b72498eb2e6d5fc0b53f03e496cfee3fac6d", - "sha256:042183de01f8b6d531e10c197f7f0315a61e8d805ab29c5f7b51a01d62782747", - "sha256:1014fbf665fef86cdfd6cb5b7371496ce35e4d2a00cda501cf9f5b9e6fced69f", - "sha256:1137f46adb28e3813dec8c01fefadcb8c614f33576f672962e323b5128d9a68d", - "sha256:16852febd96acd953b0d55fc842ce2dac1710f26729b31c80b940b9afcd9896f", - "sha256:2174e7c23e0a454ffe12267a10732c273243b4f2d50d07544a91198f05c48f47", - "sha256:2214ee920787d85db1b6a0bd9da5f8503ccc8fcd5814d90796c2f2493a2f4d2e", - "sha256:3257fdd8e574805f27bb5342b77bc65578e98cbc004a92232106344053f319ba", - "sha256:3684bc2ff328f935981847082ba4fdc950d58906a40eafa93510d1b54c08a66c", - "sha256:3a6612c99081d8d6134005b1354191e103ec9705d7ba2754e848211ac8cacc6b", - "sha256:3d7564cc09dd91b5a6001754a5b3c6ecc4aba6323baf33a12bd751036c998be4", - "sha256:44da56a2589b684813f86d07597fdf8a9c6ce77f58976727329272f5a01f99f7", - "sha256:5013ed890dc917cef2c9f765c4c6a8ae9df983cd60dbb635df8ed9f4ebc9f555", - "sha256:54317c2b806354cbb2dc7ac27e2b93f97096912cc16b18289c5d4e44fc663233", - "sha256:56b4eafa21c6c175b3ede004ca12c653a88b6f922494b023aeb1e836df953ace", - "sha256:581ea96f92bf71a5ec0974001f900db495488434a6928a2ca7f01eee20c23805", - "sha256:5cd64adedf3be66f8ccee418473c2916492d53cbafbfcff851cbec5a8454b136", - "sha256:5df54843b88901fdc2f598ac06737f03d71168fd1175728054c8f5a2739ac3e4", - "sha256:65e528e2e921ba8fd67d9055e6b9f9e34b21ebd6768ae1c1723f4ea6ace1234d", - "sha256:6aae5cce399a0f065da65c7bb1e8abd5c7a3043da9dceb429ebe1b289bc07806", - "sha256:6cfb5a4f556bb51aba274588200a46e4dd6b505fb1a5f8c5ae408222eb416f99", - "sha256:7076b4b3a5f6d2b5d7f1185fde25b1e54eb66e647a1dfef0e2c2bfaf9b4c88c8", - "sha256:73ca8fbc5bc622e54627314c1a6f1dfdd8db69788f3443e752c215f29fa87a0b", - "sha256:79b356f3dd5b26f3ad23b35c75dbdaf1f9e2450b6bcefc6d0825ea0aa3f86ca5", - "sha256:7a892be37ca35eb5019ec85402c3371b0f7cda5ab5056023a7f13da0961e60da", - "sha256:8192794d120167e2a64721d88dbd688584675e86e15d0569599257566dec9bf0", - "sha256:820bc841faa502e727a48311948e0461132a9c8baa42f6b2b84a29ced24cc078", - "sha256:8f894208794b164e6bd4bba61fc98bf6b06be4d390cf2daacfa6eca0a6d2bb4f", - "sha256:a04e990a2a41740b02d6182b498ee9796cf60eefe40cf859b016650147908029", - "sha256:a44963520b069e12789d0faea4e9fdb1e410cdc4aab89d94f7f55cbb7fef0353", - "sha256:a6bb74ed465d5fb204b2ec41d79bcd28afccf817de721e8a807d5141c3426638", - "sha256:ab73b35e8d109bffbda9a3e91c64e29fe26e03e49addf5b43d85fc426dde11f9", - "sha256:aea072a941b033813f5e4814541fc265a5c12ed9720daef11ca516aeacd3bd7f", - "sha256:b1ccf5e728ccf83acd313c89f07c22d70d6c375a9c6f339233dcf792094bcbf7", - "sha256:b385d49609f8e9efc885790a5a0e89f2e3ae042cdf12958b6034cc442de428d3", - "sha256:b3d45ff86efb129c599a3b287ae2e44c1e281ae0f9a9bad0edc202179bcc3a2e", - "sha256:b4a474f799456e0eb46d78ab07303286a84a3140e9700b9e154cfebc8f527016", - "sha256:b95c3a8cb0463ba9f77383d0fa8c9194cf91f64445a63fc26fb2327e1e1eb088", - "sha256:c5986ee7ea0795a4095ac4d113cbb3448601efca7f158ec7f7087a6c705304e4", - "sha256:cdd31315fc20868c194130de9ee6bfd99755cc9565edff98ecc12585b90be882", - "sha256:cef4649ec906ea7ea5e9e796e68b987f83fa9a718514fe147f538cfeda76d7a7", - "sha256:d05c16cf4b4c2fc880cb12ba4c9b526e9e5d5bb1d81313d4d732a5b9fe2b9d53", - "sha256:d2e344d6adc8ef81c5a233d3a57b3c7d5181f40e79e05e1c143da143ccb6377d", - "sha256:d45d3cbd94159c468b9b8c5a556e3f6b81a8d1af2a92b77320e887c3e7a5d080", - "sha256:db14f552ac38f10758ad14dd7b983dbab424e731588d300c7db25b6f89e335b5", - "sha256:dbc5958cb471e5a5af41b0ddaea96a37e74ed289535e8deca404811f6cb0bc3d", - "sha256:ddbd2f9713a79e8e7242d7c51f1929611e991d855f414ca9996c20e44a895f7c", - "sha256:e16f3d6b491c48c5ae726308e6ab1e18ee830b4cdd6913f2d7f77354b33f91c8", - "sha256:e2afe743289273209c992075a5a4913e8d007d569a406ffed0bd080ea02b0633", - "sha256:e564c2cf45d2f44a9da56f4e3a26b2236504a496eb4cb0ca7221cd4cc7a9aca9", - "sha256:ed550e7442f278af76d9d65af48069f1fb84c9f745ae249c1a183c1e9d1b025c" + "sha256:0086cd4fc71b7d485ac93ca4239c8f75732c2ae3ba83f6be1c9be59d9e2c6382", + "sha256:01c322ef2bbe15057bc4bf132b525b7e3f7206f071799eb8aa6ad1940bcf5fb1", + "sha256:03cafe82c1b32b770a29fd6de923625ccac3185a54a5e66606da26d105f37dac", + "sha256:044a0985a4f25b335882b0966625270a8d9db3d3409ddc49a4eb00b0ef5e8cee", + "sha256:07ed352205574aad067482e53dd606926afebcb5590653121063fbf4e2175166", + "sha256:0d1b923fc4a40c5832be4f35a5dab0e5ff89cddf83bb4174499e02ea089daf57", + "sha256:0e7b27d04131c46e6894f23a4ae186a6a2207209a05df5b6ad4caee6d54a222c", + "sha256:1fad32ee9b27350687035cb5fdf9145bc9cf0a094a9577d43e909948ebcfa27b", + "sha256:289cc803fa1dc901f84701ac10c9ee873619320f2f9aff38794db4a4a0268d51", + "sha256:3c59105f8d58ce500f348c5b56163a4113a440dad6daa2294b5052a10db866da", + "sha256:46c3d091059ad0b9c59d1034de74a7f36dcfa7f6d3bde782c49deb42438f2450", + "sha256:482855914928c8175735a2a59c8dc5806cf7d8f032e4820d52e845d1f731dca2", + "sha256:49c76cdfa13015c4560702574bad67f0e15ca5a2872c6a125f6327ead2b731dd", + "sha256:4b03741e70fb811d1a9a1d75355cf391f274ed85847f4b78e35459899f57af4d", + "sha256:4bea27c4269234e06f621f3fac3925f56ff34bc14521484b8f66a580aacc2e7d", + "sha256:4d5fae0a22dc86259dee66f2cc6c1d3e490c4a1214d7daa2a93d07491c5c04b6", + "sha256:543ef9179bc55edfd895154a51792b01c017c87af0ebaae092720152e19e42ca", + "sha256:54dece71673b3187c86226c3ca793c5f891f9fc3d8aa183f2e3653da18566169", + "sha256:6379688fb4cfa921ae349c76eb1a9ab26b65f32b03d46bb0eed841fd4cb6afb1", + "sha256:65fa405b837060db569a61ec368b74688f429b32fa47a8929a7a2f9b47183713", + "sha256:6616d1c9bf1e3faea78711ee42a8b972367d82ceae233ec0ac61cc7fec09fa6b", + "sha256:6fe885135c8a479d3e37a7aae61cbd3a0fb2deccb4dda3c25f92a49189f766d6", + "sha256:7221f9ac9dad9492cecab6f676b3eaf9185141539d5c9689d13fd6b0d7de840c", + "sha256:76d5f82213aa78098b9b964ea89de4617e70e0d43e97900c2778a50856dac605", + "sha256:7792f0ab20df8071d669d929c75c97fecfa6bcab82c10ee4adb91c7a54055463", + "sha256:831b476d79408ab6ccfadaaf199906c833f02fdb32c9ab907b1d4aa0713cfa3b", + "sha256:9146579352d7b5f6412735d0f203bbd8d00113a680b66565e205bc605ef81bc6", + "sha256:9cc44bf0315268e253bf563f3560e6c004efe38f76db03a1558274a6e04bf5d5", + "sha256:a73d18625f6a8a1cbb11eadc1d03929f9510f4131879288e3f7922097a429f63", + "sha256:a8659fd33ee9e6ca03950cfdcdf271d645cf681609153f218826dd9805ab585c", + "sha256:a94925102c89247530ae1dab7dc02c690942566f22e189cbd53579b0693c0783", + "sha256:ad4567d6c334c46046d1c4c20024de2a1c3abc626817ae21ae3da600f5779b44", + "sha256:b2e16f4cd2bc4d88ba30ca2d3bbf2f21f00f382cf4e1ce3b1ddc96c634bc48ca", + "sha256:bbdf9a72403110a3bdae77948b8011f644571311c2fb35ee15f0f10a8fc082e8", + "sha256:beb08e8508e53a568811016e59f3234d29c2583f6b6e28572f0954a6b4f7e03d", + "sha256:c4cbe651f3904e28f3a55d6f371203049034b4ddbce65a54527a3f189ca3b390", + "sha256:c7b525ab52ce18c57ae232ba6f7010297a87ced82a2383b1afd238849c1ff933", + "sha256:ca5d79cfdae420a1d52bf177de4bc2289c321d6c961ae321503b2ca59c17ae67", + "sha256:cdab02a0a941af190df8782aafc591ef3ad08824f97850b015c8c6a8b3877b0b", + "sha256:d17c6a415d68cfe1091d3296ba5749d3d8696e42c37fca5d4860c5bf7b729f03", + "sha256:d39bd10f0ae453554798b125d2f39884290c480f56e8a02ba7a6ed552005243b", + "sha256:d4b3cd1ca7cd73d229487fa5caca9e4bc1f0bca96526b922d61053ea751fe791", + "sha256:d50a252b23b9b4dfeefc1f663c568a221092cbaded20a05a11665d0dbec9b8fb", + "sha256:da8549d17489cd52f85a9829d0e1d91059359b3c54a26f28bec2c5d369524807", + "sha256:dcd070b5b585b50e6617e8972f3fbbee786afca71b1936ac06257f7e178f00f6", + "sha256:ddaaa91bfc4477d2871442bbf30a125e8fe6b05da8a0015507bfbf4718228ab2", + "sha256:df423f351b162a702c053d5dddc0fc0ef9a9e27ea3f449781ace5f906b664428", + "sha256:dff044f661f59dace805eedb4a7404c573b6ff0cdba4a524141bc63d7be5c7fd", + "sha256:e7e128f85c0b419907d1f38e616c4f1e9f1d1b37a7949f44df9a73d5da5cd53c", + "sha256:ed8d1d1821ba5fc88d4a4f45387b65de52382fa3ef1f0115a4f7a20cdfab0e94", + "sha256:f2501d60d7497fd55e391f423f965bbe9e650e9ffc3c627d5f0ac516026000b8", + "sha256:f7db0b6ae1f96ae41afe626095149ecd1b212b424626175a6633c2999eaad45b" ], "index": "pypi", - "version": "==7.5.4" + "markers": "python_version >= '3.8'", + "version": "==7.6.0" }, "docker": { "hashes": [ @@ -1871,6 +1902,7 @@ "sha256:b6e78b685dd4e9c016d7a4299cf1de69e299c88322e3f81c716e6e23fe5683c1" ], "index": "pypi", + "markers": "python_version >= '3.8' and python_version < '4'", "version": "==2.6.0" }, "packaging": { @@ -1937,11 +1969,12 @@ }, "pytest": { "hashes": [ - "sha256:c434598117762e2bd304e526244f67bf66bbd7b5d6cf22138be51ff661980343", - "sha256:de4bb8104e201939ccdc688b27a89a7be2079b22e2bd2b07f806b6ba71117977" + "sha256:4ba08f9ae7dcf84ded419494d229b48d0903ea6407b030eaec46df5e6a73bba5", + "sha256:c132345d12ce551242c87269de812483f5bcc87cdbb4722e48487ba194f9fdce" ], "index": "pypi", - "version": "==8.2.2" + "markers": "python_version >= '3.8'", + "version": "==8.3.2" }, "python-dateutil": { "hashes": [ @@ -1957,6 +1990,7 @@ "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6" ], "index": "pypi", + "markers": "python_version >= '3.8'", "version": "==2.32.3" }, "requests-mock": { @@ -1965,6 +1999,7 @@ "sha256:e9e12e333b525156e82a3c852f22016b9158220d2f47454de9cae8a77d371401" ], "index": "pypi", + "markers": "python_version >= '3.5'", "version": "==1.12.1" }, "six": { @@ -1987,6 +2022,7 @@ "sha256:54d330d085c0a11fc5da0b001af87aec4dd3e814104376bf7513e8646c77442a" ], "index": "pypi", + "markers": "python_version >= '3.7'", "version": "==0.0.1rc1" }, "testcontainers-opensearch": { @@ -1994,6 +2030,7 @@ "sha256:0bdf270b5b7f53915832f7c31dd2bd3ffdc20b534ea6b32231cc7003049bd0e1" ], "index": "pypi", + "markers": "python_version >= '3.7'", "version": "==0.0.1rc1" }, "typing-extensions": { diff --git a/dbrepo-analyse-service/app.py b/dbrepo-analyse-service/app.py index d19b721a33..bbce751508 100644 --- a/dbrepo-analyse-service/app.py +++ b/dbrepo-analyse-service/app.py @@ -254,10 +254,10 @@ def analyse_datatypes(): return Response(res, mimetype="application/json"), 202 except OSError as e: logging.error(f"Failed to determine data types: {e}") - return ApiError(status='BAD_REQUEST', message=str(e), code='analyse.csv.invalid'), 400 + return ApiError(status='BAD_REQUEST', message=str(e), code='error.analyse.invalid').model_dump_json(), 400 except ClientError as e: logging.error(f"Failed to determine separator: {e}") - return ApiError(status='NOT_FOUND', message='Failed to find csv', code='analyse.csv.missing'), 404 + return ApiError(status='NOT_FOUND', message='Failed to find csv', code='error.analyse.missing').model_dump_json(), 404 @app.route("/api/analyse/keys", methods=["GET"], endpoint="analyse_analyse_keys") @@ -269,7 +269,7 @@ def analyse_keys(): logging.debug(f"Analyse keys from filename '{filename}' with separator {separator}") if filename is None or separator is None: return ApiError(status='BAD_REQUEST', message="Missing required query parameters 'filename' and 'separator'", - code='analyse.csv.invalid'), 400 + code='analyse.csv.invalid').model_dump_json(), 400 try: res = { 'keys': determine_pk(filename, separator) @@ -278,4 +278,4 @@ def analyse_keys(): return Response(dumps(res), mimetype="application/json"), 202 except OSError as e: logging.error(f"Failed to determine primary key: {e}") - return ApiError(status='BAD_REQUEST', message=str(e), code='analyse.database.invalid'), 400 + return ApiError(status='BAD_REQUEST', message=str(e), code='analyse.database.invalid').model_dump_json(), 400 diff --git a/dbrepo-analyse-service/determine_dt.py b/dbrepo-analyse-service/determine_dt.py index 3fcfc73e94..6a22401866 100644 --- a/dbrepo-analyse-service/determine_dt.py +++ b/dbrepo-analyse-service/determine_dt.py @@ -9,11 +9,12 @@ import pandas from numpy import dtype, max, min from flask import current_app +from pandas.errors import EmptyDataError from clients.s3_client import S3Client -def determine_datatypes(filename, enum=False, enum_tol=0.0001, separator=None) -> {}: +def determine_datatypes(filename, enum=False, enum_tol=0.0001, separator=',') -> {}: # Use option enum=True for searching Postgres ENUM Types in CSV file. Remark # Enum is not SQL standard, hence, it might not be supported by all db-engines. # However, it can be used in Postgres and MySQL. @@ -35,10 +36,22 @@ def determine_datatypes(filename, enum=False, enum_tol=0.0001, separator=None) - line_terminator = "\r" elif b"\r\n" in line: line_terminator = "\r\n" - logging.info("Analysing corpus with separator: %s", separator) + logging.info(f"Analysing corpus with separator: {separator}") # index_col=False -> prevent shared index & count length correct - df = pandas.read_csv(fh, delimiter=separator, nrows=100, lineterminator=line_terminator, index_col=False) + df = None + for encoding in ['utf-8', 'cp1252', 'latin1', 'iso-8859-1']: + try: + logging.debug(f"attempt parsing .csv using encoding {encoding}") + df = pandas.read_csv(fh, delimiter=separator, nrows=100, lineterminator=line_terminator, + index_col=False, encoding=encoding) + logging.debug(f"parsing .csv using encoding {encoding} was successful") + break + except (UnicodeDecodeError, EmptyDataError) as error: + logging.warning(f"Failed to parse .csv using encoding {encoding}: {error}") + if df is None: + raise IOError( + f"Failed to parse .csv: no supported encoding found (one of: utf-8, cp1252, latin1, iso-8859-1)") if b"," in line: separator = "," @@ -51,31 +64,44 @@ def determine_datatypes(filename, enum=False, enum_tol=0.0001, separator=None) - for name, dataType in df.dtypes.items(): if dataType == dtype('float64'): - r[name] = 'decimal' + if pandas.to_numeric(df[name], errors='coerce').notnull().all(): + logging.debug(f"mapped column {name} from float64 to decimal") + r[name] = 'decimal' + else: + logging.debug(f"mapped column {name} from float64 to text") + r[name] = 'text' elif dataType == dtype('int64'): min_val = min(df[name]) max_val = max(df[name]) if 0 <= min_val <= 1 and 0 <= max_val <= 1: + logging.debug(f"mapped column {name} from int64 to bool") r[name] = 'bool' continue + logging.debug(f"mapped column {name} from int64 to bigint") r[name] = 'bigint' elif dataType == dtype('O'): try: pandas.to_datetime(df[name], format='mixed') + logging.debug(f"mapped column {name} from O to timestamp") r[name] = 'timestamp' continue except ValueError: pass max_size = max(df[name].astype(str).map(len)) if max_size <= 1: + logging.debug(f"mapped column {name} from O to char") r[name] = 'char' if 0 <= max_size <= 255: + logging.debug(f"mapped column {name} from O to varchar") r[name] = 'varchar' else: + logging.debug(f"mapped column {name} from O to text") r[name] = 'text' elif dataType == dtype('bool'): + logging.debug(f"mapped column {name} from bool to bool") r[name] = 'bool' elif dataType == dtype('datetime64'): + logging.debug(f"mapped column {name} from datetime64 to datetime") r[name] = 'datetime' else: logging.warning(f'default to \'text\' for column {name} and type {dtype}') diff --git a/dbrepo-analyse-service/determine_pk.py b/dbrepo-analyse-service/determine_pk.py index 0e3a66ad19..141d90b78e 100644 --- a/dbrepo-analyse-service/determine_pk.py +++ b/dbrepo-analyse-service/determine_pk.py @@ -8,7 +8,7 @@ from determine_dt import determine_datatypes from clients.s3_client import S3Client -def determine_pk(filename, separator=","): +def determine_pk(filename: str, separator: str = ','): dt = json.loads(determine_datatypes(filename=filename, separator=separator)) dt = {k.lower(): v for k, v in dt["columns"].items()} # {k.lower(): v for k, v in dt['columns'].items() if v != 'Numeric'} diff --git a/dbrepo-analyse-service/lib/dbrepo-1.4.4-py3-none-any.whl b/dbrepo-analyse-service/lib/dbrepo-1.4.4-py3-none-any.whl deleted file mode 100644 index 617969c3eb15926d932b7c0180bed51b9ef7052d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 29907 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 diff --git a/dbrepo-analyse-service/lib/dbrepo-1.4.4.tar.gz b/dbrepo-analyse-service/lib/dbrepo-1.4.4.tar.gz deleted file mode 100644 index 9d1d5ae238baba6bc51db4d219a0d09b5aca1c51..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 39490 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�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 diff --git a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/SubsetEndpoint.java b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/SubsetEndpoint.java index f4626deb93..8f5f4a8214 100644 --- a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/SubsetEndpoint.java +++ b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/SubsetEndpoint.java @@ -261,12 +261,12 @@ public class SubsetEndpoint { QueryNotFoundException, StorageUnavailableException, QueryMalformedException, SidecarExportException, StorageNotFoundException, QueryStoreInsertException, TableMalformedException, PaginationException, QueryNotSupportedException, NotAllowedException, UserNotFoundException, MetadataServiceException { - log.debug("endpoint create subset in database, databaseId={}, data.statement={}, principal.name={}, page={}, " + - "size={}, timestamp={}", databaseId, data.getStatement(), principal.getName(), page, size, timestamp); + log.debug("endpoint create subset in database, databaseId={}, data.statement={}, principal.name={}, " + + "page={}, size={}, timestamp={}", databaseId, data.getStatement(), principal.getName(), page, size, + timestamp); /* check */ endpointValidator.validateDataParams(page, size); endpointValidator.validateForbiddenStatements(data.getStatement()); - metadataServiceGateway.getAccess(databaseId, UserUtil.getId(principal)); /* parameters */ if (page == null) { page = 0L; diff --git a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java index 7e95c7cfa9..f4d31bbc21 100644 --- a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java +++ b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java @@ -646,7 +646,7 @@ public class TableEndpoint { mediaType = "application/json", schema = @Schema(implementation = ApiErrorDto.class))}), @ApiResponse(responseCode = "404", - description = "Failed to find table in metadata database", + description = "Failed to find table or database in metadata database", content = {@Content( mediaType = "application/json", schema = @Schema(implementation = ApiErrorDto.class))}), @@ -659,9 +659,10 @@ public class TableEndpoint { public ResponseEntity<TableStatisticDto> statistic(@NotBlank @PathVariable("databaseId") Long databaseId, @NotBlank @PathVariable("tableId") Long tableId) throws DatabaseUnavailableException, RemoteUnavailableException, TableNotFoundException, - MetadataServiceException, TableMalformedException, QueryMalformedException { + MetadataServiceException, TableMalformedException, DatabaseNotFoundException { log.debug("endpoint generate table statistic, databaseId={}, tableId={}", databaseId, tableId); final PrivilegedTableDto table = metadataServiceGateway.getTableById(databaseId, tableId); + table.setDatabase(metadataServiceGateway.getDatabaseById(databaseId)); try { final TableStatisticDto dto = tableService.getStatistics(table); return ResponseEntity.ok(dto); diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/SubsetEndpointUnitTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/SubsetEndpointUnitTest.java index c45454b8f3..d554667467 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/SubsetEndpointUnitTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/SubsetEndpointUnitTest.java @@ -167,7 +167,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { @Test @WithMockUser(username = USER_1_USERNAME, authorities = {"execute-query"}) - public void create_succeeds() throws UserNotFoundException, QueryStoreInsertException, TableMalformedException, + public void create_noAccess_succeeds() throws UserNotFoundException, QueryStoreInsertException, TableMalformedException, NotAllowedException, SidecarExportException, QueryNotSupportedException, PaginationException, StorageNotFoundException, DatabaseUnavailableException, StorageUnavailableException, QueryMalformedException, QueryNotFoundException, DatabaseNotFoundException, RemoteUnavailableException, @@ -177,8 +177,6 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { .build(); /* mock */ - when(metadataServiceGateway.getAccess(DATABASE_3_ID, USER_1_ID)) - .thenReturn(DATABASE_3_USER_1_READ_ACCESS_DTO); when(metadataServiceGateway.getDatabaseById(DATABASE_3_ID)) .thenReturn(DATABASE_3_PRIVILEGED_DTO); when(queryService.execute(eq(DATABASE_3_PRIVILEGED_DTO), anyString(), any(Instant.class), eq(USER_1_ID), eq(0L), eq(10L), eq(null), eq(null))) @@ -213,8 +211,6 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { .build(); /* mock */ - when(metadataServiceGateway.getAccess(DATABASE_3_ID, USER_1_ID)) - .thenReturn(DATABASE_3_USER_1_READ_ACCESS_DTO); when(metadataServiceGateway.getDatabaseById(DATABASE_3_ID)) .thenReturn(DATABASE_3_PRIVILEGED_DTO); when(queryService.execute(eq(DATABASE_3_PRIVILEGED_DTO), anyString(), any(Instant.class), eq(USER_1_ID), eq(0L), eq(10L), eq(null), eq(null))) @@ -226,15 +222,13 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { @Test @WithMockUser(username = USER_1_USERNAME, authorities = {"execute-query"}) - public void create_databaseNotFound_fails() throws NotAllowedException, RemoteUnavailableException, + public void create_databaseNotFound_fails() throws RemoteUnavailableException, DatabaseNotFoundException, MetadataServiceException { final ExecuteStatementDto request = ExecuteStatementDto.builder() .statement(QUERY_5_STATEMENT) .build(); /* mock */ - when(metadataServiceGateway.getAccess(DATABASE_3_ID, USER_1_ID)) - .thenReturn(DATABASE_3_USER_1_READ_ACCESS_DTO); doThrow(DatabaseNotFoundException.class) .when(metadataServiceGateway) .getDatabaseById(DATABASE_3_ID); @@ -258,24 +252,6 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { }); } - @Test - @WithMockUser(username = USER_4_USERNAME, authorities = {"execute-query"}) - public void create_noAccess_fails() throws NotAllowedException, RemoteUnavailableException, MetadataServiceException { - final ExecuteStatementDto request = ExecuteStatementDto.builder() - .statement(QUERY_5_STATEMENT) - .build(); - - /* mock */ - doThrow(NotAllowedException.class) - .when(metadataServiceGateway) - .getAccess(DATABASE_3_ID, USER_4_ID); - - /* test */ - assertThrows(NotAllowedException.class, () -> { - subsetEndpoint.create(DATABASE_3_ID, request, USER_4_PRINCIPAL, null, null, null); - }); - } - @Test public void getData_succeeds() throws DatabaseNotFoundException, RemoteUnavailableException, UserNotFoundException, NotAllowedException, SQLException, QueryNotFoundException, TableMalformedException, QueryMalformedException, diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/TableServiceIntegrationTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/TableServiceIntegrationTest.java index 65ec32c0d5..41fa4963fb 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/TableServiceIntegrationTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/TableServiceIntegrationTest.java @@ -451,7 +451,7 @@ public class TableServiceIntegrationTest extends AbstractUnitTest { } @Test - public void getStatistics_succeeds() throws TableMalformedException, SQLException, QueryMalformedException { + public void getStatistics_succeeds() throws TableMalformedException, SQLException, TableNotFoundException { /* test */ final TableStatisticDto response = tableService.getStatistics(TABLE_1_PRIVILEGED_DTO); @@ -493,13 +493,8 @@ public class TableServiceIntegrationTest extends AbstractUnitTest { @Test public void create_malformed_fails() { final at.tuwien.api.database.table.internal.TableCreateDto request = TableCreateDto.builder() - .needSequence(false) .name("missing_foreign_key") - .columns(List.of(ColumnCreateDto.builder() - .name("id") - .type(ColumnTypeDto.BIGINT) - .nullAllowed(false) - .build())) + .columns(List.of()) .constraints(ConstraintsCreateDto.builder() .foreignKeys(List.of(ForeignKeyCreateDto.builder() .columns(List.of("i_do_not_exist")) diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/gateway/impl/KeycloakGatewayImpl.java b/dbrepo-data-service/services/src/main/java/at/tuwien/gateway/impl/KeycloakGatewayImpl.java index f95bb3fe76..6e233395d9 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/gateway/impl/KeycloakGatewayImpl.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/gateway/impl/KeycloakGatewayImpl.java @@ -41,9 +41,17 @@ public class KeycloakGatewayImpl implements KeycloakGateway { final String url = keycloakConfig.getKeycloakEndpoint() + "/realms/dbrepo/protocol/openid-connect/token"; log.trace("request user token from url: {}", url); log.trace("request username: {}", username); - log.trace("request password: {}", password != null ? "(set)" : "(not set)"); + if (password.isEmpty() || password.isBlank()) { + log.warn("request password: (empty)"); + } else { + log.trace("request password: (set)"); + } log.trace("request client_id: {}", keycloakConfig.getKeycloakClient()); - log.trace("request client_secret: {}", keycloakConfig.getKeycloakClientSecret()); + if (keycloakConfig.getKeycloakClientSecret().isEmpty() || keycloakConfig.getKeycloakClientSecret().isBlank()) { + log.warn("request client_secret: (empty)"); + } else { + log.trace("request client_secret: (set)"); + } final ResponseEntity<TokenDto> response; try { response = new RestTemplate() diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/gateway/impl/MetadataServiceGatewayImpl.java b/dbrepo-data-service/services/src/main/java/at/tuwien/gateway/impl/MetadataServiceGatewayImpl.java index a3fda6482c..06641b738b 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/gateway/impl/MetadataServiceGatewayImpl.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/gateway/impl/MetadataServiceGatewayImpl.java @@ -313,6 +313,7 @@ public class MetadataServiceGatewayImpl implements MetadataServiceGateway { RemoteUnavailableException { final ResponseEntity<Void> response; final String url = "/api/database/" + databaseId + "/table/" + tableId; + log.trace("mapped url: {}", url); try { response = restTemplate.exchange(url, HttpMethod.PUT, HttpEntity.EMPTY, Void.class); } catch (ResourceAccessException | HttpServerErrorException e) { diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/mapper/MariaDbMapper.java b/dbrepo-data-service/services/src/main/java/at/tuwien/mapper/MariaDbMapper.java index b6b782a04c..b2ed933049 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/mapper/MariaDbMapper.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/mapper/MariaDbMapper.java @@ -234,9 +234,7 @@ public interface MariaDbMapper { /* data type */ .append(columnTypeDtoToDataType(column)) /* null expressions */ - .append(column.getNullAllowed() != null && column.getNullAllowed() ? " NULL" : " NOT NULL") - /* default expressions */ - .append(data.getNeedSequence() && column.getName().equals("id") ? " DEFAULT NEXTVAL(`" + tableCreateDtoToSequenceName(data) + "`)" : ""); + .append(column.getNullAllowed() != null && column.getNullAllowed() ? " NULL" : " NOT NULL"); if (column.getDescription() != null && !column.getDescription().isEmpty()) { /* comments */ stringBuilder.append(" COMMENT \"") @@ -565,10 +563,10 @@ public interface MariaDbMapper { final int[] jdx = new int[]{0}; data.getKeys() .forEach((key, value) -> { - statement.append(jdx[0] == 0 ? "" : ", ") + statement.append(jdx[0] == 0 ? "" : " AND ") .append("`") .append(key) - .append("` "); + .append("`"); if (value == null) { statement.append(" IS NULL"); } else { diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/service/TableService.java b/dbrepo-data-service/services/src/main/java/at/tuwien/service/TableService.java index 50894eb77a..765a3b7e2e 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/service/TableService.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/service/TableService.java @@ -32,10 +32,10 @@ public interface TableService { * @return The table statistic, if successful. * @throws SQLException Failed to parse SQL query, contains invalid syntax. * @throws TableMalformedException The table statistic generation was unsuccessful, likely due to a bug in the mapping. - * @throws QueryMalformedException The inspection query is malformed. + * @throws TableNotFoundException The table could not be inspected in the data database. */ TableStatisticDto getStatistics(PrivilegedTableDto table) throws SQLException, TableMalformedException, - QueryMalformedException; + TableNotFoundException; /** * Finds a table with given data database and table name. diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/TableServiceMariaDbImpl.java b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/TableServiceMariaDbImpl.java index e913c0cb82..4dacc1e094 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/TableServiceMariaDbImpl.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/TableServiceMariaDbImpl.java @@ -86,7 +86,7 @@ public class TableServiceMariaDbImpl extends HibernateConnector implements Table @Override public TableStatisticDto getStatistics(PrivilegedTableDto table) throws SQLException, TableMalformedException, - QueryMalformedException { + TableNotFoundException { final ComboPooledDataSource dataSource = getPrivilegedDataSource(table.getDatabase()); final Connection connection = dataSource.getConnection(); final TableStatisticDto statistic; @@ -95,7 +95,11 @@ public class TableServiceMariaDbImpl extends HibernateConnector implements Table final ResultSet resultSet = connection.prepareStatement(mariaDbMapper.tableColumnStatisticsSelectRawQuery(table.getColumns(), table.getInternalName())) .executeQuery(); statistic = dataMapper.resultSetToTableStatistic(resultSet); - statistic.setRows(getCount(table, null)); + final TableDto tmpTable = schemaService.inspectTable(table.getDatabase(), table.getInternalName()); + statistic.setAvgRowLength(tmpTable.getAvgRowLength()); + statistic.setDataLength(tmpTable.getDataLength()); + statistic.setMaxDataLength(tmpTable.getMaxDataLength()); + statistic.setRows(tmpTable.getNumRows()); } catch (SQLException e) { connection.rollback(); log.error("Failed to obtain column statistics: {}", e.getMessage()); @@ -107,7 +111,8 @@ public class TableServiceMariaDbImpl extends HibernateConnector implements Table .stream() .filter(column -> !MariaDbUtil.numericDataTypes.contains(column.getColumnType())) .forEach(column -> statistic.getColumns().put(column.getInternalName(), new ColumnStatisticDto())); - log.info("Obtained column statistics for table: {}", table.getInternalName()); + log.info("Obtained statistics for the table and {} column(s)", statistic.getColumns().size()); + log.trace("obtained statistics: {}", statistic); return statistic; } @@ -123,12 +128,6 @@ public class TableServiceMariaDbImpl extends HibernateConnector implements Table final ComboPooledDataSource dataSource = getPrivilegedDataSource(database); final Connection connection = dataSource.getConnection(); try { - if (data.getNeedSequence()) { - /* create table sequence if not exists */ - connection.prepareStatement(mariaDbMapper.tableCreateDtoToCreateSequenceRawQuery(data)) - .execute(); - log.info("Created sequence as primary key"); - } /* create table if not exists */ connection.prepareStatement(mariaDbMapper.tableCreateDtoToCreateTableRawQuery(data)) .execute(); diff --git a/dbrepo-metadata-db/setup-schema.sql b/dbrepo-metadata-db/1_setup-schema.sql similarity index 100% rename from dbrepo-metadata-db/setup-schema.sql rename to dbrepo-metadata-db/1_setup-schema.sql diff --git a/dbrepo-metadata-db/setup-data.sql b/dbrepo-metadata-db/2_setup-data.sql similarity index 100% rename from dbrepo-metadata-db/setup-data.sql rename to dbrepo-metadata-db/2_setup-data.sql diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TableCreateDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TableCreateDto.java index 312ecaf2ac..e2ef252708 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TableCreateDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TableCreateDto.java @@ -27,9 +27,6 @@ public class TableCreateDto { @Schema(example = "Air Quality") private String name; - @JsonProperty("need_sequence") - private transient boolean needSequence; - @Size(max = 180) @Schema(example = "Air Quality in Austria") private String description; diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TableStatisticDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TableStatisticDto.java index 8d41bcc7ff..8862723b9c 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TableStatisticDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/TableStatisticDto.java @@ -2,6 +2,7 @@ package at.tuwien.api.database.table; import at.tuwien.api.database.table.columns.ColumnStatisticDto; import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotNull; import lombok.*; import lombok.extern.jackson.Jacksonized; @@ -17,10 +18,23 @@ import java.util.Map; @ToString public class TableStatisticDto { - @NotNull @JsonProperty("rows") + @Schema(example = "5") private Long rows; + @JsonProperty("data_length") + @Schema(example = "16384", description = "in bytes") + private Long dataLength; + + @JsonProperty("max_data_length") + @Schema(example = "0", description = "in bytes") + private Long maxDataLength; + + @JsonProperty("avg_row_length") + @Schema(example = "3276", description = "in bytes") + private Long avgRowLength; + @NotNull + @ToString.Exclude private Map<String, ColumnStatisticDto> columns; } diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/internal/TableCreateDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/internal/TableCreateDto.java index 9e92a46c48..0db45a9f1b 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/internal/TableCreateDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/internal/TableCreateDto.java @@ -2,7 +2,6 @@ package at.tuwien.api.database.table.internal; import at.tuwien.api.database.table.columns.ColumnCreateDto; import at.tuwien.api.database.table.constraints.ConstraintsCreateDto; -import com.fasterxml.jackson.annotation.JsonProperty; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; @@ -26,10 +25,6 @@ public class TableCreateDto { @Schema(example = "Air Quality") private String name; - @NotNull - @JsonProperty("need_sequence") - private Boolean needSequence; - @Size(max = 180) @Schema(example = "Air Quality in Austria") private String description; diff --git a/dbrepo-metadata-service/entities/src/main/java/at/tuwien/entities/database/Database.java b/dbrepo-metadata-service/entities/src/main/java/at/tuwien/entities/database/Database.java index f463370e4d..2e154b8697 100644 --- a/dbrepo-metadata-service/entities/src/main/java/at/tuwien/entities/database/Database.java +++ b/dbrepo-metadata-service/entities/src/main/java/at/tuwien/entities/database/Database.java @@ -117,6 +117,7 @@ public class Database implements Serializable { private List<Identifier> subsets; @ToString.Exclude + @OrderBy("id DESC") @OneToMany(fetch = FetchType.LAZY, cascade = {CascadeType.ALL, CascadeType.PERSIST}, mappedBy = "database", orphanRemoval = true) private List<Table> tables; diff --git a/dbrepo-metadata-service/entities/src/main/java/at/tuwien/entities/database/table/Table.java b/dbrepo-metadata-service/entities/src/main/java/at/tuwien/entities/database/table/Table.java index 57d9dbab1b..9a402201ea 100644 --- a/dbrepo-metadata-service/entities/src/main/java/at/tuwien/entities/database/table/Table.java +++ b/dbrepo-metadata-service/entities/src/main/java/at/tuwien/entities/database/table/Table.java @@ -88,6 +88,7 @@ public class Table { }) private Database database; + @ToString.Exclude @OnDelete(action = OnDeleteAction.CASCADE) @OneToMany(fetch = FetchType.LAZY, cascade = {CascadeType.MERGE, CascadeType.PERSIST}, mappedBy = "table") @OrderBy("ordinalPosition") @@ -104,6 +105,7 @@ public class Table { @OrderBy("id DESC") private List<Identifier> identifiers; + @ToString.Exclude @Embedded private Constraints constraints; diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/TableServiceUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/TableServiceUnitTest.java index 33b4594424..59419f9bad 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/TableServiceUnitTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/TableServiceUnitTest.java @@ -172,29 +172,21 @@ public class TableServiceUnitTest extends AbstractUnitTest { final Table response = tableService.createTable(DATABASE_1, request, USER_1_PRINCIPAL); assertEquals("New Table", response.getName()); assertEquals("new_table", response.getInternalName()); - assertEquals(2, response.getColumns().size()); + assertEquals(1, response.getColumns().size()); /* columns */ final TableColumn column0 = response.getColumns().get(0); - assertEquals("id", column0.getName()); - assertEquals("id", column0.getInternalName()); - assertEquals(TableColumnType.BIGINT, column0.getColumnType()); - assertFalse(column0.getIsNullAllowed()); - assertTrue(column0.getAutoGenerated()); - final TableColumn column1 = response.getColumns().get(1); - assertEquals("I Am Späshül", column1.getName()); - assertEquals("i_am_spa_shu_l", column1.getInternalName()); - assertEquals(TableColumnType.TEXT, column1.getColumnType()); - assertTrue(column1.getIsNullAllowed()); - assertFalse(column1.getAutoGenerated()); + assertEquals("I Am Späshül", column0.getName()); + assertEquals("i_am_spa_shu_l", column0.getInternalName()); + assertEquals(TableColumnType.TEXT, column0.getColumnType()); + assertTrue(column0.getIsNullAllowed()); + assertFalse(column0.getAutoGenerated()); /* constraints */ final Constraints constraints = response.getConstraints(); - assertEquals(1, constraints.getPrimaryKey().size()); - assertEquals(column0.getName(), constraints.getPrimaryKey().get(0).getColumn().getName()); - assertEquals(column0.getInternalName(), constraints.getPrimaryKey().get(0).getColumn().getInternalName()); + assertEquals(0, constraints.getPrimaryKey().size()); assertEquals(1, constraints.getUniques().get(0).getColumns().size()); assertNotNull(constraints.getUniques().get(0).getName()); - assertEquals(column1.getName(), constraints.getUniques().get(0).getColumns().get(0).getName()); - assertEquals(column1.getInternalName(), constraints.getUniques().get(0).getColumns().get(0).getInternalName()); + assertEquals(column0.getName(), constraints.getUniques().get(0).getColumns().get(0).getName()); + assertEquals(column0.getInternalName(), constraints.getUniques().get(0).getColumns().get(0).getInternalName()); assertEquals(0, constraints.getChecks().size()); assertEquals(0, constraints.getForeignKeys().size()); } diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/auth/InternalRequestInterceptor.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/auth/InternalRequestInterceptor.java index 5a7288cf55..835b7245d1 100644 --- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/auth/InternalRequestInterceptor.java +++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/auth/InternalRequestInterceptor.java @@ -38,7 +38,6 @@ public class InternalRequestInterceptor implements ClientHttpRequestInterceptor gatewayConfig.getSystemPassword()); headers.setBearerAuth(token.getAccessToken()); log.trace("set bearer token for internal user: {}", gatewayConfig.getSystemUsername()); - log.trace("set access token: {}", token.getAccessToken()); return execution.execute(request, body); } catch (AuthServiceConnectionException | CredentialsInvalidException | AccountNotSetupException e) { log.error("Failed to obtain token for internal user: {}", gatewayConfig.getSystemUsername()); diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/TableServiceImpl.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/TableServiceImpl.java index 46a0602c74..7ca855974e 100644 --- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/TableServiceImpl.java +++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/TableServiceImpl.java @@ -100,24 +100,6 @@ public class TableServiceImpl implements TableService { TableExistsException, SearchServiceException, SearchServiceConnectionException, MalformedException, OntologyNotFoundException, SemanticEntityNotFoundException { final User owner = userService.findByUsername(principal.getName()); - /* check */ - if (data.getConstraints().getPrimaryKey().isEmpty()) { - final List<ColumnCreateDto> columns = new LinkedList<>(); - columns.add(ColumnCreateDto.builder() - .name("id") - .type(ColumnTypeDto.BIGINT) - .nullAllowed(false) - .build()); - columns.addAll(data.getColumns()); - data.setNeedSequence(true); - data.setColumns(columns); - data.getConstraints() - .setPrimaryKey(Set.of("id")); - log.debug("no primary key provided: generate primary key column with sequence"); - } else { - log.trace("primary key provided: no column with sequence needed"); - data.setNeedSequence(false); - } /* map table */ final Table table = Table.builder() .isVersioned(true) @@ -144,9 +126,6 @@ public class TableServiceImpl implements TableService { final TableColumn column = metadataMapper.columnCreateDtoToTableColumn(c, database.getContainer().getImage()); column.setOrdinalPosition(idx[0]++); column.setTable(table); - if (data.isNeedSequence() && column.getName().equals("id")) { - column.setAutoGenerated(true); - } if (c.getUnitUri() != null) { log.trace("column {} has assigned unit uri: {}", column.getInternalName(), c.getUnitUri()); TableColumnUnit unit; @@ -292,6 +271,9 @@ public class TableServiceImpl implements TableService { DataServiceException, DataServiceConnectionException { final TableStatisticDto statistic = dataServiceGateway.getTableStatistics(table.getTdbid(), table.getId()); table.setNumRows(statistic.getRows()); + table.setDataLength(statistic.getDataLength()); + table.setAvgRowLength(statistic.getAvgRowLength()); + table.setMaxDataLength(statistic.getMaxDataLength()); for (Map.Entry<String, ColumnStatisticDto> entry : statistic.getColumns().entrySet()) { final Optional<TableColumn> optional = table.getColumns().stream().filter(c -> c.getInternalName().equals(entry.getKey())).findFirst(); if (optional.isEmpty()) { @@ -313,7 +295,8 @@ public class TableServiceImpl implements TableService { databaseRepository.save(database); /* update in open search service */ searchServiceGateway.update(database); - log.info("Updated statistics of table with id: {}", table.getId()); + log.info("Updated statistics for the table and {} column(s)", table.getColumns().size()); + log.trace("updated statistics: {}", table); } } diff --git a/dbrepo-metadata-service/test/src/main/java/at/tuwien/test/BaseTest.java b/dbrepo-metadata-service/test/src/main/java/at/tuwien/test/BaseTest.java index 173a3ba9f4..0341a7a5df 100644 --- a/dbrepo-metadata-service/test/src/main/java/at/tuwien/test/BaseTest.java +++ b/dbrepo-metadata-service/test/src/main/java/at/tuwien/test/BaseTest.java @@ -1842,11 +1842,11 @@ public abstract class BaseTest { .checks(new LinkedHashSet<>()) .primaryKey(new LinkedHashSet<>()) .foreignKeys(new LinkedList<>()) - .uniques(List.of(List.of("id"))) + .uniques(new LinkedList<>()) .build(); public final static ConstraintsCreateDto TABLE_3_CONSTRAINTS_INVALID_CREATE_DTO = ConstraintsCreateDto.builder() - .uniques(List.of(List.of("id"))) + .uniques(new LinkedList<>()) .foreignKeys(List.of(ForeignKeyCreateDto.builder() .referencedTable("weather_location") .columns(List.of("fahrzeug")) @@ -2186,7 +2186,6 @@ public abstract class BaseTest { .description(TABLE_4_DESCRIPTION) .columns(TABLE_4_COLUMNS_CREATE_DTO) .constraints(TABLE_4_CONSTRAINTS_CREATE_DTO) - .needSequence(false) .build(); public final static List<ColumnDto> TABLE_4_COLUMNS_DTO = List.of(ColumnDto.builder() @@ -2889,7 +2888,6 @@ public abstract class BaseTest { .description(TABLE_1_DESCRIPTION) .columns(TABLE_1_COLUMNS_CREATE_DTO) .constraints(TABLE_1_CONSTRAINTS_CREATE_DTO) - .needSequence(true) .build(); public final static List<TableColumn> TABLE_2_COLUMNS = List.of(TableColumn.builder() diff --git a/dbrepo-search-service/init/app.py b/dbrepo-search-service/init/app.py index 4c8bde0bf9..fccbd92fea 100644 --- a/dbrepo-search-service/init/app.py +++ b/dbrepo-search-service/init/app.py @@ -2,7 +2,6 @@ import json import os import logging from typing import List -from flask import current_app import opensearchpy.exceptions from dbrepo.RestClient import RestClient @@ -49,11 +48,11 @@ class App: search_instance: OpenSearch = None def __init__(self): - self.metadata_service_endpoint = current_app.config["METADATA_SERVICE_ENDPOINT"] - self.search_host = current_app.config["OPENSEARCH_HOST"] - self.search_port = int(current_app.config["OPENSEARCH_PORT"]) - self.search_username = current_app.config["OPENSEARCH_USERNAME"] - self.search_password = current_app.config["OPENSEARCH_PASSWORD"] + self.metadata_service_endpoint = os.getenv("METADATA_SERVICE_ENDPOINT") + self.search_host = os.getenv("OPENSEARCH_HOST") + self.search_port = int(os.getenv("OPENSEARCH_PORT")) + self.search_username = os.getenv("OPENSEARCH_USERNAME") + self.search_password = os.getenv("OPENSEARCH_PASSWORD") def _instance(self) -> OpenSearch: """ diff --git a/dbrepo-ui/components/database/DatabaseToolbar.vue b/dbrepo-ui/components/database/DatabaseToolbar.vue index 3597876609..5a86bbaa01 100644 --- a/dbrepo-ui/components/database/DatabaseToolbar.vue +++ b/dbrepo-ui/components/database/DatabaseToolbar.vue @@ -15,7 +15,7 @@ bottom> <template v-slot:activator="{ props }"> <v-icon - class="ml-2" + class="mr-2" size="small" right :color="database.is_public ? 'success' : 'chip'" @@ -33,20 +33,13 @@ color="tertiary" :variant="buttonVariant" :text="$t('toolbars.database.dashboard.permanent') + ($vuetify.display.lgAndUp ? ' ' + $t('toolbars.database.dashboard.xl') : '')" /> - <v-btn - v-if="canImportCsv" - :prepend-icon="$vuetify.display.lgAndUp ? 'mdi-cloud-upload' : null" - color="tertiary" - :variant="buttonVariant" - :text="$t('toolbars.database.import-csv.permanent') + ($vuetify.display.lgAndUp ? ' ' + $t('toolbars.database.import-csv.xl') : '')" - :to="`/database/${$route.params.database_id}/table/import`" /> <v-btn v-if="canCreateSubset" :prepend-icon="$vuetify.display.lgAndUp ? 'mdi-wrench' : null" color="secondary" variant="flat" :text="($vuetify.display.lgAndUp ? $t('toolbars.database.create-subset.xl') + ' ' : '') + $t('toolbars.database.create-subset.permanent')" - class="ml-2 white--text" + class="mr-2 white--text" :to="`/database/${$route.params.database_id}/subset/create`" /> <v-btn v-if="canCreateView" @@ -54,7 +47,7 @@ color="secondary" variant="flat" :text="($vuetify.display.lgAndUp ? $t('toolbars.database.create-view.xl') + ' ' : '') + $t('toolbars.database.create-view.permanent')" - class="ml-2 white--text" + class="mr-2 white--text" :to="`/database/${$route.params.database_id}/view/create`" /> <v-btn v-if="canCreateTable" @@ -62,15 +55,15 @@ color="secondary" variant="flat" :text="($vuetify.display.lgAndUp ? $t('toolbars.database.create-table.xl') + ' ' : '') + $t('toolbars.database.create-table.permanent')" - class="ml-2" - :to="`/database/${$route.params.database_id}/table/create`" /> + class="mr-2" + :to="`/database/${$route.params.database_id}/table/create/dataset`" /> <v-btn v-if="canCreateIdentifier" :prepend-icon="$vuetify.display.lgAndUp ? 'mdi-identifier' : null" color="primary" variant="flat" :text="($vuetify.display.lgAndUp ? $t('toolbars.database.create-pid.xl') + ' ' : '') + $t('toolbars.database.create-pid.permanent')" - class="ml-2" + class="mr-2" :to="`/database/${$route.params.database_id}/persist`" /> <template v-slot:extension> <v-tabs diff --git a/dbrepo-ui/components/dialogs/EditTuple.vue b/dbrepo-ui/components/dialogs/EditTuple.vue index 3f40a93175..589c82b9b7 100644 --- a/dbrepo-ui/components/dialogs/EditTuple.vue +++ b/dbrepo-ui/components/dialogs/EditTuple.vue @@ -169,6 +169,7 @@ export default { return { valid: false, loading: false, + oldTuple: null, error: false, menu: false, bools: [ @@ -177,6 +178,9 @@ export default { ] } }, + mounted() { + this.oldTuple = Object.assign({}, this.tuple) + }, computed: { title () { return (this.edit ? this.$t('toolbars.table.data.edit') : this.$t('toolbars.table.data.add')) + ' ' + this.$t('toolbars.table.data.tuple') @@ -272,10 +276,19 @@ export default { }, updateTuple () { const constraints = {} - this.table.constraints.primary_key - .forEach((pk) => { - constraints[pk] = this.tuple[pk] - }) + if (this.table.constraints.primary_key.length > 0) { + this.table.constraints.primary_key + .forEach((pk) => { + constraints[pk.column.internal_name] = this.oldTuple[pk.column.internal_name] + }) + console.debug('table has primary key: set update tuple constraints', constraints) + } else { + this.table.columns + .forEach((column) => { + constraints[column.internal_name] = this.oldTuple[column.internal_name] + }) + console.debug('table does not have a primary key: set update tuple constraints', constraints) + } const tupleService = useTupleService() this.loading = true tupleService.update(this.$route.params.database_id, this.$route.params.table_id, { data: this.tuple, keys: constraints }) diff --git a/dbrepo-ui/components/subset/Builder.vue b/dbrepo-ui/components/subset/Builder.vue index a70c32f650..7c30fd8f5a 100644 --- a/dbrepo-ui/components/subset/Builder.vue +++ b/dbrepo-ui/components/subset/Builder.vue @@ -10,10 +10,10 @@ :text="title" /> <v-spacer /> <v-btn - v-if="user" :disabled="!canExecute" color="secondary" variant="flat" + class="mr-2" :loading="loadingQuery" :prepend-icon="$vuetify.display.lgAndUp ? 'mdi-run' : null" :text="$t('navigation.create')" @@ -52,11 +52,13 @@ clearable persistent-hint :variant="inputVariant" + required + :rules="[ + v => !!v || $t('validation.required'), + v => !validViewName(v) || $t('validation.view.exists') + ]" :label="$t('pages.view.subpages.create.name.label')" - :hint="$t('pages.view.subpages.create.name.hint')" - :rules="[v => !!v || $t('validation.required'), - v => !validViewName(v) || $t('validation.view.exists')]" - required /> + :hint="$t('pages.view.subpages.create.name.hint')" /> </v-col> </v-row> <v-row @@ -80,9 +82,11 @@ :variant="inputVariant" required clearable + :rules="[ + v => !!v || $t('validation.required') + ]" :label="$t('pages.view.subpages.create.visibility.label')" - :hint="$t('pages.view.subpages.create.visibility.hint')" - :rules="[v => !!v || $t('validation.required')]" /> + :hint="$t('pages.view.subpages.create.visibility.hint')" /> </v-col> </v-row> <v-window @@ -219,21 +223,6 @@ </div> </div> </div> - <v-row - dense> - <v-col - v-text="$t('pages.subset.subpages.create.generated')" /> - </v-row> - <v-row - id="query-raw" - dense> - <v-col> - <Raw - :value="query.formatted" - disabled - class="mt-2" /> - </v-col> - </v-row> </v-window-item> <v-window-item value="1"> diff --git a/dbrepo-ui/components/table/TableImport.vue b/dbrepo-ui/components/table/TableImport.vue index 8528b3b04e..22f7081751 100644 --- a/dbrepo-ui/components/table/TableImport.vue +++ b/dbrepo-ui/components/table/TableImport.vue @@ -2,15 +2,16 @@ <div> <v-stepper-header> <v-stepper-item - :title="$t('pages.table.subpages.import.dataset.title')" + :title="$t('pages.table.subpages.import.schema.title')" :complete="validStep1" - :value="stepStart"/> + :value="1"/> </v-stepper-header> <v-stepper-window direction="vertical"> <v-form ref="form" v-model="validStep1" + :disabled="disabled" @submit.prevent="submit"> <v-container> <v-row dense> @@ -122,102 +123,149 @@ <v-stepper-item :title="$t('pages.table.subpages.import.file.title')" :complete="validStep2" - :value="stepStart+1"/> + :value="2" /> </v-stepper-header> <v-stepper-window direction="vertical"> - <v-form - ref="form" - v-model="validStep2" - @submit.prevent="submit"> - <v-container> - <v-row - v-if="step > 1 && suggestedAnalyseSeparator && providedSeparator !== analysedSeparator" - dense> - <v-col> - <v-alert - border="start" - color="warning"> - {{ $t('pages.table.subpages.import.separator.warn.prefix') }} - <strong v-text="tableImport.separator"/> - {{ $t('pages.table.subpages.import.separator.warn.middle') }} - <strong v-text="suggestedAnalyseSeparator"/> - {{ $t('pages.table.subpages.import.separator.warn.suffix') }} - </v-alert> - </v-col> - </v-row> - <v-row - v-if="step > 1 && suggestedAnalyseLineTerminator && providedTerminator !== analysedTerminator" - dense> - <v-col> - <v-alert - border="start" - color="warning"> - {{ $t('pages.table.subpages.import.terminator.warn.prefix') }} - <strong>{{ JSON.stringify(tableImport.line_termination).replaceAll('"', '') }}</strong> - {{ $t('pages.table.subpages.import.terminator.warn.middle') }} - <strong>{{ JSON.stringify(suggestedAnalyseLineTerminator).replaceAll('"', '') }}</strong> - {{ $t('pages.table.subpages.import.terminator.warn.suffix') }} - </v-alert> - </v-col> - </v-row> - <v-row - v-if="!hasCompatibleSchema" - dense> - <v-col> - <v-alert - border="start" - color="warning" - :text="$t('pages.table.subpages.import.dataset.warn')"/> - </v-col> - </v-row> - <v-row> - <v-col cols="8"> - <v-file-input - v-model="file" - accept=".csv,.tsv" - :show-size="1000" - counter - required - :rules="[ - v => notFile(v) || $t('validation.required'), - ]" - :prepend-icon="validStep1 ? 'mdi-database-check-outline' : 'mdi-database-arrow-up-outline'" - persistent-hint - :variant="inputVariant" - :hint="$t('pages.table.subpages.import.file.hint')" - :label="$t('pages.table.subpages.import.file.label')" /> - </v-col> - </v-row> - <v-row> - <v-col cols="8"> - <v-btn - :disabled="!isAnalyseAllowed || !validStep1 || !validStep2" - :loading="loading" - :variant="buttonVariant" - color="secondary" - size="small" - :text="$t('pages.table.subpages.import.analyse.text')" - @click="uploadAndAnalyse"/> - </v-col> - </v-row> + <v-container> + <v-row + v-if="$route.query.location" + dense> + <v-col> + <p + v-text="$t('pages.table.subpages.import.storage.text')" /> + <v-chip + prepend-icon="mdi-cloud-upload" + label> + {{ $route.query.location }} + </v-chip> + </v-col> + </v-row> + <v-form + v-if="!$route.query.location" + ref="form" + v-model="validStep2" + :disabled="disabled" + @submit.prevent="submit"> + <v-row + v-if="step > 1 && suggestedAnalyseSeparator && providedSeparator !== analysedSeparator" + dense> + <v-col> + <v-alert + border="start" + color="warning"> + {{ $t('pages.table.subpages.import.separator.warn.prefix') }} + <strong v-text="tableImport.separator"/> + {{ $t('pages.table.subpages.import.separator.warn.middle') }} + <strong v-text="suggestedAnalyseSeparator"/> + {{ $t('pages.table.subpages.import.separator.warn.suffix') }} + </v-alert> + </v-col> + </v-row> + <v-row + v-if="step > 1 && suggestedAnalyseLineTerminator && providedTerminator !== analysedTerminator" + dense> + <v-col> + <v-alert + border="start" + color="warning"> + {{ $t('pages.table.subpages.import.terminator.warn.prefix') }} + <strong>{{ JSON.stringify(tableImport.line_termination).replaceAll('"', '') }}</strong> + {{ $t('pages.table.subpages.import.terminator.warn.middle') }} + <strong>{{ JSON.stringify(suggestedAnalyseLineTerminator).replaceAll('"', '') }}</strong> + {{ $t('pages.table.subpages.import.terminator.warn.suffix') }} + </v-alert> + </v-col> + </v-row> + <v-row + v-if="!hasCompatibleSchema" + dense> + <v-col + md="8"> + <v-alert + border="start" + color="warning" + :text="$t('pages.table.subpages.import.dataset.warn')"/> + </v-col> + </v-row> + <v-row + v-if="!$route.query.location" + dense> + <v-col cols="8"> + <v-file-input + v-model="file" + accept=".csv,.tsv" + :show-size="1000" + counter + required + :rules="[ + v => notFile(v) || $t('validation.required'), + ]" + :prepend-icon="validStep1 ? 'mdi-database-check-outline' : 'mdi-database-arrow-up-outline'" + persistent-hint + :variant="inputVariant" + :hint="$t('pages.table.subpages.import.file.hint')" + :label="$t('pages.table.subpages.import.file.label')" /> + </v-col> + </v-row> + <v-row + dense> + <v-col + cols="8"> + <v-btn + :disabled="!isAnalyseAllowed || !validStep1 || !validStep2 || disabled" + :loading="loading" + :variant="buttonVariant" + color="secondary" + size="small" + :text="$t('pages.table.subpages.import.analyse.text')" + @click="uploadAndAnalyse"/> + </v-col> + </v-row> + </v-form> </v-container> - </v-form> </v-stepper-window> <v-stepper-header v-if="!create"> <v-stepper-item - :title="$t('pages.table.subpages.import.summary.title')" - :value="stepStart+2"/> + :title="$t('pages.table.subpages.import.dataset.title')" + :complete="validStep3" + :value="3" /> </v-stepper-header> <v-stepper-window v-if="!create" direction="vertical"> <v-container> <v-row - v-if="rowCount" dense> <v-col> + <v-btn + color="secondary" + :disabled="step !== 3 || disabled" + size="small" + variant="flat" + :loading="loadingImport" + :text="$t('navigation.import')" + @click="importCsv"/> + </v-col> + </v-row> + </v-container> + </v-stepper-window> + <v-stepper-header + v-if="!create"> + <v-stepper-item + :title="$t('pages.table.subpages.import.summary.title')" + :value="4"/> + </v-stepper-header> + <v-stepper-window + v-if="!create && step === 4" + direction="vertical"> + <v-container> + <v-row + v-if="rowCount" + dense> + <v-col + md="8"> <v-alert border="start" color="success"> @@ -232,19 +280,11 @@ <v-btn v-if="rowCount !== null" color="secondary" - :disabled="step !== stepStart + 2" + :disabled="step !== 4 || disabled" size="small" variant="flat" :text="$t('navigation.data')" :to="`/database/${$route.params.database_id}/table/${tableId}/data`" /> - <v-btn - v-else - color="secondary" - :disabled="step !== stepStart + 2" - size="small" - variant="flat" - :text="$t('navigation.import')" - @click="importCsv"/> </v-col> </v-row> </v-container> @@ -253,7 +293,7 @@ </template> <script> -import {isNonNegativeInteger} from '@/utils' +import { isNonNegativeInteger } from '@/utils' import { useCacheStore } from '@/stores/cache' export default { @@ -263,12 +303,12 @@ export default { return null } }, - stepStart: { + create: { default: () => { - return 1 + return false } }, - create: { + disabled: { default: () => { return false } @@ -276,11 +316,13 @@ export default { }, data() { return { - step: 1, + step: 2, validStep1: false, validStep2: false, + validStep3: false, file: null, loading: false, + loadingImport: false, rowCount: null, suggestedAnalyseSeparator: null, suggestedAnalyseLineTerminator: null, @@ -312,15 +354,19 @@ export default { cacheStore: useCacheStore() } }, - watch: { - stepStart: { - handler() { - this.step = this.stepStart - } - } - }, mounted() { - this.step = this.stepStart + this.setQueryParamSafely('location') + this.setQueryParamSafely('quote') + this.setQueryParamSafely('false_element') + this.setQueryParamSafely('true_element') + this.setQueryParamSafely('null_element') + this.setQueryParamSafely('separator') + this.setQueryParamSafely('line_termination') + this.setQueryParamSafely('skip_lines') + if (this.$route.query.location) { + this.step = 3 + this.validStep2 = true + } }, computed: { table() { @@ -339,11 +385,11 @@ export default { if (!this.columns || !this.table) { return false } - const schema = this.table.columns.map(c => c.internal_name) + const schema = this.table.columns.map(c => c.name) let pass = true this.columns.forEach(c => { if (!schema.includes(c.name)) { - console.error('Failed to find column with id', c.name, 'in schema') + console.error('Failed to find column', c.name, 'in schema') pass = false } }) @@ -395,8 +441,13 @@ export default { submit() { this.$refs.form.validate() }, + setQueryParamSafely(name) { + if (this.$route.query[name]) { + this.tableImport[name] = this.$route.query[name] + } + }, importCsv() { - this.loading = true + this.loadingImport = true const tableService = useTableService() tableService.importCsv(this.$route.params.database_id, this.tableId, this.tableImport) .then(() => { @@ -407,16 +458,18 @@ export default { .then((rowCount) => { this.rowCount = rowCount }) - this.step = this.stepStart + 2 - this.loading = false + this.step = 4 + this.validStep3 = true + this.loadingImport = false }) - .catch(() => { + .catch(({code, message}) => { const toast = useToastInstance() - toast.error(this.$t('error.import.dataset')) - this.loading = false + console.error(code, message) + toast.error(`${this.$t(code)}: ${message}`) + this.loadingImport = false }) .finally(() => { - this.loading = false + this.loadingImport = false }) }, uploadAndAnalyse() { @@ -464,19 +517,21 @@ export default { this.suggestedAnalyseSeparator = separator this.suggestedAnalyseLineTerminator = line_termination this.tableImport.location = filename - this.step = this.stepStart + 2 + this.step = 3 const toast = useToastInstance() toast.success(this.$t('success.analyse.dataset')) this.$emit('analyse', {columns: this.columns, filename, line_termination}) this.loading = false }) - .catch(({code}) => { + .catch(({code, message}) => { this.loading = false const toast = useToastInstance() if (typeof code !== 'string') { + /* fallback default error message */ + toast.error(this.$t('error.analyse.invalid')) return } - toast.error(this.$t(code)) + toast.error(`${this.$t(code)}: ${message}`) }) } } diff --git a/dbrepo-ui/components/table/TableSchema.vue b/dbrepo-ui/components/table/TableSchema.vue index 1cc172858c..51f186dd4a 100644 --- a/dbrepo-ui/components/table/TableSchema.vue +++ b/dbrepo-ui/components/table/TableSchema.vue @@ -4,33 +4,24 @@ ref="form" v-model="valid" :disabled="disabled"> - <v-row> - <v-col md="8"> - <v-alert - v-if="needsSequence" - class="mb-6" - border="start" - :text="$t('validation.schema.primary-key')" - color="info" /> - </v-col> - </v-row> <v-row v-for="(c, idx) in columns" :key="`r-${idx}`" class="column pa-2 ml-1 mr-1 mb-2" dense> - <v-col cols="2"> + <v-col + cols="2"> <v-text-field v-model="c.name" required :rules="[v => !!v || $t('validation.required')]" - :error-messages="needsSequence && c.name.toLowerCase() === 'id' ? [$t('validation.schema.id')] : []" persistent-hint :variant="inputVariant" :label="$t('pages.table.subpages.schema.name.label')" :hint="$t('pages.table.subpages.schema.name.hint')" /> </v-col> - <v-col cols="2"> + <v-col + cols="1"> <v-select v-model="c.type" :items="columnTypes" @@ -44,7 +35,9 @@ :hint="$t('pages.table.subpages.schema.type.hint')" @update:modelValue="setDefaultSizeAndD(c)" /> </v-col> - <v-col cols="2" :hidden="c.type !== 'set'"> + <v-col + v-if="c.type === 'set'" + cols="2"> <v-text-field v-model="c.sets_values" required @@ -57,7 +50,9 @@ :label="$t('pages.table.subpages.schema.set.label')" @focusout="formatValues(c)" /> </v-col> - <v-col cols="2" :hidden="c.type !== 'enum'"> + <v-col + v-if="c.type === 'enum'" + cols="2"> <v-text-field v-model="c.enums_values" required @@ -66,11 +61,15 @@ :variant="inputVariant" :counter-value="() => c.enums.length" :hint="$t('pages.table.subpages.schema.enum.hint')" - :rules="[v => !!v || $t('validation.required')]" + :rules="[ + v => !!v || $t('validation.required') + ]" :label="$t('pages.table.subpages.schema.enum.label')" @focusout="formatValues(c)" /> </v-col> - <v-col cols="1" :hidden="defaultSize(c) === false"> + <v-col + v-if="defaultSize(c) !== false" + cols="1"> <v-text-field v-model.number="c.size" type="number" @@ -80,7 +79,9 @@ :error-messages="sizeErrorMessages(c)" :label="$t('pages.table.subpages.schema.size.label')" /> </v-col> - <v-col cols="1" :hidden="defaultD(c) === false"> + <v-col + v-if="defaultD(c) !== false" + cols="1"> <v-text-field v-model.number="c.d" type="number" @@ -90,50 +91,49 @@ :error-messages="dErrorMessages(c)" :label="$t('pages.table.subpages.schema.d.label')" /> </v-col> - <v-col v-if="hasDate(c)" cols="2"> + <v-col + cols="2" + v-if="hasDate(c)"> <v-select v-model="c.dfid" required :variant="inputVariant" + :disabled="disabled" :rules="[v => !!v || $t('validation.required')]" :items="filterDateFormats(c)" item-title="unix_format" item-value="id" :label="$t('pages.table.subpages.schema.fsp.label')" /> </v-col> - <v-col v-if="shift(c)" :cols="shift(c)" /> - <v-col cols="auto" class="pl-2"> + <v-col + v-if="shift(c)" + :cols="shift(c)" /> + <v-col + cols="auto" + class="pl-2"> <v-checkbox v-model="c.primary_key" + :disabled="disabled" :label="$t('pages.table.subpages.schema.primary-key.label')" @click="setOthers(c)" /> </v-col> - <v-col cols="auto" class="pl-10"> + <v-col + cols="auto" + class="pl-10"> <v-checkbox v-model="c.null_allowed" - :disabled="c.primary_key" + :disabled="c.primary_key || disabled" :label="$t('pages.table.subpages.schema.null.label')" /> </v-col> - <v-col cols="auto" class="pl-10"> + <v-col + cols="auto" + class="pl-10"> <v-checkbox v-model="c.unique" + :disabled="disabled" :hidden="c.primary_key" :label="$t('pages.table.subpages.schema.unique.label')" /> </v-col> - <v-col v-if="false" cols="auto" class="pl-10"> - <v-text-field - v-model="c.foreign_key" - :variant="inputVariant" - required - :label="$t('pages.table.subpages.schema.foreign-key.label')" /> - </v-col> - <v-col v-if="false" cols="auto" class="pl-10"> - <v-text-field - v-model="c.references" - :variant="inputVariant" - required - :label="$t('pages.table.subpages.schema.references.label')" /> - </v-col> <v-col v-if="canRemove(idx)" cols="auto" @@ -160,21 +160,12 @@ </v-row> <v-row> <v-col> - <v-btn - v-if="back" - :color="disabled ? '' : 'tertiary'" - :variant="buttonVariant" - size="small" - class="mr-2" - :disabled="disabled" - :text="$t('navigation.back')" - @click="goBack" /> <v-btn color="secondary" variant="flat" size="small" :loading="loading" - :disabled="disabled" + :disabled="disabled || !valid || this.columns.length === 0" :text="submitText" @click="submit" /> </v-col> @@ -194,12 +185,6 @@ export default { return [] } }, - back: { - type: Boolean, - default () { - return false - } - }, disabled: { type: Boolean, default () { @@ -262,7 +247,10 @@ export default { return false } let shift = 0 - if (this.hasDate(column) === false && this.columns.filter(c => this.hasDate(c) !== false).length > 0 && this.defaultSize(column) === false && this.columns.filter(c => this.defaultSize(c) !== false).length > 0) { + if (this.hasDate(column) === false && this.columns.filter(c => this.hasDate(c) !== false).length > 0) { + shift++ + } + if (this.defaultSize(column) === false && this.columns.filter(c => this.defaultSize(c) !== false).length > 0) { shift++ } if (this.defaultD(column) === false && this.columns.filter(c => this.defaultD(c) !== false).length > 0) { @@ -281,9 +269,6 @@ export default { column.null_allowed = false column.unique = true }, - goBack () { - this.$emit('back', { success: false }) - }, canRemove (idx) { if (idx > 0) { return true diff --git a/dbrepo-ui/components/table/TableToolbar.vue b/dbrepo-ui/components/table/TableToolbar.vue index d7b3d1596a..ef95ad1bd2 100644 --- a/dbrepo-ui/components/table/TableToolbar.vue +++ b/dbrepo-ui/components/table/TableToolbar.vue @@ -22,7 +22,7 @@ color="tertiary" :variant="buttonVariant" :text="$t('toolbars.database.import-csv.permanent') + ($vuetify.display.lgAndUp ? ' ' + $t('toolbars.database.import-csv.xl') : '')" - class="ml-2" + class="mr-2" :to="`/database/${$route.params.database_id}/table/${$route.params.table_id}/import`" /> <v-btn v-if="canExecuteQuery" @@ -30,7 +30,7 @@ color="secondary" variant="flat" :text="($vuetify.display.lgAndUp ? $t('toolbars.database.create-subset.xl') + ' ' : '') + $t('toolbars.database.create-subset.permanent')" - class="ml-2" + class="mr-2" :to="`/database/${$route.params.database_id}/subset/create?tid=${$route.params.table_id}`" /> <v-btn v-if="canCreateView" @@ -38,7 +38,7 @@ color="secondary" variant="flat" :text="($vuetify.display.lgAndUp ? $t('toolbars.database.create-view.xl') + ' ' : '') + $t('toolbars.database.create-view.permanent')" - class="ml-2" + class="mr-2" :to="`/database/${$route.params.database_id}/view/create?tid=${$route.params.table_id}`" /> <v-btn v-if="canDropTable" @@ -46,7 +46,7 @@ color="error" variant="flat" :text="($vuetify.display.lgAndUp ? 'Drop ' : '') + 'Table'" - class="ml-2" + class="mr-2" @click="dropTableDialog = true" /> <v-btn v-if="canGetPid" @@ -54,7 +54,7 @@ color="primary" variant="flat" :text="($vuetify.display.lgAndUp ? 'Get ' : '') + 'PID'" - class="ml-2" + class="mr-2" :to="`/database/${$route.params.database_id}/table/${$route.params.table_id}/persist`" /> <template v-slot:extension> <v-tabs v-model="tab" color="primary"> diff --git a/dbrepo-ui/locales/de-AT.json b/dbrepo-ui/locales/de-AT.json index e32fe38ee0..4d9e25b36e 100644 --- a/dbrepo-ui/locales/de-AT.json +++ b/dbrepo-ui/locales/de-AT.json @@ -30,11 +30,15 @@ "yes": "Ja", "no": "Nein", "mine": "(Meine)", - "loading": "Lade" + "loading": "Lade", + "view": "Ansicht" }, "pages": { "identifier": { "title": "Kennung", + "export": { + "text": "Metadatenexport" + }, "pid": { "title": "Persistenter Bezeichner" }, @@ -273,6 +277,19 @@ "broker": { "title": "Broker" }, + "connection": { + "title": "Verbindungsdetails", + "secure": "sicher", + "insecure": "unsicher", + "permissions": { + "write": "Sie können in diese Tabelle schreiben", + "read": "Sie können den gesamten Inhalt dieser Tabelle lesen" + } + }, + "description": { + "title": "Beschreibung", + "empty": "(Keine Beschreibung)" + }, "exchange": { "title": "Exchange" }, @@ -282,14 +299,8 @@ "routing-key": { "title": "Routing-Schlüssel" }, - "connection": { - "title": "Verbindungsdetails", - "secure": "sicher", - "insecure": "unsicher", - "permissions": { - "write": "Sie können in diese Tabelle schreiben", - "read": "Sie können den gesamten Inhalt dieser Tabelle lesen" - } + "name": { + "title": "Interner Name" }, "protocol": { "title": "Protokoll", @@ -301,10 +312,6 @@ "result-rows": { "title": "Reihen" }, - "description": { - "title": "Beschreibung", - "empty": "(Keine Beschreibung)" - }, "owner": { "title": "Eigentümer" }, @@ -320,9 +327,14 @@ "metadata": { "title": "Tabellenmetadaten" }, - "dataset": { + "schema": { "title": "Datensatzstruktur", - "warn": "Das Datensatzschema stimmt nicht mit dem Zieltabellenschema überein. " + "text": "das Tabellenschema manuell." + }, + "dataset": { + "title": "Datensatz importieren", + "text": "Erstellt ein Tabellenschema aus einem bestimmten CSV-Datensatz. ", + "warn": "Das Datensatzschema stimmt (wahrscheinlich) nicht mit dem Schema der Zieltabelle überein. " }, "name": { "label": "Name", @@ -349,12 +361,15 @@ "label": "Zeilen überspringen", "hint": "Optional. " }, + "storage": { + "text": "Datensatz vom Speicherdienst" + }, "quote": { "label": "Angebotskodierung", "hint": "Optional. " }, "terminator": { - "label": "Kodierung des Leitungsabschlusses", + "label": "Codierung des Leitungsabschlusses", "hint": "Optional. ", "warn": { "prefix": "Wir haben Ihren .csv/.tsv-Datensatz analysiert und festgestellt, dass die von Ihnen angegebene Leitungsabschlusscodierung vorliegt", @@ -375,7 +390,7 @@ "hint": "Optional. " }, "file": { - "title": "Datensatz-Upload", + "title": "Datensatzanalyse", "label": "Datensatzdatei", "hint": "Erforderlich. " }, @@ -396,6 +411,12 @@ "information": { "title": "Information" }, + "simple": { + "text": "Aus Schema" + }, + "from-csv": { + "text": "Von CSV" + }, "name": { "label": "Tabellenname", "hint": "Erforderlich" @@ -405,8 +426,7 @@ "hint": "" }, "summary": { - "prefix": "Tabelle mit Namen erstellt", - "suffix": "und importierter Datensatz erfolgreich" + "text": "Erstellte Tabelle mit internem Namen:" } }, "drop": { @@ -421,7 +441,7 @@ } }, "schema": { - "title": "System Versioniert", + "title": "Systemversioniert", "subtitle": "Tabellenbeschränkungen", "bullet": "●", "assign": "Zuordnen", @@ -447,7 +467,7 @@ "title": "Nullbar" }, "sequence": { - "title": "Sequenz" + "title": "Reihenfolge" }, "description": { "title": "Beschreibung" @@ -501,7 +521,7 @@ } }, "semantics": { - "title": "Semantische Instanz für Tabellenspalte zuweisen", + "title": "Weisen Sie der Tabellenspalte eine semantische Instanz zu", "subtitle": "Semantische Instanzen helfen Maschinen dabei, den richtigen Kontext Ihres Datensatzes zu ermitteln", "recommended": "Empfohlene semantische Instanzen", "bullet": "●", @@ -534,7 +554,7 @@ "hint": "Der Wert ist ein Primärschlüssel" }, "format": { - "hint": "Der Wert muss folgendes Format haben" + "hint": "Der Wert muss im Format vorliegen" }, "required": { "hint": "Erforderlich. " @@ -561,7 +581,7 @@ "title": "Interner Name" }, "visibility": { - "title": "Sichtbarkeit" + "title": "Sichtweite" }, "size": { "title": "Größe" @@ -618,7 +638,7 @@ } }, "tables": { - "empty": "(keine Tabellen)" + "empty": "(keine Tische)" }, "tuple": { "create": { @@ -650,9 +670,9 @@ }, "scheme": { "title": "Schema", - "subtitle": "Aktualisiert die Metadaten im Datenbankschema, um systemversionierte Tabellen und Ansichten in der Benutzeroberfläche anzuzeigen", + "subtitle": "Aktualisieren Sie die Metadaten im Datenbankschema, um systemversionierte Tabellen und Ansichten in der Benutzeroberfläche anzuzeigen", "submit": { - "text": "Aktualisieren" + "text": "Aktualisierung" } }, "ownership": { @@ -665,7 +685,7 @@ } }, "visibility": { - "title": "Sichtbarkeit", + "title": "Sichtweite", "subtitle": "Private Datenbanken verbergen die Daten, während Metadaten weiterhin sichtbar sind. ", "visibility": { "label": "Datenbanksichtbarkeit", @@ -723,7 +743,7 @@ "title": "Information", "subtitle": "Allgemeine Benutzermetadaten", "id": { - "label": "ID" + "label": "AUSWEIS" }, "username": { "label": "Nutzername" @@ -754,13 +774,13 @@ "de": "Deutsch (DE)" }, "theme": { - "title": "Theme", + "title": "Thema", "subtitle": "Aktualisieren Sie das Benutzerdesign, wenn Sie angemeldet sind", "label": "Thema", "dark": "Dunkel", "dark-contrast": "Dunkel – hoher Kontrast", "light": "Licht", - "light-contrast": "Hell – hoher Kontrast", + "light-contrast": "Licht – hoher Kontrast", "submit": { "text": "Aktualisieren" } @@ -836,25 +856,25 @@ } }, "view": { - "title": "Ansicht", + "title": "Sicht", "tabs": { - "info": "Info", + "info": "Die Info", "data": "Daten" }, "name": { "title": "Name" }, "query": { - "title": "Abfrage" + "title": "Stellungnahme" }, "creator": { - "title": "Ersteller" + "title": "Schöpfer" }, "creation": { - "title": "Erstellt" + "title": "Schaffung" }, "visibility": { - "title": "Sichtbarkeit" + "title": "Sichtweite" }, "subpages": { "create": { @@ -885,7 +905,7 @@ "title": "Sichtweite" }, "creator": { - "title": "Ersteller" + "title": "Schöpfer" }, "query": { "title": "Abfrage" @@ -904,13 +924,12 @@ "title": "Ergebniszeilen" }, "tabs": { - "info": "Info", + "info": "Die Info", "data": "Daten" }, "subpages": { "create": { "title": "Teilmenge erstellen", - "generated": "Die folgende Abfrage wird ausgeführt (schreibgeschützt)", "subtitle": "Die folgende Abfrage wird ausgeführt", "simple": { "text": "Einfach" @@ -952,20 +971,20 @@ "search": { "types": { "database": "Datenbank", - "table": "Tabelle", + "table": "Tisch", "column": "Spalte", "user": "Benutzer", "identifier": "Kennung", "concept": "Konzept", "unit": "Einheit", - "view": "View" + "view": "Sicht" }, "type": { "label": "Typ", "hint": "" }, "id": { - "label": "ID", + "label": "AUSWEIS", "hint": "" }, "name": { @@ -977,7 +996,7 @@ "hint": "" }, "publication-range": { - "hint": "Geben Sie Ihren benutzerdefinierten Veröffentlichungsjahrbereich an" + "hint": "Geben Sie Ihren benutzerdefinierten Veröffentlichungsjahresbereich an" }, "start-year": { "label": "Startjahr", @@ -1031,14 +1050,18 @@ }, "error": { "auth": { - "connection": "Kontakt zum Authentifizierungsdienst fehlgeschlagen", - "invalid": "Authentifizierung im Authentifizierungsdienst fehlgeschlagen" + "connection": "Der Authentifizierungsdienst konnte nicht kontaktiert werden", + "invalid": "Die Authentifizierung im Authentifizierungsdienst ist fehlgeschlagen" + }, + "analyse": { + "invalid": "Der Datensatz konnte nicht analysiert werden", + "missing": "Datensatz konnte nicht gefunden werden" }, "access": { "missing": "Der Zugriff in der Metadatendatenbank konnte nicht gefunden werden" }, "axios": { - "connection": "Es konnte keine Verbindung hergestellt werden", + "connection": "Kontakt zum Backend fehlgeschlagen", "timeout": "Zeitüberschreitung der Verbindung" }, "concept": { @@ -1049,17 +1072,17 @@ "missing": "Der Container konnte in der Metadatendatenbank nicht gefunden werden" }, "data": { + "connection": "Kontaktaufnahme zum Datendienst fehlgeschlagen", "invalid": "Die Kommunikation mit dem Datendienst ist fehlgeschlagen", - "connection": "Es konnte keine Verbindung zum Datendienst hergestellt werden", "value": "Spaltenwert konnte nicht festgelegt werden", - "drift": "Die Uhr Ihres Browsers ist nicht mit UTC synchronisiert und scheint um Folgendes eingestellt zu sein" + "drift": "Die Uhr Ihres Browsers ist nicht mit UTC synchronisiert und scheint um 16:00 Uhr verschoben zu sein" }, "database": { - "connection": "Es konnte keine Verbindung zur Datenbank hergestellt werden", + "connection": "Kontakt zur Datenbank konnte nicht hergestellt werden", + "create": "Datenbank konnte im Datendienst nicht erstellt werden", "invalid": "Aktion in der Datenbank konnte nicht ausgeführt werden", "querystore": "Die Abfrage konnte nicht in den Abfragespeicher eingefügt werden", - "missing": "Die Datenbank konnte nicht in der Metadatendatenbank gefunden werden", - "create": "Es konnte keine Verbindung zum Metadatendienst hergestellt werden" + "missing": "Die Datenbank konnte nicht in der Metadatendatenbank gefunden werden" }, "doi": { "missing": "DOI konnte in der Metadatendatenbank nicht gefunden werden" @@ -1107,7 +1130,7 @@ "query": { "missing": "Die Abfrage konnte im Datendienst nicht gefunden werden", "invalid": "Die Abfrage ist ungültig", - "type.exists": "Abfrage konnte nicht erstellt werden: kein solcher Spaltentyp", + "type.exists": "Abfrage konnte nicht erstellt werden: Kein solcher Spaltentyp", "type.build": "Abfrage konnte nicht erstellt werden: Derzeit gibt es keine Abfrageerstellungsunterstützung für den Spaltentyp", "column.exists": "Abfrage konnte nicht erstellt werden: In den Datenspalten fehlt die Spalte mit dem Namen" }, @@ -1118,9 +1141,9 @@ "persist": "Die Abfrage konnte nicht im Abfragespeicher der Datenbank gespeichert werden" }, "metadata": { - "privileged": "Das Abrufen privilegierter Metadaten im Datendienst ist fehlgeschlagen", - "connection": "Es konnte keine Verbindung zum Metadatendienst hergestellt werden", - "invalid": "Es konnten keine Authentifizierungsmetadaten im Datendienst abgerufen werden" + "connection": "Es konnte kein Kontakt zum Metadatendienst hergestellt werden", + "invalid": "Es konnten keine Authentifizierungsmetadaten im Datendienst abgerufen werden", + "privileged": "Das Abrufen privilegierter Metadaten im Datendienst ist fehlgeschlagen" }, "sidecar": { "export": "Der Datensatz konnte nicht in den Datenbank-Sidecar exportiert werden", @@ -1165,12 +1188,12 @@ "malformed": "Ungültige Paginierungsanforderung" }, "table": { + "connection": "Kontakt zur Datenbank konnte nicht hergestellt werden", + "create": "Tabelle konnte nicht erstellt werden", "missing": "Die Tabelle konnte in der Metadatendatenbank nicht gefunden werden", "exists": "Die Tabelle mit diesem Namen existiert bereits", "invalid": "Die Spalten im Datendienst konnten nicht analysiert werden", - "malformed": "Eintrag konnte nicht eingefügt werden", - "create": "Tabelle konnte nicht erstellt werden", - "connection": "Das Laden der Tabellendaten ist fehlgeschlagen, da die Datenbank nicht erreichbar ist" + "malformed": "Eintrag konnte nicht eingefügt werden" }, "unit": { "missing": "Die semantische Einheit konnte in der Metadatendatenbank nicht gefunden werden" @@ -1181,11 +1204,11 @@ "invalid": "Die Ansichtsabfrage konnte den Spalten im Datendienst nicht zugeordnet werden" }, "broker": { - "connection": "Es konnte keine Verbindung zum Vermittlungsdienst hergestellt werden", - "invalid": "Es konnten keine Metadaten im Metadatendienst abgerufen werden" + "connection": "Kontakt zum Broker-Service fehlgeschlagen", + "invalid": "Es konnten keine Metadaten im Brokerdienst abgerufen werden" }, "external": { - "invalid": "Metadaten konnten nicht aus dem Datacite-System abgerufen werden" + "invalid": "Es konnten keine Metadaten vom Datacite-System abgerufen werden" } }, "success": { @@ -1220,10 +1243,6 @@ "created": "Tabelle erfolgreich erstellt", "semantics": "Semantische Instanz erfolgreich zugewiesen" }, - "schema": { - "tables": "Die Metadaten der Datenbanktabellen wurden erfolgreich aktualisiert", - "views": "Metadaten der Datenbankansichten wurden erfolgreich aktualisiert" - }, "schema": { "tables": "Die Metadaten der Datenbanktabellen wurden erfolgreich aktualisiert.", "views": "Metadaten der Datenbankansichten wurden erfolgreich aktualisiert." @@ -1240,14 +1259,14 @@ "pid": { "saved": "Kennung erfolgreich gespeichert", "created": "Kennung erfolgreich erstellt", - "published": "Identifikator erfolgreich veröffentlicht", + "published": "Erfolgreich veröffentlichte Kennung", "updated": "Kennung erfolgreich aktualisiert", "deleted": "Kennung erfolgreich gelöscht" }, "user": { "info": "Benutzerinformationen erfolgreich aktualisiert", "theme": "Benutzerthema erfolgreich aktualisiert", - "password": "Benutzerkennwort erfolgreich aktualisiert", + "password": "Benutzerpasswort erfolgreich aktualisiert", "login": "Erfolgreich angemeldet" }, "view": { @@ -1260,7 +1279,7 @@ }, "toolbars": { "user": { - "info": "Info", + "info": "Die Info", "authentication": "Authentifizierung", "developer": "Entwickler" }, @@ -1294,30 +1313,30 @@ "xl": "CSV" }, "dashboard": { - "permanent": "Visualisiere", + "permanent": "Visualisieren", "xl": "Daten" }, "create-subset": { "permanent": "Teilmenge", - "xl": "Erstelle" + "xl": "Erstellen" }, "create-view": { - "permanent": "Ansicht", - "xl": "Erstelle" + "permanent": "Sicht", + "xl": "Erstellen" }, "create-table": { - "permanent": "Tabelle", - "xl": "Erstelle" + "permanent": "Tisch", + "xl": "Erstellen" }, "create-pid": { "permanent": "PID", "xl": "Erhalten" }, "info": { - "tab": "Info" + "tab": "Die Info" }, "tables": { - "tab": "Tabellen" + "tab": "Tische" }, "subsets": { "tab": "Teilmengen" @@ -1400,14 +1419,11 @@ "integer": "Größer oder gleich Null", "max-length": "Die maximale Länge beträgt: ", "day": "Ungültiger Tag", + "matching": "Nicht passend!", "doi": { "invalid": "Ungültiger DOI. " }, "month": "Ungültiger Monat", - "schema": { - "id": "Die Spalte muss als Primärschlüssel deklariert werden", - "primary-key": "Wir erstellen eine Spalte mit dem Namen „id“ mit einer automatisch ansteigenden Sequenz, die bei 1 beginnt. Bitte geben Sie eine Spalte mit Primärschlüssel an, wenn Sie dieses Verhalten nicht wünschen" - }, "uri": { "pattern": "Ungültiger URI", "exists": "URI existiert" @@ -1420,7 +1436,7 @@ "exists": "Der Ansichtsname existiert bereits" }, "table": { - "exists": "Tabellenname existiert bereits" + "exists": "Der Tabellenname existiert bereits" }, "prefix": { "pattern": "Ungültiges Präfixmuster", diff --git a/dbrepo-ui/locales/en-US.json b/dbrepo-ui/locales/en-US.json index edf6c66ca1..935023300e 100644 --- a/dbrepo-ui/locales/en-US.json +++ b/dbrepo-ui/locales/en-US.json @@ -19,7 +19,7 @@ "persist": "Persist", "cancel": "Cancel", "user": "User", - "import": "Import", + "import": "Import Data", "delete": "Delete", "recommend": "Recommend", "now": "Now", @@ -30,7 +30,8 @@ "yes": "Yes", "no": "No", "mine": "(mine)", - "loading": "Loading" + "loading": "Loading", + "view": "View" }, "pages": { "identifier": { @@ -276,6 +277,19 @@ "broker": { "title": "Broker" }, + "connection": { + "title": "Connection Details", + "secure": "secure", + "insecure": "insecure", + "permissions": { + "write": "You can write to this table", + "read": "You can read all contents of this table" + } + }, + "description": { + "title": "Description", + "empty": "(no description)" + }, "exchange": { "title": "Exchange" }, @@ -285,14 +299,8 @@ "routing-key": { "title": "Routing Key" }, - "connection": { - "title": "Connection Details", - "secure": "secure", - "insecure": "insecure", - "permissions": { - "write": "You can write to this table", - "read": "You can read all contents of this table" - } + "name": { + "title": "Internal Name" }, "protocol": { "title": "Protocol", @@ -304,10 +312,6 @@ "result-rows": { "title": "Rows" }, - "description": { - "title": "Description", - "empty": "(no description)" - }, "owner": { "title": "Owner" }, @@ -323,9 +327,14 @@ "metadata": { "title": "Table Metadata" }, - "dataset": { + "schema": { "title": "Dataset Structure", - "warn": "The dataset schema does not match the target table schema. You can still force the import but it is not recommended" + "text": "the table schema manually." + }, + "dataset": { + "title": "Import Dataset", + "text": "Creates a table schema from a given .csv dataset. Alternatively you can create", + "warn": "The dataset schema (likely) does not match the target table schema. You can still force the import but it is not recommended" }, "name": { "label": "Name", @@ -352,6 +361,9 @@ "label": "Skip Rows", "hint": "Optional. Number of rows to skip, e.g. when the first one contains header and no data" }, + "storage": { + "text": "Dataset from Storage Service" + }, "quote": { "label": "Quote Encoding", "hint": "Optional. Character that quotes data, e.g. double-quotes \"value\"" @@ -378,7 +390,7 @@ "hint": "Optional. Character sequence that represents boolean false, e.g. 0, false, no" }, "file": { - "title": "Dataset Upload", + "title": "Dataset Analysis", "label": "Dataset File", "hint": "Required. Needs to be in .csv/.tsv file format" }, @@ -399,6 +411,12 @@ "information": { "title": "Information" }, + "simple": { + "text": "From Schema" + }, + "from-csv": { + "text": "From CSV" + }, "name": { "label": "Table Name", "hint": "Required" @@ -408,8 +426,7 @@ "hint": "" }, "summary": { - "prefix": "Created table with name", - "suffix": "and imported dataset successfully" + "text": "Created table with internal name:" } }, "drop": { @@ -913,7 +930,6 @@ "subpages": { "create": { "title": "Create Subset", - "generated": "The following query will be executed (readonly)", "subtitle": "The following query will be executed", "simple": { "text": "Simple" @@ -1037,6 +1053,10 @@ "connection": "Failed to contact auth service", "invalid": "Failed to authenticate in auth service" }, + "analyse": { + "invalid": "Failed to analyse dataset", + "missing": "Failed to find dataset" + }, "access": { "missing": "Failed to find access in metadata database" }, @@ -1399,14 +1419,11 @@ "integer": "Greater or equal to zero", "max-length": "Maximum length is: ", "day": "Invalid day", + "matching": "Not matching!", "doi": { "invalid": "Invalid DOI. Must start with 10.xyz" }, "month": "Invalid month", - "schema": { - "id": "Column needs to be declared as primary key", - "primary-key": "We create a column named id with a auto-increasing sequence starting at 1. Please specify a column with primary key if you don't want this behavior" - }, "uri": { "pattern": "Invalid URI", "exists": "URI exists" diff --git a/dbrepo-ui/nuxt.config.ts b/dbrepo-ui/nuxt.config.ts index 7103e352a3..14a6c3034d 100644 --- a/dbrepo-ui/nuxt.config.ts +++ b/dbrepo-ui/nuxt.config.ts @@ -22,137 +22,149 @@ const routeRules = { } export default defineNuxtConfig({ - app: { - head: { - charset: 'utf-8', - viewport: 'width=device-width, initial-scale=1', - meta: [ - { 'http-equiv': 'Content-Security-Policy', content: 'upgrade-insecure-requests' } - ], - htmlAttrs: { - lang: 'en-US' - } - } - }, - build: { - transpile: ['vuetify'], - }, - css: [ - 'vuetify/lib/styles/main.sass', - '@mdi/font/css/materialdesignicons.min.css', - '@/assets/globals.css', - '@/assets/overrides.css', - ], - runtimeConfig: { - public: { - commit: '', - title: 'Database Repository', - logo: '/logo.svg', - icon: '/favicon.ico', - touch: '/apple-touch-icon.png', - version: 'bun-dev', - broker: { - host: 'localhost', - port: { - '5672': false - }, - extra: '' - }, - variant: { - input: { - normal: 'underlined', - contrast: 'outlined', - }, - button: { - normal: 'flat', - contrast: 'outlined', - }, - list: { - normal: '', - contrast: 'flat', - } - }, - api: { - client: 'http://localhost', - server: 'http://gateway-service', - }, - upload: { - client: 'http://localhost/api/upload/files' - }, - database: { - unsupported: '*,AVG,BIT_AND,BIT_OR,BIT_XOR,COUNT,COUNTDISTINCT,GROUP_CONCAT,JSON_ARRAYAGG,JSON_OBJECTAGG,MAX,MIN,STD,STDDEV,STDDEV_POP,STDDEV_SAMP,SUM,VARIANCE,VAR_POP,VAR_SAMP,--', - image: { - width: 400, - height: 400 - }, - extra: '' - }, - pid: { - default: { - publisher: 'Example University' - } - }, - doi: { - enabled: false, - endpoint: 'https://doi.org' - }, - links: { - rabbitmq: { - text: 'RabbitMQ Admin', - href: '/admin/broker/' - }, - keycloak: { - text: 'Keycloak Admin', - href: '/api/auth/' - } - } - } - }, - routeRules, - devServer: { - port: 3001 - }, - modules: [ - '@pinia/nuxt', - '@pinia-plugin-persistedstate/nuxt', - '@nuxtjs/i18n' - ], - pinia: { - storesDirs: ['./stores/**'], - }, - piniaPersistedstate: { - storage: 'localStorage' - }, - i18n: { - lazy: false, - langDir: 'locales', - strategy: 'no_prefix', - defaultLocale: 'de', - locales: [ - { - 'code': 'en', - 'file': 'en-US.json', - 'name': 'English (US)', - 'iso': 'en-US' - }, - { - 'code': 'de', - 'file': 'de-AT.json', - 'name': 'German (AT)', - 'iso': 'de-AT' - } - ] - - }, - vite: { - server: { - proxy - }, - vue: { - template: { - transformAssetUrls, - }, - }, - }, - devtools: { enabled: true } -}) + app: { + head: { + charset: 'utf-8', + viewport: 'width=device-width, initial-scale=1', + meta: [ + { 'http-equiv': 'Content-Security-Policy', content: 'upgrade-insecure-requests' } + ], + htmlAttrs: { + lang: 'en-US' + } + } + }, + + build: { + transpile: ['vuetify'], + }, + + css: [ + 'vuetify/lib/styles/main.sass', + '@mdi/font/css/materialdesignicons.min.css', + '@/assets/globals.css', + '@/assets/overrides.css', + ], + + runtimeConfig: { + public: { + commit: '', + title: 'Database Repository', + logo: '/logo.svg', + icon: '/favicon.ico', + touch: '/apple-touch-icon.png', + version: 'bun-dev', + broker: { + host: 'localhost', + port: { + '5672': false + }, + extra: '' + }, + variant: { + input: { + normal: 'underlined', + contrast: 'outlined', + }, + button: { + normal: 'flat', + contrast: 'outlined', + }, + list: { + normal: '', + contrast: 'flat', + } + }, + api: { + client: 'http://localhost', + server: 'http://gateway-service', + }, + upload: { + client: 'http://localhost/api/upload/files' + }, + database: { + unsupported: '*,AVG,BIT_AND,BIT_OR,BIT_XOR,COUNT,COUNTDISTINCT,GROUP_CONCAT,JSON_ARRAYAGG,JSON_OBJECTAGG,MAX,MIN,STD,STDDEV,STDDEV_POP,STDDEV_SAMP,SUM,VARIANCE,VAR_POP,VAR_SAMP,--', + image: { + width: 400, + height: 400 + }, + extra: '' + }, + pid: { + default: { + publisher: 'Example University' + } + }, + doi: { + enabled: false, + endpoint: 'https://doi.org' + }, + links: { + rabbitmq: { + text: 'RabbitMQ Admin', + href: '/admin/broker/' + }, + keycloak: { + text: 'Keycloak Admin', + href: '/api/auth/' + } + } + } + }, + + routeRules, + + devServer: { + port: 3001 + }, + + modules: [ + '@pinia/nuxt', + '@pinia-plugin-persistedstate/nuxt', + '@nuxtjs/i18n' + ], + + pinia: { + storesDirs: ['./stores/**'], + }, + + piniaPersistedstate: { + storage: 'localStorage' + }, + + i18n: { + lazy: false, + langDir: 'locales', + strategy: 'no_prefix', + defaultLocale: 'de', + locales: [ + { + 'code': 'en', + 'file': 'en-US.json', + 'name': 'English (US)', + 'iso': 'en-US' + }, + { + 'code': 'de', + 'file': 'de-AT.json', + 'name': 'German (AT)', + 'iso': 'de-AT' + } + ] + + }, + + vite: { + server: { + proxy + }, + vue: { + template: { + transformAssetUrls, + }, + }, + }, + + devtools: { enabled: true }, + compatibilityDate: '2024-07-24' +}) \ No newline at end of file diff --git a/dbrepo-ui/pages/database/[database_id]/table/[table_id]/import.vue b/dbrepo-ui/pages/database/[database_id]/table/[table_id]/import.vue index 7b29c6ba8e..38c9be5567 100644 --- a/dbrepo-ui/pages/database/[database_id]/table/[table_id]/import.vue +++ b/dbrepo-ui/pages/database/[database_id]/table/[table_id]/import.vue @@ -1,5 +1,6 @@ <template> - <div v-if="canInsertTableData"> + <div + v-if="canInsertTableData"> <v-toolbar flat> <v-btn class="mr-2" @@ -18,8 +19,7 @@ vertical variant="flat"> <TableImport - :table-id="$route.params.table_id" - @analyse="onAnalyse" /> + :table-id="$route.params.table_id" /> </v-stepper> </v-card-text> </v-card> @@ -94,11 +94,6 @@ export default { } return this.roles.includes('insert-table-data') } - }, - methods: { - onAnalyse (event) { - const { columns } = event - } } } </script> diff --git a/dbrepo-ui/pages/database/[database_id]/table/[table_id]/info.vue b/dbrepo-ui/pages/database/[database_id]/table/[table_id]/info.vue index 47ba1af433..08b42c0d93 100644 --- a/dbrepo-ui/pages/database/[database_id]/table/[table_id]/info.vue +++ b/dbrepo-ui/pages/database/[database_id]/table/[table_id]/info.vue @@ -32,6 +32,10 @@ :title="$t('pages.table.id.title')"> {{ table.id }} </v-list-item> + <v-list-item + :title="$t('pages.table.name.title')"> + {{ table.internal_name }} + </v-list-item> <v-list-item :title="$t('pages.table.size.title')"> {{ sizeToHumanLabel(table.data_length) }} @@ -71,7 +75,8 @@ </v-list> </v-card-text> </v-card> - <v-divider /> + <v-divider + v-if="canWrite && canWriteQueues" /> <v-card v-if="canWrite && canWriteQueues" variant="flat" @@ -86,25 +91,11 @@ </v-list-item> <v-list-item :title="$t('pages.table.exchange.title')"> - <span> - <v-badge - inline - color="code" - :content="database.exchange_type"> - <span v-text="database.exchange_name" /> - </v-badge> - </span> + {{ database.exchange_name }} </v-list-item> <v-list-item :title="$t('pages.table.queue.title')"> - <span> - <v-badge - inline - color="code" - :content="table.queue_type" > - <span v-text="table.queue_name" /> - </v-badge> - </span> + {{ table.queue_name }} </v-list-item> <v-list-item :title="$t('pages.table.routing-key.title')"> @@ -261,7 +252,7 @@ export default { return this.userStore.getAccess }, hasDescription () { - return this.table && this.table.description !== null + return this.table && this.table.description }, canWriteQueues () { if (!this.roles) { @@ -306,7 +297,7 @@ export default { if (!this.$config.public.broker.port) { return [] } - Object.keys(this.$config.public.broker.port).map(key => { + return Object.keys(this.$config.public.broker.port).map(key => { return { port: key, secure: this.$config.public.broker.port[key] @@ -325,7 +316,6 @@ export default { } }, methods: { - sizeToHumanLabel, amqpString (port) { if (!this.user) { return null diff --git a/dbrepo-ui/pages/database/[database_id]/table/import.vue b/dbrepo-ui/pages/database/[database_id]/table/create/dataset.vue similarity index 85% rename from dbrepo-ui/pages/database/[database_id]/table/import.vue rename to dbrepo-ui/pages/database/[database_id]/table/create/dataset.vue index ed86afe09e..9a68b5786f 100644 --- a/dbrepo-ui/pages/database/[database_id]/table/import.vue +++ b/dbrepo-ui/pages/database/[database_id]/table/create/dataset.vue @@ -13,6 +13,21 @@ <v-card variant="flat" rounded="0"> + <v-card-text> + <v-row> + <v-col + md="8"> + <v-alert + border="start" + color="info"> + {{ $t('pages.table.subpages.import.dataset.text') }} + <NuxtLink + :href="`/database/${$route.params.database_id}/table/create/schema`" + v-text="$t('pages.table.subpages.import.schema.text')" /> + </v-alert> + </v-col> + </v-row> + </v-card-text> <v-card-text> <v-stepper vertical @@ -28,10 +43,11 @@ <v-form ref="form" v-model="validStep1" + :disabled="step > 4" @submit.prevent="submit"> <v-container> <v-row dense> - <v-col md="8"> + <v-col md="4"> <v-text-field v-model="tableCreate.name" :rules="[ @@ -46,9 +62,7 @@ :hint="$t('pages.table.subpages.import.name.hint')" :label="$t('pages.table.subpages.import.name.label')"/> </v-col> - </v-row> - <v-row dense> - <v-col md="8"> + <v-col md="4"> <v-text-field v-model="generatedTableName" :rules="[ @@ -86,8 +100,8 @@ </v-form> </v-stepper-window> <TableImport - :step-start="2" :create="true" + :disabled="!validStep1 || step > 4" :table="table" @analyse="onAnalyse"/> <v-stepper-header> @@ -103,6 +117,7 @@ <TableSchema ref="schema" :back="false" + :disabled="step > 4" :loading="loading" :submit-text="$t('navigation.continue')" :columns="tableCreate.columns" @@ -119,25 +134,33 @@ direction="vertical"> <v-container> <v-row dense> - <v-col> + <v-col + md="8"> <v-alert border="start" color="success"> - {{ $t('pages.table.subpages.create.summary.prefix') }} + {{ $t('pages.table.subpages.create.summary.text') }} <strong v-text="table.internal_name"/> - {{ $t('pages.table.subpages.create.summary.suffix') }} </v-alert> </v-col> </v-row> <v-row> <v-col> + <v-btn + class="mb-1 mr-2" + color="tertiary" + size="small" + variant="flat" + :loading="loadingImport" + :text="$t('navigation.import')" + @click="onImport"/> <v-btn class="mb-1" color="secondary" size="small" variant="flat" :loading="loadingContinue" - :text="$t('navigation.data')" + :text="$t('navigation.view')" @click="onContinue"/> </v-col> </v-row> @@ -169,6 +192,7 @@ export default { validStep4: false, error: false, loadingContinue: false, + loadingImport: false, fileModel: null, rowCount: null, file: { @@ -306,53 +330,24 @@ export default { if (!success) { return } - const payload = Object.assign({}, this.tableCreate) - payload.columns = columns - payload.constraints = constraints - this.createTable(payload) - .then(table => this.import(table)) - }, - createTable(payload) { + const schema = Object.assign({}, this.tableCreate) + schema.columns = columns + schema.constraints = constraints this.loading = true const tableService = useTableService() - return new Promise((resolve, reject) => { - if (this.table) { - resolve(this.table) - return - } - tableService.create(this.$route.params.database_id, payload) + tableService.create(this.$route.params.database_id, schema) .then((table) => { this.table = table - resolve(table) - }) - .catch((error) => { - this.loading = false const toast = useToastInstance() - if (typeof error.code !== 'string' || typeof error.message !== 'string') { - reject(error) - } - toast.error(`${this.$t(error.code)}: ${error.message}`) - reject(error) - }) - .finally(() => { - this.loading = false - }) - }) - }, - import(table) { - this.loading = true - const tableService = useTableService() - tableService.importCsv(this.$route.params.database_id, table.id, this.tableImport) - .then(() => { + toast.success(this.$t('success.table.created')) this.step = 5 - const toast = useToastInstance() - toast.success(this.$t('success.import.dataset')) - this.cacheStore.reloadDatabase() }) .catch(({code, message}) => { this.loading = false const toast = useToastInstance() if (typeof code !== 'string' || typeof message !== 'string') { + /* fallback */ + toast.error(`${this.$t('error.table.create')}: ${message}`) return } toast.error(`${this.$t(code)}: ${message}`) @@ -361,10 +356,6 @@ export default { this.loading = false }) }, - schemaValidity(event) { - const {valid} = event - this.validStep4 = valid - }, onAnalyse({columns, filename, line_termination}) { console.debug('analysed', columns) this.tableCreate.columns = columns @@ -374,6 +365,10 @@ export default { this.step = 4 } }, + async onImport () { + this.loadingImport = true + await this.$router.push({ path: `/database/${this.$route.params.database_id}/table/${this.table.id}/import`, query: this.tableImport }) + }, async onContinue () { this.loadingContinue = true await this.$router.push(`/database/${this.$route.params.database_id}/table/${this.table.id}/data`) diff --git a/dbrepo-ui/pages/database/[database_id]/table/create.vue b/dbrepo-ui/pages/database/[database_id]/table/create/schema.vue similarity index 93% rename from dbrepo-ui/pages/database/[database_id]/table/create.vue rename to dbrepo-ui/pages/database/[database_id]/table/create/schema.vue index 55c9599f8d..b60de5df30 100644 --- a/dbrepo-ui/pages/database/[database_id]/table/create.vue +++ b/dbrepo-ui/pages/database/[database_id]/table/create/schema.vue @@ -31,7 +31,7 @@ @submit.prevent="submit"> <v-container> <v-row dense> - <v-col md="8"> + <v-col md="4"> <v-text-field v-model="tableCreate.name" :rules="[ @@ -46,9 +46,7 @@ :hint="$t('pages.table.subpages.import.name.hint')" :label="$t('pages.table.subpages.import.name.label')" /> </v-col> - </v-row> - <v-row dense> - <v-col md="8"> + <v-col md="4"> <v-text-field v-model="generatedTableName" :rules="[ @@ -96,7 +94,7 @@ <v-container> <TableSchema submit-text="Create" - :disabled="!tableCreate.name || table" + :disabled="!valid || table" :columns="tableCreate.columns" :loading="loading" @close="schemaClose" /> @@ -109,8 +107,9 @@ :value="3" /> </v-stepper-header> <v-stepper-window + v-if="table" direction="vertical"> - <v-container v-if="table"> + <v-container> <v-row dense> <v-col md="8"> @@ -122,6 +121,14 @@ </v-row> <v-row> <v-col> + <v-btn + color="tertiary" + class="mr-2" + variant="flat" + size="small" + :loading="loadingImport" + :text="$t('navigation.import')" + @click="onImport" /> <v-btn color="secondary" variant="flat" @@ -157,6 +164,7 @@ export default { valid: false, description: null, loading: false, + loadingImport: false, loadingContinue: false, step: 1, table: null, @@ -279,6 +287,10 @@ export default { } this.createTable(columns, constraints) }, + async onImport () { + this.loadingImport = true + await this.$router.push(`/database/${this.$route.params.database_id}/table/${this.table.id}/import`) + }, async onContinue () { this.loadingContinue = true await this.$router.push(`/database/${this.$route.params.database_id}/table/${this.table.id}/info`) diff --git a/dbrepo-ui/pages/signup.vue b/dbrepo-ui/pages/signup.vue index 5c95e0c0f0..54c0060225 100644 --- a/dbrepo-ui/pages/signup.vue +++ b/dbrepo-ui/pages/signup.vue @@ -47,7 +47,9 @@ autocomplete="off" required name="password" - :rules="[v => !!v || $t('validation.required')]" + :rules="[ + v => !!v || $t('validation.required') + ]" type="password" persistent-hint :label="$t('pages.signup.password.label')" @@ -61,7 +63,10 @@ autocomplete="off" required name="password-confirm" - :rules="[v => !!v || $t('validation.required'), v => (!!v && v) === createAccount.password || $t('Not matching!')]" + :rules="[ + v => !!v || $t('validation.required') + ]" + :error-messages="password2 && password2 !== this.createAccount.password ? [this.$t('validation.matching')] : []" type="password" persistent-hint :label="$t('pages.signup.confirm.label')" diff --git a/dbrepo-ui/utils/index.ts b/dbrepo-ui/utils/index.ts index 66dbe94483..d940d076ae 100644 --- a/dbrepo-ui/utils/index.ts +++ b/dbrepo-ui/utils/index.ts @@ -1,7 +1,6 @@ -import {format} from 'date-fns' +import { format } from 'date-fns' import moment from 'moment' import type {AxiosError} from 'axios' -import type {Api} from "@vitejs/plugin-vue"; export function notEmpty(str: string) { @@ -1086,7 +1085,7 @@ export function timestampsToHumanDifference(date1: string, date2: string) { export function sizeToHumanLabel(num: number) { let number = Number(num) if (!number) { - return '0' + return '0 B' } if (number < 1000) { return `${Math.floor(number)} B` diff --git a/docker-compose.yml b/docker-compose.yml index aa869d7bc2..1e77531c42 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,5 +1,3 @@ -version: "3.6" - volumes: metadata-db-data: data-db-data: @@ -18,8 +16,8 @@ services: image: docker.io/bitnami/mariadb:11.1.3-debian-11-r6 volumes: - metadata-db-data:/bitnami/mariadb - - ./dbrepo-metadata-db/setup-schema.sql:/docker-entrypoint-initdb.d/1_setup-schema.sql - - ./dbrepo-metadata-db/setup-data.sql:/docker-entrypoint-initdb.d/2_setup-data.sql + - ./dbrepo-metadata-db/1_setup-schema.sql:/docker-entrypoint-initdb.d/1_setup-schema.sql + - ./dbrepo-metadata-db/2_setup-data.sql:/docker-entrypoint-initdb.d/2_setup-data.sql ports: - "3306:3306" environment: diff --git a/install.sh b/install.sh index 1dc5cdf82b..a9fcf6d10d 100644 --- a/install.sh +++ b/install.sh @@ -41,7 +41,7 @@ if [[ $SKIP_CHECKS -eq 0 ]] && [[ $DOWNLOAD_ONLY -ne 1 ]]; then else echo "RAM ${RAM}GB OK" fi - MAX_MAP_COUNT=$(cat /etc/sysctl.conf | grep -oP "vm.max_map_count=.*" | grep -oP "[0-9]+") + MAX_MAP_COUNT=$(cat /proc/sys/vm/max_map_count) if [[ $MAX_MAP_COUNT -lt $MIN_MAP_COUNT ]]; then echo "You do not have enough max. map counts:" echo "" @@ -58,17 +58,8 @@ fi # environment echo "[🚀] Gathering environment for version ${VERSION} ..." -mkdir -p ./dist -curl -sSL -o ./docker-compose.yml "https://gitlab.phaidra.org/fair-data-austria-db-repository/fda-services/-/raw/release-${VERSION}/.docker/docker-compose.yml" -curl -sSL -o ./.env "https://gitlab.phaidra.org/fair-data-austria-db-repository/fda-services/-/raw/release-${VERSION}/.docker/.env" -curl -sSL -o ./dist/1_setup-schema.sql "https://gitlab.phaidra.org/fair-data-austria-db-repository/fda-services/-/raw/release-${VERSION}/dbrepo-metadata-db/setup-schema.sql" -curl -sSL -o ./dist/2_setup-data.sql "https://gitlab.phaidra.org/fair-data-austria-db-repository/fda-services/-/raw/release-${VERSION}/dbrepo-metadata-db/setup-data.sql" -curl -sSL -o ./dist/rabbitmq.conf "https://gitlab.phaidra.org/fair-data-austria-db-repository/fda-services/-/raw/release-${VERSION}/dbrepo-broker-service/rabbitmq.conf" -curl -sSL -o ./dist/enabled_plugins "https://gitlab.phaidra.org/fair-data-austria-db-repository/fda-services/-/raw/release-${VERSION}/dbrepo-broker-service/enabled_plugins" -curl -sSL -o ./dist/definitions.json "https://gitlab.phaidra.org/fair-data-austria-db-repository/fda-services/-/raw/release-${VERSION}/dbrepo-broker-service/definitions.json" -curl -sSL -o ./dist/advanced.config "https://gitlab.phaidra.org/fair-data-austria-db-repository/fda-services/-/raw/release-${VERSION}/dbrepo-broker-service/advanced.config" -curl -sSL -o ./dist/dbrepo.conf "https://gitlab.phaidra.org/fair-data-austria-db-repository/fda-services/-/raw/release-${VERSION}/dbrepo-gateway-service/dbrepo.conf" -curl -sSL -o ./dist/s3_config.json "https://gitlab.phaidra.org/fair-data-austria-db-repository/fda-services/-/raw/release-${VERSION}/dbrepo-storage-service/s3_config.json" +curl -sSL -o ./dist.tar.gz "https://gitlab.phaidra.org/fair-data-austria-db-repository/fda-services/-/raw/release-${VERSION}/.docker/dist.tar.gz" +tar xzfv ./dist.tar.gz if [[ $DOWNLOAD_ONLY -eq 1 ]]; then echo "[🎉] Successfully downloaded environment!" diff --git a/make/dev.mk b/make/dev.mk index 14eba11d52..fa3c71a537 100644 --- a/make/dev.mk +++ b/make/dev.mk @@ -8,3 +8,15 @@ start-dev: build-images ## Start the development deployment. .PHONY: stop-dev stop-dev: ## Stop the development deployment and remove all data. docker compose down + +.PHONY: package-config +package-config: ## Package the config files + cp ./dbrepo-metadata-db/1_setup-schema.sql ./.docker/config + cp ./dbrepo-metadata-db/2_setup-data.sql ./.docker/config + cp ./dbrepo-broker-service/rabbitmq.conf ./.docker/config + cp ./dbrepo-broker-service/enabled_plugins ./.docker/config + cp ./dbrepo-broker-service/definitions.json ./.docker/config + cp ./dbrepo-broker-service/advanced.config ./.docker/config + cp ./dbrepo-storage-service/s3_config.json ./.docker/config + cp ./dbrepo-gateway-service/dbrepo.conf ./.docker/config + cd ./.docker && tar czfv ./dist.tar.gz ./docker-compose.yml ./.env ./config -- GitLab