diff --git a/dbrepo-authentication-service/dbrepo-realm.json b/dbrepo-authentication-service/dbrepo-realm.json index e3b1298850684fa60662888ffd27082181102b4c..88b4fbec0040e351caef5e6dc211c07cbc60af1a 100644 --- a/dbrepo-authentication-service/dbrepo-realm.json +++ b/dbrepo-authentication-service/dbrepo-realm.json @@ -7,8 +7,8 @@ "refreshTokenMaxReuse" : 1, "accessTokenLifespan" : 720, "accessTokenLifespanForImplicitFlow" : 900, - "ssoSessionIdleTimeout" : 1800, - "ssoSessionMaxLifespan" : 36000, + "ssoSessionIdleTimeout" : 1296000, + "ssoSessionMaxLifespan" : 1296000, "ssoSessionIdleTimeoutRememberMe" : 0, "ssoSessionMaxLifespanRememberMe" : 0, "offlineSessionIdleTimeout" : 2592000, @@ -1054,7 +1054,7 @@ "otpPolicyLookAheadWindow" : 1, "otpPolicyPeriod" : 30, "otpPolicyCodeReusable" : false, - "otpSupportedApplications" : [ "totpAppMicrosoftAuthenticatorName", "totpAppGoogleName", "totpAppFreeOTPName" ], + "otpSupportedApplications" : [ "totpAppFreeOTPName", "totpAppMicrosoftAuthenticatorName", "totpAppGoogleName" ], "webAuthnPolicyRpEntityName" : "keycloak", "webAuthnPolicySignatureAlgorithms" : [ "ES256" ], "webAuthnPolicyRpId" : "", @@ -2004,23 +2004,6 @@ "config" : { "allow-default-scopes" : [ "true" ] } - }, { - "id" : "1849e52a-b8c9-44a8-af3d-ee19376a1ed1", - "name" : "Trusted Hosts", - "providerId" : "trusted-hosts", - "subType" : "anonymous", - "subComponents" : { }, - "config" : { - "host-sending-registration-request-must-match" : [ "true" ], - "client-uris-must-match" : [ "true" ] - } - }, { - "id" : "f565cb47-3bcf-4078-8f94-eb4179c375b8", - "name" : "Full Scope Disabled", - "providerId" : "scope", - "subType" : "anonymous", - "subComponents" : { }, - "config" : { } }, { "id" : "0efa669d-1017-4b4a-82e1-c2eaf72de2c9", "name" : "Allowed Client Scopes", @@ -2038,25 +2021,60 @@ "subComponents" : { }, "config" : { } }, { - "id" : "104ec5a9-025b-4c44-8ac0-82d22887ca3e", + "id" : "3ab11d74-5e76-408a-b85a-26bf8950f979", "name" : "Allowed Protocol Mapper Types", "providerId" : "allowed-protocol-mappers", - "subType" : "authenticated", + "subType" : "anonymous", "subComponents" : { }, "config" : { - "allowed-protocol-mapper-types" : [ "oidc-address-mapper", "saml-user-attribute-mapper", "saml-role-list-mapper", "saml-user-property-mapper", "oidc-full-name-mapper", "oidc-usermodel-property-mapper", "oidc-usermodel-attribute-mapper", "oidc-sha256-pairwise-sub-mapper" ] + "allowed-protocol-mapper-types" : [ "oidc-usermodel-attribute-mapper", "oidc-sha256-pairwise-sub-mapper", "saml-user-attribute-mapper", "saml-role-list-mapper", "saml-user-property-mapper", "oidc-address-mapper", "oidc-usermodel-property-mapper", "oidc-full-name-mapper" ] } }, { - "id" : "3ab11d74-5e76-408a-b85a-26bf8950f979", + "id" : "1849e52a-b8c9-44a8-af3d-ee19376a1ed1", + "name" : "Trusted Hosts", + "providerId" : "trusted-hosts", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { + "host-sending-registration-request-must-match" : [ "true" ], + "client-uris-must-match" : [ "true" ] + } + }, { + "id" : "f565cb47-3bcf-4078-8f94-eb4179c375b8", + "name" : "Full Scope Disabled", + "providerId" : "scope", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { } + }, { + "id" : "104ec5a9-025b-4c44-8ac0-82d22887ca3e", "name" : "Allowed Protocol Mapper Types", "providerId" : "allowed-protocol-mappers", - "subType" : "anonymous", + "subType" : "authenticated", "subComponents" : { }, "config" : { - "allowed-protocol-mapper-types" : [ "oidc-sha256-pairwise-sub-mapper", "oidc-full-name-mapper", "oidc-usermodel-attribute-mapper", "saml-user-property-mapper", "oidc-address-mapper", "saml-role-list-mapper", "saml-user-attribute-mapper", "oidc-usermodel-property-mapper" ] + "allowed-protocol-mapper-types" : [ "saml-user-attribute-mapper", "oidc-usermodel-attribute-mapper", "oidc-sha256-pairwise-sub-mapper", "oidc-address-mapper", "saml-user-property-mapper", "saml-role-list-mapper", "oidc-usermodel-property-mapper", "oidc-full-name-mapper" ] } } ], + "org.keycloak.userprofile.UserProfileProvider" : [ { + "id" : "5da93330-911b-44c9-b971-5176ed5ce1f9", + "providerId" : "declarative-user-profile", + "subComponents" : { }, + "config" : { } + } ], "org.keycloak.keys.KeyProvider" : [ { + "id" : "2f53ccf3-37b0-4d34-83e7-ed497499ee51", + "name" : "rsa-enc-generated", + "providerId" : "rsa-enc-generated", + "subComponents" : { }, + "config" : { + "privateKey" : [ "MIIEowIBAAKCAQEA3b1tNLfcjFLUw9UShVDNf+ZD8sQqb4YBaIXcSJTX/zDQUPiCp176BBGI3s4VplDArnOW+LumozmKogeoHEnGEIDW8ovgK5uMU9tSA2p0qqGBUMOdR8YATTIfCJe7qGiiuGa3WZy3sQLM70SuRzx02YU8gvUcvl2Js4KyqAziOUX/w3Wa59H9jjGNUXYyqaPWJp73eHzbVYWySzyLG22mVlcUtBx5siL5T2/Xu0p9z4l7/bapwwmOVi1ZrcHjbEAwdGEiSMGI/uWqAF+r1BRpmJLR7HNXcL3eK4/56VYLaiwSejfyYeRFMITEn/UxGYhcXZ5xMUUCG0TxjBhLYpTBuwIDAQABAoIBAA4dwebcxkrH99Poa8+WkiE7JgaS9sahx9OBI2xwJANoIU2TpzGuNLQZ76uLgB+rPWZTD9Xm5a1iJjwOyQ9/937TzPCk91D0tpgcusRikb8jx/6TGB9acL4kBjYUVCCHr3BA2G75MKKGtJ2OMvAbCQSosZj+r2VDwYFEPUkV2jheE5JHSBkwyIRrus3JCwu8gu5fyCg9z8ljcxJxI5HIsi4v8Z21aCw/cLj7h5cMt44wCjQz4rOfYNBEFeHDtlfR1QtWKgjm4ZHHJbKrzf9b2kQXclziceEbSM0tYbROEXKi+s0Zc+z3HEG89vv0vfN400clmzzIAijKY6gg3pPRWdECgYEA+lnWYbSlXDMNYx6RBXm1RnlMUYIm4oy4/9ljgnoGJ6WCn3SjFkgaDtiKfGIG1BSB85r04pAPANgcWHf5tWDnq0ARvBVG0BX2bKd++7B3D4d3CRYKCwm88SslJXv9dfHVhq4+zViFPiUWwT20A72jCuUCvL88y5fh/YBecfdh+jECgYEA4r5RD0NB9dMaeg5/jk/GEHIo4Z9KLc6FrSoOFo2xFkPOy1sgDpDOiNtypuWvniO7k7Ose3DS3hlfTMsKzIW/CgQJ20+Y4cvBWDaOsRxfjj7w3d+jH5OSJdKKSzTrgLKc9ZhlRzVXy0J0hipIA6HG5kdVdLXmh85ITmf1CbJhE6sCgYBjPVeBNbXTHZ2x6/z62aslO5IoQVqetb/kE82hfDOSZcao5Ph9Lam+ttH2ynkAevykj4mBgi+gWwqpey2uW7KaLPSaxShj9kDQA3mP1fzsV/u0y1rB02Nlin/YIxVvOqU1FT9p8SwoXVVu1sHUNck62VtDbN9xqUx5S/ikXrclEQKBgQCoTssOwEcK+Vty9KYcdfy4onTUHZBLdjxl8Iyqkxy7QTQUYRznkvesQPDXEDGO+kk3dyx2KKZt9Hl4IFNww2quPZcvcuMx4DQxjbXXpA8OIIxcta95NepLJwA+mRai3nKCH1A2TlNP7pFeMa5o+8IPly3Ix2lKr4Wepa4PN5i1pwKBgCZ1QP6XAOERl9NznNmU0rXVcvYNP4PIIfQWfvGsldZ4QKkmjjAGiS0/oYqdWs+UDRZyCRChaVjDXO9fk0PEG5OGKAj9nyiYCT/M8xtJ3UeP5ffZZvJ/vnye3QdDIo1e38ZzsWwJHmLYw7fRqY9W5Vxo0Vsy22U3CJY70KTxVdTy" ], + "keyUse" : [ "ENC" ], + "certificate" : [ "MIICmzCCAYMCBgGG3GWycDANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDDAZkYnJlcG8wHhcNMjMwMzEzMTkxMzE3WhcNMzMwMzEzMTkxNDU3WjARMQ8wDQYDVQQDDAZkYnJlcG8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDdvW00t9yMUtTD1RKFUM1/5kPyxCpvhgFohdxIlNf/MNBQ+IKnXvoEEYjezhWmUMCuc5b4u6ajOYqiB6gcScYQgNbyi+Arm4xT21IDanSqoYFQw51HxgBNMh8Il7uoaKK4ZrdZnLexAszvRK5HPHTZhTyC9Ry+XYmzgrKoDOI5Rf/DdZrn0f2OMY1RdjKpo9Ymnvd4fNtVhbJLPIsbbaZWVxS0HHmyIvlPb9e7Sn3PiXv9tqnDCY5WLVmtweNsQDB0YSJIwYj+5aoAX6vUFGmYktHsc1dwvd4rj/npVgtqLBJ6N/Jh5EUwhMSf9TEZiFxdnnExRQIbRPGMGEtilMG7AgMBAAEwDQYJKoZIhvcNAQELBQADggEBAK3kQ1VkQrzvSWvmXmazmNoA1ZiPzRDs1XhGUWxgsxzgPylr3dGBuqQbKvgnLUBQLSqlJHpI4fZflHswu1qrvVZYtekPcGef4WhcKAu2i1RwxrKa6RJQ1tRbrLuVYCzPv5p/DWgltWVn88aoLnqQn0SK/0PB/o4a4Cm7Kq2ZzCr1dACBr06LvOHsc7249OySmbG4HH+pLK6jVURhZ9VaObqAHe2FJBVVoIzURbdiRRURqumrIvbnpeaU1aFyg6ED5wTnXvmMPmVPt9F79mcB33bASO5wyu00X8t1hyN2Show2l2vxLACGUzVkTQt15s7uDLKE7qLmKSR3EuSGXWv3wA=" ], + "priority" : [ "100" ], + "algorithm" : [ "RSA-OAEP" ] + } + }, { "id" : "28ca0b6d-b2e2-4785-b04b-2391e6344e30", "name" : "aes-generated", "providerId" : "aes-generated", @@ -2077,18 +2095,6 @@ "priority" : [ "100" ], "algorithm" : [ "HS256" ] } - }, { - "id" : "2f53ccf3-37b0-4d34-83e7-ed497499ee51", - "name" : "rsa-enc-generated", - "providerId" : "rsa-enc-generated", - "subComponents" : { }, - "config" : { - "privateKey" : [ "MIIEowIBAAKCAQEA3b1tNLfcjFLUw9UShVDNf+ZD8sQqb4YBaIXcSJTX/zDQUPiCp176BBGI3s4VplDArnOW+LumozmKogeoHEnGEIDW8ovgK5uMU9tSA2p0qqGBUMOdR8YATTIfCJe7qGiiuGa3WZy3sQLM70SuRzx02YU8gvUcvl2Js4KyqAziOUX/w3Wa59H9jjGNUXYyqaPWJp73eHzbVYWySzyLG22mVlcUtBx5siL5T2/Xu0p9z4l7/bapwwmOVi1ZrcHjbEAwdGEiSMGI/uWqAF+r1BRpmJLR7HNXcL3eK4/56VYLaiwSejfyYeRFMITEn/UxGYhcXZ5xMUUCG0TxjBhLYpTBuwIDAQABAoIBAA4dwebcxkrH99Poa8+WkiE7JgaS9sahx9OBI2xwJANoIU2TpzGuNLQZ76uLgB+rPWZTD9Xm5a1iJjwOyQ9/937TzPCk91D0tpgcusRikb8jx/6TGB9acL4kBjYUVCCHr3BA2G75MKKGtJ2OMvAbCQSosZj+r2VDwYFEPUkV2jheE5JHSBkwyIRrus3JCwu8gu5fyCg9z8ljcxJxI5HIsi4v8Z21aCw/cLj7h5cMt44wCjQz4rOfYNBEFeHDtlfR1QtWKgjm4ZHHJbKrzf9b2kQXclziceEbSM0tYbROEXKi+s0Zc+z3HEG89vv0vfN400clmzzIAijKY6gg3pPRWdECgYEA+lnWYbSlXDMNYx6RBXm1RnlMUYIm4oy4/9ljgnoGJ6WCn3SjFkgaDtiKfGIG1BSB85r04pAPANgcWHf5tWDnq0ARvBVG0BX2bKd++7B3D4d3CRYKCwm88SslJXv9dfHVhq4+zViFPiUWwT20A72jCuUCvL88y5fh/YBecfdh+jECgYEA4r5RD0NB9dMaeg5/jk/GEHIo4Z9KLc6FrSoOFo2xFkPOy1sgDpDOiNtypuWvniO7k7Ose3DS3hlfTMsKzIW/CgQJ20+Y4cvBWDaOsRxfjj7w3d+jH5OSJdKKSzTrgLKc9ZhlRzVXy0J0hipIA6HG5kdVdLXmh85ITmf1CbJhE6sCgYBjPVeBNbXTHZ2x6/z62aslO5IoQVqetb/kE82hfDOSZcao5Ph9Lam+ttH2ynkAevykj4mBgi+gWwqpey2uW7KaLPSaxShj9kDQA3mP1fzsV/u0y1rB02Nlin/YIxVvOqU1FT9p8SwoXVVu1sHUNck62VtDbN9xqUx5S/ikXrclEQKBgQCoTssOwEcK+Vty9KYcdfy4onTUHZBLdjxl8Iyqkxy7QTQUYRznkvesQPDXEDGO+kk3dyx2KKZt9Hl4IFNww2quPZcvcuMx4DQxjbXXpA8OIIxcta95NepLJwA+mRai3nKCH1A2TlNP7pFeMa5o+8IPly3Ix2lKr4Wepa4PN5i1pwKBgCZ1QP6XAOERl9NznNmU0rXVcvYNP4PIIfQWfvGsldZ4QKkmjjAGiS0/oYqdWs+UDRZyCRChaVjDXO9fk0PEG5OGKAj9nyiYCT/M8xtJ3UeP5ffZZvJ/vnye3QdDIo1e38ZzsWwJHmLYw7fRqY9W5Vxo0Vsy22U3CJY70KTxVdTy" ], - "keyUse" : [ "ENC" ], - "certificate" : [ "MIICmzCCAYMCBgGG3GWycDANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDDAZkYnJlcG8wHhcNMjMwMzEzMTkxMzE3WhcNMzMwMzEzMTkxNDU3WjARMQ8wDQYDVQQDDAZkYnJlcG8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDdvW00t9yMUtTD1RKFUM1/5kPyxCpvhgFohdxIlNf/MNBQ+IKnXvoEEYjezhWmUMCuc5b4u6ajOYqiB6gcScYQgNbyi+Arm4xT21IDanSqoYFQw51HxgBNMh8Il7uoaKK4ZrdZnLexAszvRK5HPHTZhTyC9Ry+XYmzgrKoDOI5Rf/DdZrn0f2OMY1RdjKpo9Ymnvd4fNtVhbJLPIsbbaZWVxS0HHmyIvlPb9e7Sn3PiXv9tqnDCY5WLVmtweNsQDB0YSJIwYj+5aoAX6vUFGmYktHsc1dwvd4rj/npVgtqLBJ6N/Jh5EUwhMSf9TEZiFxdnnExRQIbRPGMGEtilMG7AgMBAAEwDQYJKoZIhvcNAQELBQADggEBAK3kQ1VkQrzvSWvmXmazmNoA1ZiPzRDs1XhGUWxgsxzgPylr3dGBuqQbKvgnLUBQLSqlJHpI4fZflHswu1qrvVZYtekPcGef4WhcKAu2i1RwxrKa6RJQ1tRbrLuVYCzPv5p/DWgltWVn88aoLnqQn0SK/0PB/o4a4Cm7Kq2ZzCr1dACBr06LvOHsc7249OySmbG4HH+pLK6jVURhZ9VaObqAHe2FJBVVoIzURbdiRRURqumrIvbnpeaU1aFyg6ED5wTnXvmMPmVPt9F79mcB33bASO5wyu00X8t1hyN2Show2l2vxLACGUzVkTQt15s7uDLKE7qLmKSR3EuSGXWv3wA=" ], - "priority" : [ "100" ], - "algorithm" : [ "RSA-OAEP" ] - } }, { "id" : "2293ff99-3c6d-46d1-8635-5e679d5b134a", "name" : "rsa-generated", @@ -2105,7 +2111,7 @@ "internationalizationEnabled" : false, "supportedLocales" : [ ], "authenticationFlows" : [ { - "id" : "cd829305-f1a9-463e-961e-4b60cfc81639", + "id" : "136de2cd-39b0-451f-9b5b-0596a6e703ba", "alias" : "Account verification options", "description" : "Method with which to verity the existing account", "providerId" : "basic-flow", @@ -2127,7 +2133,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "69964d29-cfbe-42a8-8358-2ab0f1dc7fb1", + "id" : "6d041452-0e17-4c2f-8dc1-077ae4bc0d15", "alias" : "Authentication Options", "description" : "Authentication options.", "providerId" : "basic-flow", @@ -2156,7 +2162,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "ae17e1f4-6e9e-4490-91a3-2d2082cfe506", + "id" : "a124f3c8-73e9-4ba9-bebc-8282aa0fad62", "alias" : "Browser - Conditional OTP", "description" : "Flow to determine if the OTP is required for the authentication", "providerId" : "basic-flow", @@ -2178,7 +2184,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "7fb4fa17-2ab9-4505-89d9-e179e970e06c", + "id" : "b989868b-effd-494b-90ce-72311208cd07", "alias" : "Direct Grant - Conditional OTP", "description" : "Flow to determine if the OTP is required for the authentication", "providerId" : "basic-flow", @@ -2200,7 +2206,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "027c55b1-301d-4a9f-9701-2e5b207002ff", + "id" : "3a1986ff-77b5-43ee-8ea5-e83ce4c0b052", "alias" : "First broker login - Conditional OTP", "description" : "Flow to determine if the OTP is required for the authentication", "providerId" : "basic-flow", @@ -2222,7 +2228,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "93543890-7922-452c-bb75-6948cb280adf", + "id" : "8ef32e4a-384c-4a0f-9ef5-6b6c6dd67e23", "alias" : "Handle Existing Account", "description" : "Handle what to do if there is existing account with same email/username like authenticated identity provider", "providerId" : "basic-flow", @@ -2244,7 +2250,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "646a0eba-748d-4f6a-a36c-ad16f50b8e0d", + "id" : "b800468d-d6f4-4e01-a143-f3b9a24767dd", "alias" : "Reset - Conditional OTP", "description" : "Flow to determine if the OTP should be reset or not. Set to REQUIRED to force.", "providerId" : "basic-flow", @@ -2266,7 +2272,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "65dd1d54-1050-44ef-882b-120d1afd61f1", + "id" : "1a25e553-ebc5-407a-b432-ffdeea36907d", "alias" : "User creation or linking", "description" : "Flow for the existing/non-existing user alternatives", "providerId" : "basic-flow", @@ -2289,7 +2295,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "1890c22c-42b3-4510-b2a9-4a0badee54e2", + "id" : "b799d43f-e8f4-4d6a-93e9-39f73e42e22a", "alias" : "Verify Existing Account by Re-authentication", "description" : "Reauthentication of existing account", "providerId" : "basic-flow", @@ -2311,7 +2317,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "6eb01183-1131-4448-9fa9-253b6bc1a980", + "id" : "6f1b9b5f-3d09-4b2e-ba42-237ca174f393", "alias" : "browser", "description" : "browser based authentication", "providerId" : "basic-flow", @@ -2347,7 +2353,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "7940d0bb-75fe-4e39-93d0-8f55bc63639f", + "id" : "2eddb88f-3c4c-4d2f-99ba-f2cba595b94a", "alias" : "clients", "description" : "Base authentication for clients", "providerId" : "client-flow", @@ -2383,7 +2389,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "6a0716fb-40f7-43c1-88aa-5efd0f6d2a47", + "id" : "509dd5c3-ef75-45be-8cad-6328eef3c94e", "alias" : "direct grant", "description" : "OpenID Connect Resource Owner Grant", "providerId" : "basic-flow", @@ -2412,7 +2418,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "0f824ffd-5bc0-4b0a-a1b5-146c5dee5db2", + "id" : "995b881c-71d0-4ac1-be36-ce7cf7a9779d", "alias" : "docker auth", "description" : "Used by Docker clients to authenticate against the IDP", "providerId" : "basic-flow", @@ -2427,7 +2433,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "615160e4-83ef-4e83-ad9a-8e629611ecf7", + "id" : "2c18ddd1-e304-4e7e-98f5-25f8abba945d", "alias" : "first broker login", "description" : "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account", "providerId" : "basic-flow", @@ -2450,7 +2456,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "e9aa07ba-7513-4ba6-ae57-b464a9196d25", + "id" : "7abb16cf-310d-465f-93ef-34349ffc42fb", "alias" : "forms", "description" : "Username, password, otp and other auth forms.", "providerId" : "basic-flow", @@ -2472,7 +2478,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "58aa0310-3f16-4f32-abb1-7b365ff22e48", + "id" : "c01ab73b-0bc7-4047-98a3-502f98e6cc74", "alias" : "http challenge", "description" : "An authentication flow based on challenge-response HTTP Authentication Schemes", "providerId" : "basic-flow", @@ -2494,7 +2500,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "ce6a9047-5de8-4075-8031-50273d228f8a", + "id" : "9270fad3-c2da-4d20-9d8d-c55c3bace9fc", "alias" : "registration", "description" : "registration flow", "providerId" : "basic-flow", @@ -2510,7 +2516,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "4484dc1e-2376-4c6e-bc40-90014b9b65e0", + "id" : "1c8a94b0-eb77-4201-aa7f-4411da843ba1", "alias" : "registration form", "description" : "registration form", "providerId" : "form-flow", @@ -2546,7 +2552,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "2791d2e6-9324-4eeb-ae46-e62e71260dd3", + "id" : "28b08679-329a-4555-a0a0-7396e398e5bb", "alias" : "reset credentials", "description" : "Reset credentials for a user if they forgot their password or something", "providerId" : "basic-flow", @@ -2582,7 +2588,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "2803e0bf-363d-43a6-97c2-704289670197", + "id" : "3e3a0a50-0850-4f48-b315-dcd9340b1c2a", "alias" : "saml ecp", "description" : "SAML ECP Profile Authentication Flow", "providerId" : "basic-flow", @@ -2598,13 +2604,13 @@ } ] } ], "authenticatorConfig" : [ { - "id" : "0b5b090c-8ea3-438b-a599-7bd30ebc4df8", + "id" : "2e6a1507-6515-4d2a-8462-a2517df9d1da", "alias" : "create unique user config", "config" : { "require.password.update.after.registration" : "false" } }, { - "id" : "e9a9cf38-92eb-4199-9017-d3e2383d2339", + "id" : "4b0c01c1-aa00-432c-a9c6-640881e71fb6", "alias" : "review profile config", "config" : { "update.profile.on.first.login" : "missing" diff --git a/dbrepo-data-service/pom.xml b/dbrepo-data-service/pom.xml index a64785635ee2171947000336bc099aae5a15d29c..114544101a3a50d8ccf0aa9c932271bf2988820b 100644 --- a/dbrepo-data-service/pom.xml +++ b/dbrepo-data-service/pom.xml @@ -40,8 +40,6 @@ <opencsv.version>5.7.1</opencsv.version> <super-csv.version>2.4.0</super-csv.version> <jsql.version>4.6</jsql.version> - <c3p0.version>0.9.5.5</c3p0.version> - <c3p0-hibernate.version>6.2.2.Final</c3p0-hibernate.version> <springdoc-openapi.version>2.1.0</springdoc-openapi.version> <hsqldb.version>2.7.2</hsqldb.version> <testcontainers.version>1.18.3</testcontainers.version> @@ -157,6 +155,22 @@ <artifactId>amqp-client</artifactId> <version>${rabbit-amqp-client.version}</version> </dependency> + <!-- Monitoring --> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-aop</artifactId> + </dependency> + <dependency> + <groupId>io.micrometer</groupId> + <artifactId>micrometer-registry-prometheus</artifactId> + <version>${micrometer.version}</version> + </dependency> + <dependency> + <groupId>io.micrometer</groupId> + <artifactId>micrometer-observation-test</artifactId> + <version>${micrometer.version}</version> + <scope>test</scope> + </dependency> <!-- Testing --> <dependency> <groupId>org.springframework.boot</groupId> diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/listener/DefaultListenerIntegrationTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/listener/DefaultListenerIntegrationTest.java new file mode 100644 index 0000000000000000000000000000000000000000..b7991ce5a393039fa622b8686063bc2e95151638 --- /dev/null +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/listener/DefaultListenerIntegrationTest.java @@ -0,0 +1,74 @@ +package at.tuwien.listener; + +import at.tuwien.BaseUnitTest; +import at.tuwien.annotations.MockOpensearch; +import at.tuwien.config.MariaDbConfig; +import at.tuwien.config.MariaDbContainerConfig; +import at.tuwien.exception.DatabaseNotFoundException; +import at.tuwien.service.DatabaseService; +import lombok.extern.log4j.Log4j2; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.amqp.core.Message; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.boot.test.system.CapturedOutput; +import org.springframework.boot.test.system.OutputCaptureExtension; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.testcontainers.containers.MariaDBContainer; +import org.testcontainers.containers.RabbitMQContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +import java.sql.SQLException; +import java.util.HashMap; +import java.util.List; + +import static at.tuwien.utils.RabbitMqUtils.buildMessage; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.when; + +@Log4j2 +@SpringBootTest +@ExtendWith({SpringExtension.class, OutputCaptureExtension.class}) +@Testcontainers +@MockOpensearch +public class DefaultListenerIntegrationTest extends BaseUnitTest { + + @MockBean + private DatabaseService databaseService; + + @Autowired + private DefaultListener defaultListener; + + @Container + private static RabbitMQContainer rabbitContainer = new RabbitMQContainer("rabbitmq:3.10"); + + @Container + private static MariaDBContainer<?> mariaDBContainer = MariaDbContainerConfig.getContainer(); + + @BeforeEach + public void beforeEach() throws SQLException { + /* metadata database */ + TABLE_1.setColumns(TABLE_1_COLUMNS); + DATABASE_1.setTables(List.of(TABLE_1, TABLE_2, TABLE_3)); + MariaDbConfig.dropAllDatabases(CONTAINER_1); + MariaDbConfig.createInitDatabase(CONTAINER_1, DATABASE_1); + } + + @Test + public void onMessage_succeeds(CapturedOutput output) throws DatabaseNotFoundException { + final Message request = buildMessage("dbrepo." + DATABASE_1_INTERNALNAME + "." + TABLE_1_INTERNALNAME, "{\"id\":4,\"date\":\"2023-10-03\",\"mintemp\":15.0,\"rainfall\":0.2}", new HashMap<>()); + + /* mock */ + when(databaseService.findByInternalName(DATABASE_1_INTERNALNAME)) + .thenReturn(DATABASE_1); + + /* test */ + defaultListener.onMessage(request); + assertTrue(output.getAll().contains("successfully inserted tuple")); + } + +} diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/listener/DefaultListenerUnitTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/listener/DefaultListenerUnitTest.java index 04aff250f5580a9ac1c68e68630acbc4eeca1f56..a366513a6840cc6e1713ab1deb2f2e23b98467b1 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/listener/DefaultListenerUnitTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/listener/DefaultListenerUnitTest.java @@ -1,14 +1,14 @@ package at.tuwien.listener; import at.tuwien.BaseUnitTest; -import at.tuwien.annotations.MockAmqp; import at.tuwien.annotations.MockOpensearch; +import at.tuwien.config.MariaDbConfig; import at.tuwien.config.MariaDbContainerConfig; import lombok.extern.log4j.Log4j2; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.amqp.core.Message; -import org.springframework.amqp.core.MessageProperties; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.system.CapturedOutput; @@ -19,10 +19,10 @@ import org.testcontainers.containers.RabbitMQContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; -import java.nio.charset.StandardCharsets; +import java.sql.SQLException; import java.util.HashMap; -import java.util.Map; +import static at.tuwien.utils.RabbitMqUtils.buildMessage; import static org.junit.jupiter.api.Assertions.assertTrue; @Log4j2 @@ -38,6 +38,16 @@ public class DefaultListenerUnitTest extends BaseUnitTest { @Container private static RabbitMQContainer rabbitContainer = new RabbitMQContainer("rabbitmq:3.10"); + @Container + private static MariaDBContainer<?> mariaDBContainer = MariaDbContainerConfig.getContainer(); + + @BeforeEach + public void beforeEach() throws SQLException { + /* metadata database */ + MariaDbConfig.dropAllDatabases(CONTAINER_1); + MariaDbConfig.createInitDatabase(CONTAINER_1, DATABASE_1); + } + @Test public void onMessage_routingKeyDatabaseAndTableMissing_fails(CapturedOutput output) { final Message request = buildMessage("dbrepo", "{}", new HashMap<>()); @@ -65,11 +75,13 @@ public class DefaultListenerUnitTest extends BaseUnitTest { assertTrue(output.getAll().contains("Failed to read object")); } - protected Message buildMessage(String routingKey, String payload, Map<String, Object> headers) { - final MessageProperties properties = new MessageProperties(); - properties.setReceivedRoutingKey(routingKey); - properties.setHeaders(headers); - return new Message(payload.getBytes(StandardCharsets.UTF_8), properties); + @Test + public void onMessage_databaseNotFound_fails(CapturedOutput output) { + final Message request = buildMessage("dbrepo.database.table", "{\"id\":1}", new HashMap<>()); + + /* test */ + defaultListener.onMessage(request); + assertTrue(output.getAll().contains("Failed to find database")); } } diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/mvc/ActuatorEndpointMvcTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/mvc/ActuatorEndpointMvcTest.java new file mode 100644 index 0000000000000000000000000000000000000000..11d52c79efd91d66aec071b20d9b7f409d507dd0 --- /dev/null +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/mvc/ActuatorEndpointMvcTest.java @@ -0,0 +1,50 @@ +package at.tuwien.mvc; + +import at.tuwien.BaseUnitTest; +import at.tuwien.annotations.MockAmqp; +import at.tuwien.annotations.MockOpensearch; +import lombok.extern.log4j.Log4j2; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.actuate.observability.AutoConfigureObservability; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.web.servlet.MockMvc; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@Log4j2 +@ExtendWith(SpringExtension.class) +@AutoConfigureMockMvc +@SpringBootTest +@AutoConfigureObservability +@MockAmqp +@MockOpensearch +public class ActuatorEndpointMvcTest extends BaseUnitTest { + + @Autowired + private MockMvc mockMvc; + + @Test + public void actuatorInfo_succeeds() throws Exception { + + /* test */ + this.mockMvc.perform(get("/actuator/info")) + .andDo(print()) + .andExpect(status().isOk()); + } + + @Test + public void actuatorPrometheus_succeeds() throws Exception { + + /* test */ + this.mockMvc.perform(get("/actuator/prometheus")) + .andDo(print()) + .andExpect(status().isOk()); + } + +} diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/mvc/PrometheusEndpointMvcTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/mvc/PrometheusEndpointMvcTest.java new file mode 100644 index 0000000000000000000000000000000000000000..0781569eb8aff0e5aa91b1df6381dd10945f35c4 --- /dev/null +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/mvc/PrometheusEndpointMvcTest.java @@ -0,0 +1,78 @@ +package at.tuwien.mvc; + +import at.tuwien.BaseUnitTest; +import at.tuwien.annotations.MockAmqp; +import at.tuwien.annotations.MockOpensearch; +import at.tuwien.config.MetricsConfig; +import at.tuwien.listener.DefaultListener; +import io.micrometer.observation.tck.TestObservationRegistry; +import lombok.extern.log4j.Log4j2; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.amqp.core.Message; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.actuate.observability.AutoConfigureObservability; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.web.servlet.MockMvc; + +import java.util.HashMap; + +import static at.tuwien.utils.RabbitMqUtils.buildMessage; +import static io.micrometer.observation.tck.TestObservationRegistryAssert.assertThat; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@Log4j2 +@ExtendWith(SpringExtension.class) +@AutoConfigureMockMvc +@SpringBootTest +@Import(MetricsConfig.class) +@AutoConfigureObservability +@MockOpensearch +public class PrometheusEndpointMvcTest extends BaseUnitTest { + + @Autowired + private MockMvc mockMvc; + + @Autowired + private TestObservationRegistry registry; + + @Autowired + private DefaultListener defaultListener; + + @TestConfiguration + static class ObservationTestConfiguration { + + @Bean + public TestObservationRegistry observationRegistry() { + return TestObservationRegistry.create(); + } + } + + @Test + public void prometheus_succeeds() throws Exception { + + /* test */ + this.mockMvc.perform(get("/actuator/prometheus")) + .andDo(print()) + .andExpect(status().isOk()); + } + + @Test + public void prometheusMessageReceiveExists_succeeds() { + + /* mock */ + defaultListener.onMessage(buildMessage("dbrepo.database", "{}", new HashMap<>())); + + /* test */ + assertThat(registry) + .hasObservationWithNameEqualTo("dbr_message_receive"); + } + +} diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/mvc/SwaggerEndpointMvcTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/mvc/SwaggerEndpointMvcTest.java new file mode 100644 index 0000000000000000000000000000000000000000..c07446eee592ebdb8d2b7d7e631fdc9f10eca927 --- /dev/null +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/mvc/SwaggerEndpointMvcTest.java @@ -0,0 +1,37 @@ +package at.tuwien.mvc; + +import at.tuwien.BaseUnitTest; +import at.tuwien.annotations.MockAmqp; +import at.tuwien.annotations.MockOpensearch; +import lombok.extern.log4j.Log4j2; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.web.servlet.MockMvc; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@Log4j2 +@ExtendWith(SpringExtension.class) +@AutoConfigureMockMvc +@SpringBootTest +@MockAmqp +@MockOpensearch +public class SwaggerEndpointMvcTest extends BaseUnitTest { + + @Autowired + private MockMvc mockMvc; + + @Test + public void swaggerUi_succeeds() throws Exception { + this.mockMvc.perform(get("/swagger-ui/index.html")) + .andDo(print()) + .andExpect(status().isOk()); + } + +} diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/DatabaseServiceIntegrationTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/DatabaseServiceIntegrationTest.java new file mode 100644 index 0000000000000000000000000000000000000000..55feb01c14091d0d1f0dec7d1a492a7bf95adcca --- /dev/null +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/DatabaseServiceIntegrationTest.java @@ -0,0 +1,103 @@ +package at.tuwien.service; + +import at.tuwien.BaseUnitTest; +import at.tuwien.annotations.MockAmqp; +import at.tuwien.annotations.MockOpensearch; +import at.tuwien.entities.database.Database; +import at.tuwien.entities.user.User; +import at.tuwien.exception.DatabaseNotFoundException; +import at.tuwien.exception.UserNotFoundException; +import at.tuwien.repository.mdb.ContainerRepository; +import at.tuwien.repository.mdb.DatabaseRepository; +import at.tuwien.repository.mdb.ImageRepository; +import at.tuwien.repository.mdb.UserRepository; +import lombok.extern.log4j.Log4j2; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.testcontainers.junit.jupiter.Testcontainers; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +@Log4j2 +@SpringBootTest +@ExtendWith(SpringExtension.class) +@Testcontainers +@MockAmqp +@MockOpensearch +public class DatabaseServiceIntegrationTest extends BaseUnitTest { + + @Autowired + private UserRepository userRepository; + + @Autowired + private ImageRepository imageRepository; + + @Autowired + private ContainerRepository containerRepository; + + @Autowired + private DatabaseRepository databaseRepository; + + @Autowired + private DatabaseService databaseService; + + @BeforeEach + public void beforeEach() { + userRepository.save(USER_1); + imageRepository.save(IMAGE_1_SIMPLE); + containerRepository.save(CONTAINER_1_SIMPLE); + databaseRepository.save(DATABASE_1_SIMPLE); + } + + @Test + public void find_succeeds() throws DatabaseNotFoundException { + + /* test */ + final Database response = databaseService.find(DATABASE_1_ID); + assertEquals(DATABASE_1_ID, response.getId()); + } + + @Test + public void find_fails() { + + /* test */ + assertThrows(DatabaseNotFoundException.class, () -> { + databaseService.find(DATABASE_2_ID); + }); + } + + @Test + public void findByInternalName_succeeds() throws DatabaseNotFoundException { + + /* test */ + final Database response = databaseService.findByInternalName(DATABASE_1_INTERNALNAME); + assertEquals(DATABASE_1_ID, response.getId()); + } + + @Test + public void findByInternalName_fails() { + + /* test */ + assertThrows(DatabaseNotFoundException.class, () -> { + databaseService.findByInternalName(DATABASE_2_INTERNALNAME); + }); + } + + @Test + public void findAll_succeeds() { + + /* test */ + final List<Database> response = databaseService.findAll(); + assertEquals(1, response.size()); + final Database database0 = response.get(0); + assertEquals(DATABASE_1_ID, database0.getId()); + } + +} diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/QueueServiceIntegrationTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/QueueServiceIntegrationTest.java index 7fb17a5d3be2d49720feaabd804ad6a0cad2c229..669ae322bb7a1256fa4dfa9eeed4a073abe568d8 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/QueueServiceIntegrationTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/QueueServiceIntegrationTest.java @@ -8,6 +8,7 @@ import at.tuwien.exception.DatabaseNotFoundException; import at.tuwien.exception.QueryMalformedException; import at.tuwien.exception.TableNotFoundException; import at.tuwien.repository.mdb.*; +import at.tuwien.service.impl.QueueServiceImpl; import lombok.extern.log4j.Log4j2; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -16,7 +17,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.testcontainers.containers.MariaDBContainer; -import org.testcontainers.containers.RabbitMQContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; @@ -26,6 +26,8 @@ import java.util.List; import java.util.Map; import java.util.stream.Stream; +import static org.junit.jupiter.api.Assertions.assertThrows; + @Log4j2 @SpringBootTest @ExtendWith(SpringExtension.class) @@ -52,7 +54,7 @@ public class QueueServiceIntegrationTest extends BaseUnitTest { private ImageRepository imageRepository; @Autowired - private QueueService queueService; + private QueueServiceImpl queueService; @Container private static MariaDBContainer<?> mariaDBContainer = MariaDbContainerConfig.getContainer(); @@ -63,16 +65,16 @@ public class QueueServiceIntegrationTest extends BaseUnitTest { MariaDbConfig.createInitDatabase(CONTAINER_1, DATABASE_1); /* metadata database */ imageRepository.save(IMAGE_1); - userRepository.saveAll(List.of(USER_1, USER_2, USER_3, USER_4, USER_5)); - containerRepository.saveAll(List.of(CONTAINER_1, CONTAINER_2)); - databaseRepository.saveAll(List.of(DATABASE_1, DATABASE_2)); - tableRepository.saveAll(List.of(TABLE_1, TABLE_2, TABLE_3, TABLE_4, TABLE_5, TABLE_6, TABLE_7)); - tableColumnRepository.saveAll(Stream.of(TABLE_1_COLUMNS, TABLE_2_COLUMNS, TABLE_3_COLUMNS, TABLE_4_COLUMNS, TABLE_5_COLUMNS, TABLE_6_COLUMNS, TABLE_7_COLUMNS).flatMap(List::stream).toList()); + userRepository.save(USER_1); + containerRepository.save(CONTAINER_1_SIMPLE); + databaseRepository.save(DATABASE_1_SIMPLE); + tableRepository.save(TABLE_1_SIMPLE); + tableColumnRepository.saveAll(TABLE_1_COLUMNS); } @Test - public void insert_succeeds() throws TableNotFoundException, QueryMalformedException, DatabaseNotFoundException, - InterruptedException { + public void insert_succeeds() throws TableNotFoundException, DatabaseNotFoundException, InterruptedException, + SQLException { final Map<String, Object> request = new HashMap<>() {{ put("id", 4L); put("date", "2023-10-03"); @@ -89,8 +91,8 @@ public class QueueServiceIntegrationTest extends BaseUnitTest { } @Test - public void insert_onlyMandatoryFields_succeeds() throws TableNotFoundException, QueryMalformedException, - DatabaseNotFoundException, InterruptedException { + public void insert_onlyMandatoryFields_succeeds() throws TableNotFoundException, DatabaseNotFoundException, + InterruptedException, SQLException { final Map<String, Object> request = new HashMap<>() {{ put("id", 5L); put("date", "2023-10-04"); @@ -103,4 +105,28 @@ public class QueueServiceIntegrationTest extends BaseUnitTest { queueService.insert(DATABASE_1_INTERNALNAME, TABLE_1_INTERNALNAME, request); } + @Test + public void insert_databaseNotExists_fails() throws InterruptedException { + + /* pre-condition */ + Thread.sleep(1000) /* wait for test container some more */; + + /* test */ + assertThrows(DatabaseNotFoundException.class, () -> { + queueService.insert("not_exists", TABLE_1_INTERNALNAME, new HashMap<>()); + }); + } + + @Test + public void insert_tableNotExists_fails() throws InterruptedException { + + /* pre-condition */ + Thread.sleep(1000) /* wait for test container some more */; + + /* test */ + assertThrows(TableNotFoundException.class, () -> { + queueService.insert(DATABASE_1_INTERNALNAME, "not_exists", new HashMap<>()); + }); + } + } diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/UserServiceIntegrationTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/UserServiceIntegrationTest.java new file mode 100644 index 0000000000000000000000000000000000000000..3e3cff725d951850bfaa128f7c52a340f2afc4be --- /dev/null +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/service/UserServiceIntegrationTest.java @@ -0,0 +1,70 @@ +package at.tuwien.service; + +import at.tuwien.BaseUnitTest; +import at.tuwien.annotations.MockAmqp; +import at.tuwien.annotations.MockOpensearch; +import at.tuwien.config.MariaDbConfig; +import at.tuwien.config.MariaDbContainerConfig; +import at.tuwien.entities.user.User; +import at.tuwien.exception.DatabaseNotFoundException; +import at.tuwien.exception.QueryMalformedException; +import at.tuwien.exception.TableNotFoundException; +import at.tuwien.exception.UserNotFoundException; +import at.tuwien.repository.mdb.*; +import lombok.extern.log4j.Log4j2; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.testcontainers.containers.MariaDBContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +import java.sql.SQLException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +@Log4j2 +@SpringBootTest +@ExtendWith(SpringExtension.class) +@Testcontainers +@MockAmqp +@MockOpensearch +public class UserServiceIntegrationTest extends BaseUnitTest { + + @Autowired + private UserRepository userRepository; + + @Autowired + private UserService userService; + + @BeforeEach + public void beforeEach() { + userRepository.save(USER_1); + } + + @Test + public void findByUsername_succeeds() throws UserNotFoundException { + + /* test */ + final User response = userService.findByUsername(USER_1_USERNAME); + assertEquals(USER_1_ID, response.getId()); + } + + @Test + public void findByUsername_fails() { + + /* test */ + assertThrows(UserNotFoundException.class, () -> { + userService.findByUsername(USER_2_USERNAME); + }); + } + +} diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/utils/RabbitMqUtils.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/utils/RabbitMqUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..636ae4db745260e176840e17741f57b46fe40680 --- /dev/null +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/utils/RabbitMqUtils.java @@ -0,0 +1,17 @@ +package at.tuwien.utils; + +import org.springframework.amqp.core.Message; +import org.springframework.amqp.core.MessageProperties; + +import java.nio.charset.StandardCharsets; +import java.util.Map; + +public class RabbitMqUtils { + + public static Message buildMessage(String routingKey, String payload, Map<String, Object> headers) { + final MessageProperties properties = new MessageProperties(); + properties.setReceivedRoutingKey(routingKey); + properties.setHeaders(headers); + return new Message(payload.getBytes(StandardCharsets.UTF_8), properties); + } +} diff --git a/dbrepo-data-service/rest-service/src/test/resources/application.properties b/dbrepo-data-service/rest-service/src/test/resources/application.properties index 11033e17a88c840c3e78c6ec70ed61d3c6449e75..54ad577192b2c4b01404e786edd7b89de1234d7e 100644 --- a/dbrepo-data-service/rest-service/src/test/resources/application.properties +++ b/dbrepo-data-service/rest-service/src/test/resources/application.properties @@ -19,7 +19,9 @@ spring.sql.init.schema-locations=classpath*:init/schema.sql spring.jpa.hibernate.ddl-auto=create # log -logging.level.org.hibernate.SQL=trace +logging.level.at.tuwien.=debug +logging.level.at.tuwien.service.impl.QueueServiceImpl=trace +logging.level.at.tuwien.listener.DefaultListener=trace # rabbitmq spring.rabbitmq.host=localhost diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/api/CachedConnection.java b/dbrepo-data-service/services/src/main/java/at/tuwien/api/CachedConnection.java deleted file mode 100644 index d2da9237118ca5f7be0b9e0f9f677d243c3d0701..0000000000000000000000000000000000000000 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/api/CachedConnection.java +++ /dev/null @@ -1,37 +0,0 @@ -package at.tuwien.api; - -import at.tuwien.entities.database.Database; -import at.tuwien.entities.database.table.Table; -import at.tuwien.exception.TableNotFoundException; -import com.mchange.v2.c3p0.ComboPooledDataSource; -import lombok.Builder; -import lombok.Getter; -import lombok.Setter; - -import java.time.Instant; -import java.util.Optional; - -@Getter -@Setter -@Builder -public class CachedConnection { - - private ComboPooledDataSource dataSource; - - private Database database; - - private Instant lastUsed; - - public Table getTable(String internalName) throws TableNotFoundException { - final Optional<Table> optional = database.getTables() - .stream() - .filter(t -> t.getInternalName().equals(internalName)) - .findFirst(); - if (optional.isEmpty()) { - /* can never happen */ - throw new TableNotFoundException("Failed to find table with internal name " + internalName); - } - return optional.get(); - } - -} diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/config/MetricsConfig.java b/dbrepo-data-service/services/src/main/java/at/tuwien/config/MetricsConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..450be2f7df8b52fe493dd498dc0422350bb3ff39 --- /dev/null +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/config/MetricsConfig.java @@ -0,0 +1,15 @@ +package at.tuwien.config; + +import io.micrometer.observation.ObservationRegistry; +import io.micrometer.observation.aop.ObservedAspect; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class MetricsConfig { + + @Bean + public ObservedAspect observedAspect(ObservationRegistry observationRegistry) { + return new ObservedAspect(observationRegistry); + } +} diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/listener/DefaultListener.java b/dbrepo-data-service/services/src/main/java/at/tuwien/listener/DefaultListener.java index 01b3722ada9449a06cf230832d3768596251c26c..272a636e6087d99d9ac16393e907ab5115fc4b2a 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/listener/DefaultListener.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/listener/DefaultListener.java @@ -6,6 +6,7 @@ import at.tuwien.exception.TableNotFoundException; import at.tuwien.service.QueueService; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; +import io.micrometer.observation.annotation.Observed; import lombok.extern.log4j.Log4j2; import org.springframework.amqp.core.Message; import org.springframework.amqp.core.MessageListener; @@ -15,6 +16,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.io.IOException; +import java.sql.SQLException; import java.util.HashMap; import java.util.Map; @@ -33,6 +35,7 @@ public class DefaultListener implements MessageListener { } @Override + @Observed(name = "dbr_message_receive") public void onMessage(Message message) { final MessageProperties properties = message.getMessageProperties(); final TypeReference<HashMap<String, Object>> typeRef = new TypeReference<>() { @@ -46,6 +49,7 @@ public class DefaultListener implements MessageListener { log.error("Failed to map database and table names from routing key: is not 3-part"); return; } + log.trace("received message with id {} and content length: {} bytes", message.getMessageProperties().getMessageId(), message.getMessageProperties().getContentLength()); final String database = parts[1]; final String table = parts[2]; final Map<String, Object> body; @@ -54,7 +58,7 @@ public class DefaultListener implements MessageListener { queueService.insert(database, table, body); } catch (IOException e) { log.error("Failed to read object: {}", e.getMessage()); - } catch (TableNotFoundException | QueryMalformedException | DatabaseNotFoundException e) { + } catch (TableNotFoundException | QueryMalformedException | DatabaseNotFoundException | SQLException e) { log.error("Failed to insert tuple: {}", e.getMessage()); } } diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/service/QueueService.java b/dbrepo-data-service/services/src/main/java/at/tuwien/service/QueueService.java index 6649047500d591617ea76f1492787f799f78fee2..29a2f47599838de3989b24d5bc7140b75b6234aa 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/service/QueueService.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/service/QueueService.java @@ -1,10 +1,10 @@ package at.tuwien.service; -import at.tuwien.exception.ContainerNotFoundException; import at.tuwien.exception.DatabaseNotFoundException; import at.tuwien.exception.QueryMalformedException; import at.tuwien.exception.TableNotFoundException; +import java.sql.SQLException; import java.util.Map; public interface QueueService { @@ -15,5 +15,5 @@ public interface QueueService { * @param table The table name. * @param data The data. */ - void insert(String database, String table, Map<String, Object> data) throws DatabaseNotFoundException, QueryMalformedException, TableNotFoundException; + void insert(String database, String table, Map<String, Object> data) throws DatabaseNotFoundException, QueryMalformedException, TableNotFoundException, SQLException; } diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/QueueServiceImpl.java b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/QueueServiceImpl.java index 6146c08828d7a232b826c833640808fbca4cdd3d..68f665f12bbf776b278f8bedd61010e5bc0718ce 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/QueueServiceImpl.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/service/impl/QueueServiceImpl.java @@ -1,10 +1,8 @@ package at.tuwien.service.impl; -import at.tuwien.api.CachedConnection; -import at.tuwien.config.RabbitConfig; import at.tuwien.entities.database.Database; +import at.tuwien.entities.database.table.Table; import at.tuwien.exception.DatabaseNotFoundException; -import at.tuwien.exception.QueryMalformedException; import at.tuwien.exception.TableNotFoundException; import at.tuwien.mapper.DataMapper; import at.tuwien.service.DatabaseService; @@ -12,83 +10,53 @@ import at.tuwien.service.QueueService; import com.mchange.v2.c3p0.ComboPooledDataSource; import lombok.extern.log4j.Log4j2; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; -import java.time.Instant; -import java.time.temporal.ChronoUnit; -import java.util.HashMap; import java.util.Map; +import java.util.Optional; @Log4j2 @Service public class QueueServiceImpl extends HibernateConnector implements QueueService { private final DataMapper dataMapper; - private final RabbitConfig rabbitMqConfig; private final DatabaseService databaseService; - private final Map<String, CachedConnection> cachedConnections; @Autowired - public QueueServiceImpl(DataMapper dataMapper, RabbitConfig rabbitMqConfig, DatabaseService databaseService) { + public QueueServiceImpl(DataMapper dataMapper, DatabaseService databaseService) { this.dataMapper = dataMapper; - this.rabbitMqConfig = rabbitMqConfig; this.databaseService = databaseService; - this.cachedConnections = new HashMap<>(); - } - - @Scheduled(fixedRate = 5000) - @Transactional(readOnly = true) - public void updateCachedConnections() { - final Instant threshold = Instant.now().minus(rabbitMqConfig.getConnectionTimeout(), ChronoUnit.MILLIS); - cachedConnections.entrySet() - .stream() - .filter(e -> e.getValue().getLastUsed().isAfter(threshold)) - .forEach(connection -> { - connection.getValue().getDataSource().close(); - cachedConnections.remove(connection.getKey()); - log.debug("connection for database {} expired", connection.getKey()); - }); } @Override @Transactional(readOnly = true) - public void insert(String database, String table, Map<String, Object> data) throws DatabaseNotFoundException, - QueryMalformedException, TableNotFoundException { - /* check if connection can be reused */ - final CachedConnection cachedConnection = getCachedConnection(database); - cachedConnection.setLastUsed(Instant.now()); + public void insert(String databaseInternalName, String tableInternalName, Map<String, Object> data) + throws DatabaseNotFoundException, TableNotFoundException, SQLException { + final Database database = databaseService.findByInternalName(databaseInternalName); + log.debug("found database with id {} for name {}", database.getId(), databaseInternalName); + final Optional<Table> optional = database.getTables() + .stream() + .filter(t -> t.getInternalName().equals(tableInternalName)) + .findFirst(); + if (optional.isEmpty()) { + log.error("Failed to insert tuple into table {}: the table does not exist in database with name {}", tableInternalName, databaseInternalName); + throw new TableNotFoundException("Failed to insert tuple into table " + tableInternalName + ": the table does not exist in database with name " + databaseInternalName); + } + final ComboPooledDataSource dataSource = getPrivilegedDataSource(database.getContainer().getImage(), + database.getContainer(), database); /* run query */ try { - final Connection connection = cachedConnection.getDataSource().getConnection(); - final PreparedStatement preparedStatement = dataMapper.rabbitMqTupleToInsertOrUpdateQuery(connection, cachedConnection.getTable(table), data); + final Connection connection = dataSource.getConnection(); + final PreparedStatement preparedStatement = dataMapper.rabbitMqTupleToInsertOrUpdateQuery(connection, optional.get(), data); preparedStatement.executeUpdate(); - } catch (SQLException e) { - log.error("Failed to insert/update tuple in database {}: {}", database, e.getMessage()); - throw new QueryMalformedException("Failed to insert/update tuple in database " + database, e); + log.trace("successfully inserted tuple"); + } finally { + dataSource.close(); } } - @Transactional(readOnly = true) - public CachedConnection getCachedConnection(String databaseInternalName) throws DatabaseNotFoundException { - if (this.cachedConnections.containsKey(databaseInternalName)) { - return this.cachedConnections.get(databaseInternalName); - } - /* create */ - final Database database = databaseService.findByInternalName(databaseInternalName); - final ComboPooledDataSource dataSource = getPrivilegedDataSource(database.getContainer().getImage(), - database.getContainer(), database); - final CachedConnection cachedConnection = CachedConnection.builder() - .dataSource(dataSource) - .database(database) - .lastUsed(Instant.now()) - .build(); - this.cachedConnections.put(databaseInternalName, cachedConnection); - log.info("Established connection and added database {} to cache pool", databaseInternalName); - return cachedConnection; - } } diff --git a/dbrepo-metadata-service/pom.xml b/dbrepo-metadata-service/pom.xml index 8cd08e853640a0b6c810c36b65a5c46d4cc425c0..c76bc5517788001521cf01d551573d0db6b044f5 100644 --- a/dbrepo-metadata-service/pom.xml +++ b/dbrepo-metadata-service/pom.xml @@ -151,10 +151,20 @@ <version>${c3p0-hibernate.version}</version> </dependency> <!-- Monitoring --> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-aop</artifactId> + </dependency> <dependency> <groupId>io.micrometer</groupId> <artifactId>micrometer-registry-prometheus</artifactId> - <scope>runtime</scope> + <version>${micrometer.version}</version> + </dependency> + <dependency> + <groupId>io.micrometer</groupId> + <artifactId>micrometer-observation-test</artifactId> + <version>${micrometer.version}</version> + <scope>test</scope> </dependency> <!-- Authentication --> <dependency> diff --git a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/QueryMapper.java b/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/QueryMapper.java index a96053ee95bd3e07c91b3d1a6217d702497d524d..3672082da6ca91345b20a71a668a40f335ed76b7 100644 --- a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/QueryMapper.java +++ b/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/QueryMapper.java @@ -72,9 +72,7 @@ public interface QueryMapper { String nowhitespace = WHITESPACE.matcher(data).replaceAll("_"); String normalized = Normalizer.normalize(nowhitespace, Normalizer.Form.NFD); String slug = NONLATIN.matcher(normalized).replaceAll(""); - final String name = slug.toLowerCase(Locale.ENGLISH); - log.trace("mapped name {} to name {}", data, name); - return name; + return slug.toLowerCase(Locale.ENGLISH); } default QueryResultDto resultListToQueryResultDto(List<TableColumn> columns, ResultSet result) throws SQLException { @@ -119,8 +117,16 @@ public interface QueryMapper { .build(); } - default PreparedStatement generateTemporaryTableSQL(Connection connection, Table table) throws QueryMalformedException { - final StringBuilder statement = new StringBuilder("CREATE TABLE `") + default void importCsvQuery(Connection connection, Table table, ImportDto csv) throws SQLException { + final Statement statement = connection.createStatement(); + final StringBuilder query0 = new StringBuilder("DROP TABLE IF EXISTS `") + .append(table.getDatabase().getInternalName()) + .append("`.`") + .append(table.getInternalName()) + .append("_temporary`;"); + log.trace("mapped drop temporary table statement: {}", query0); + statement.execute(query0.toString()); + final StringBuilder query1 = new StringBuilder("CREATE TABLE `") .append(table.getDatabase().getInternalName()) .append("`.`") .append(table.getInternalName()) @@ -130,33 +136,18 @@ public interface QueryMapper { .append("`.`") .append(table.getInternalName()) .append("`;"); - try { - final PreparedStatement pstmt = connection.prepareStatement(statement.toString()); - log.trace("mapped create table {} to prepared statement {}", table.getName(), pstmt); - return pstmt; - } catch (SQLException e) { - log.error("Failed to prepare statement {}, reason: {}", statement, e.getMessage()); - throw new QueryMalformedException("Failed to prepare statement", e); - } - } + log.trace("mapped create temporary table statement: {}", query1); + statement.execute(query1.toString()); + final String query2 = pathToRawInsertQuery(table, csv); + log.trace("mapped import csv statement: {}", query2); + statement.execute(query2.toString()); + final String query3 = generateInsertFromTemporaryTableSQL(table); + log.trace("mapped import table statement: {}", query3); + statement.execute(query3.toString()); - default PreparedStatement dropTemporaryTableSQL(Connection connection, Table table) throws QueryMalformedException { - final StringBuilder statement = new StringBuilder("DROP TABLE IF EXISTS `") - .append(table.getDatabase().getInternalName()) - .append("`.`") - .append(table.getInternalName()) - .append("_temporary`;"); - try { - final PreparedStatement pstmt = connection.prepareStatement(statement.toString()); - log.trace("mapped drop temporary table {} to prepared statement {}", table.getName(), pstmt); - return pstmt; - } catch (SQLException e) { - log.error("Failed to prepare statement {}, reason: {}", statement, e.getMessage()); - throw new QueryMalformedException("Failed to prepare statement", e); - } } - default PreparedStatement pathToRawInsertQuery(Connection connection, Table table, ImportDto data) throws QueryMalformedException { + default String pathToRawInsertQuery(Table table, ImportDto data) { final StringBuilder statement = new StringBuilder("LOAD DATA INFILE '/tmp/") .append(data.getLocation()) .append("' INTO TABLE `") @@ -204,14 +195,7 @@ public interface QueryMapper { statement.append(")") .append(set.length() != 0 ? (" SET " + set) : "") .append(";"); - try { - final PreparedStatement pstmt = connection.prepareStatement(statement.toString()); - log.trace("mapped drop temporary table {} to prepared statement {}", table.getName(), pstmt); - return pstmt; - } catch (SQLException e) { - log.error("Failed to prepare statement {}, reason: {}", statement, e.getMessage()); - throw new QueryMalformedException("Failed to prepare statement", e); - } + return statement.toString(); } default void columnToBoolSet(ImportDto data, TableColumn column, StringBuilder set) { @@ -871,8 +855,7 @@ public interface QueryMapper { return "`" + item.substring(idx + 1) + "`"; } - default PreparedStatement generateInsertFromTemporaryTableSQL(Connection connection, Table table) - throws QueryMalformedException { + default String generateInsertFromTemporaryTableSQL(Table table) { final StringBuilder statement = new StringBuilder("INSERT INTO `") .append(table.getDatabase().getInternalName()) .append("`.`") @@ -900,14 +883,7 @@ public interface QueryMapper { .append("`),"); statement.deleteCharAt(statement.length() - 1); statement.append(";"); - try { - final PreparedStatement pstmt = connection.prepareStatement(statement.toString()); - log.trace("mapped generate insert from temporary table {} to prepared statement {}", statement, pstmt); - return pstmt; - } catch (SQLException e) { - log.error("Failed to prepare statement {}, reason: {}", statement, e.getMessage()); - throw new QueryMalformedException("Failed to prepare statement", e); - } + return statement.toString(); } default void prepareStatementWithColumnTypeObject(PreparedStatement ps, TableColumnType columnType, int idx, Object value) throws SQLException { diff --git a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/TableMapper.java b/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/TableMapper.java index fbdb04f252d9a99e14ba3ccb0b81d92b230cc373..f63e9913ee605356b9bc7701557027b873f5b25b 100644 --- a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/TableMapper.java +++ b/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/TableMapper.java @@ -40,6 +40,7 @@ import org.mapstruct.Named; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; +import java.sql.Statement; import java.text.Normalizer; import java.util.*; import java.util.regex.Pattern; @@ -219,14 +220,14 @@ public interface TableMapper { @Mappings({ @Mapping(source = "table", target = "table"), @Mapping(target = "id", ignore = true), - @Mapping(target = "autoGenerated", expression = "java(data.getInternalName() == \"id\" && query.getGenerated())"), + @Mapping(target = "autoGenerated", expression = "java(data.getInternalName() == \"id\" && generatedSequence)"), @Mapping(source = "data.name", target = "name"), @Mapping(source = "data.internalName", target = "internalName"), @Mapping(source = "data.created", target = "created"), @Mapping(source = "data.dateFormat", target = "dateFormat"), @Mapping(source = "data.lastModified", target = "lastModified"), }) - TableColumn tableColumnToTableColumn(Table table, TableColumn data, TableCreateRawQuery query); + TableColumn tableColumnToTableColumn(Table table, TableColumn data, Boolean generatedSequence); @Named("internalMapping") default String nameToInternalName(String data) { @@ -239,9 +240,7 @@ public interface TableMapper { String normalized = Normalizer.normalize(nowhitespace, Normalizer.Form.NFD); String slug = NONLATIN.matcher(normalized).replaceAll("_") .replaceAll("-", "_"); - final String name = slug.toLowerCase(Locale.ENGLISH); - log.trace("mapped name {} to internal name {}", data, name); - return name; + return slug.toLowerCase(Locale.ENGLISH); } @Mappings({ @@ -249,6 +248,7 @@ public interface TableMapper { @Mapping(target = "columnType", source = "data.type"), @Mapping(target = "isNullAllowed", source = "data.nullAllowed"), @Mapping(target = "name", source = "data.name"), + @Mapping(target = "autoGenerated", expression = "java(false)"), @Mapping(target = "internalName", expression = "java(nameToInternalName(data.getName()))"), @Mapping(target = "dateFormat", expression = "java(dateFormatIdToContainerImageDate(data.getDfid(), image))"), }) @@ -332,15 +332,15 @@ public interface TableMapper { } /** - * Map the table to a create table query - * TODO for e.g. postgres image + * Map the table to a create table and eventual create sequence query. * * @param data The table - * @return The create table query + * @return True if a sequence has been generated, false otherwise. */ - default TableCreateRawQuery tableToCreateTableRawQuery(Connection connection, TableCreateDto data) + default Boolean tableToCreateTableRawQuery(Connection connection, TableCreateDto data) throws TableMalformedException, QueryMalformedException { - final StringBuilder query = new StringBuilder("CREATE TABLE `") + final StringBuilder sequence = new StringBuilder(); + final StringBuilder table = new StringBuilder("CREATE TABLE `") .append(nameToInternalName(data.getName())) .append("` ("); /* internal checks */ @@ -359,17 +359,24 @@ public interface TableMapper { .build(); log.trace("attempt to create id column {}", idColumn); if (data.getColumns().stream().anyMatch(c -> c.getName().equals("id"))) { - log.error("Cannot create id column, it already exists"); - throw new TableMalformedException("Cannot create id column"); + log.error("Cannot create id column: it already exists"); + throw new TableMalformedException("Cannot create id column: it already exists"); } + /* metadata */ final List<ColumnCreateDto> columns = new LinkedList<>(); columns.add(idColumn); columns.addAll(data.getColumns()); data.setColumns(columns); + /* data */ + final String sequenceName = tableCreateDtoToSequenceName(data); + log.debug("create sequence with name {}", sequenceName); + sequence.append("CREATE SEQUENCE `") + .append(sequenceName) + .append("` START WITH 1 INCREMENT BY 1 NOCACHE; "); } final int[] idx = {0}; for (ColumnCreateDto column : data.getColumns()) { - query.append(idx[0]++ > 0 ? ", " : "") + table.append(idx[0]++ > 0 ? ", " : "") .append("`") .append(nameToInternalName(column.getName())) .append("` ") @@ -382,7 +389,7 @@ public interface TableMapper { "id") ? " DEFAULT NEXTVAL(`" + tableCreateDtoToSequenceName(data) + "`)" : ""); } /* create primary key index */ - query.append(", PRIMARY KEY (") + table.append(", PRIMARY KEY (") .append(String.join(",", data.getColumns() .stream() .filter(c -> Objects.nonNull(c.getPrimaryKey())) @@ -396,7 +403,7 @@ public interface TableMapper { if (data.getConstraints().getUniques() != null) { /* create unique indices */ data.getConstraints().getUniques() - .forEach(u -> query.append(", ") + .forEach(u -> table.append(", ") .append("UNIQUE KEY (`") .append(u.stream().map(this::nameToInternalName).collect(Collectors.joining("`,`"))) .append("`)")); @@ -405,7 +412,7 @@ public interface TableMapper { /* create foreign key indices */ data.getConstraints().getForeignKeys() .forEach(fk -> { - query.append(", FOREIGN KEY (`") + table.append(", FOREIGN KEY (`") .append(fk.getColumns().stream().map(this::nameToInternalName).collect(Collectors.joining("`,`"))) .append("`) REFERENCES `") .append(nameToInternalName(fk.getReferencedTable())) @@ -413,33 +420,35 @@ public interface TableMapper { .append(fk.getReferencedColumns().stream().map(this::nameToInternalName).collect(Collectors.joining("`,`"))) .append("`)"); if (fk.getOnDelete() != null) { - query.append(" ON DELETE ").append(fk.getOnDelete()); + table.append(" ON DELETE ").append(fk.getOnDelete()); } if (fk.getOnUpdate() != null) { - query.append(" ON UPDATE ").append(fk.getOnUpdate()); + table.append(" ON UPDATE ").append(fk.getOnUpdate()); } }); } if (data.getConstraints().getChecks() != null) { /* create check constraints */ data.getConstraints().getChecks() - .forEach(ck -> query.append(", ") + .forEach(ck -> table.append(", ") .append("CHECK (") .append(ck) .append(")")); } } - query.append(") WITH SYSTEM VERSIONING;"); + table.append(") WITH SYSTEM VERSIONING;"); log.trace("create table query built with {} columns and system versioning", data.getColumns().size()); try { - final PreparedStatement pstmt = connection.prepareStatement(query.toString()); - log.trace("prepared create table statement {}", query); - return TableCreateRawQuery.builder() - .preparedStatement(pstmt) - .generated(!primaryColumnExists) - .build(); + final Statement statement = connection.createStatement(); + if (sequence.length() > 0) { + log.trace("mapped create sequence statement: {}", sequence); + statement.execute(sequence.toString()); + } + log.trace("mapped create table statement: {}", table); + statement.execute(table.toString()); + return !sequence.isEmpty(); } catch (SQLException e) { - log.error("Failed to prepare statement {}, reason: {}", query, e.getMessage()); + log.error("Failed to prepare statement {}, reason: {}", table, e.getMessage()); throw new QueryMalformedException("Failed to prepare statement", e); } } @@ -450,64 +459,22 @@ public interface TableMapper { return name; } - default PreparedStatement tableToCreateSequenceRawQuery(Connection connection, Database database, TableCreateDto data) - throws ImageNotSupportedException, QueryMalformedException { - if (!database.getContainer().getImage().getName().equals("mariadb")) { - log.error("Currently only MariaDB is supported"); - throw new ImageNotSupportedException("Currently only MariaDB is supported"); - } - final StringBuilder statement = new StringBuilder("CREATE SEQUENCE `") - .append(tableCreateDtoToSequenceName(data)) - .append("` START WITH 1 INCREMENT BY 1 NOCACHE;"); - try { - final PreparedStatement pstmt = connection.prepareStatement(statement.toString()); - log.trace("prepared create sequence statement {}", statement); - return pstmt; - } catch (SQLException e) { - log.error("Failed to prepare statement {}, reason: {}", statement, e.getMessage()); - throw new QueryMalformedException("Failed to prepare statement", e); - } - } - - default PreparedStatement tableToDropSequenceRawQuery(Connection connection, Database database, TableCreateDto data) - throws ImageNotSupportedException, QueryMalformedException { - if (!database.getContainer().getImage().getName().equals("mariadb")) { - log.error("Currently only MariaDB is supported"); - throw new ImageNotSupportedException("Currently only MariaDB is supported"); - } - final StringBuilder statement = new StringBuilder("DROP SEQUENCE `") - .append(tableCreateDtoToSequenceName(data)) - .append("`;"); - try { - final PreparedStatement pstmt = connection.prepareStatement(statement.toString()); - log.trace("prepared drop sequence statement {}", statement); - return pstmt; - } catch (SQLException e) { - log.error("Failed to prepare statement {}, reason: {}", statement, e.getMessage()); - throw new QueryMalformedException("Failed to prepare statement", e); - } - } - default PreparedStatement tableToCreateHistoryViewRawQuery(Connection connection, Table data) throws QueryMalformedException { final StringBuilder statement = new StringBuilder("CREATE VIEW `hs_") .append(data.getInternalName()) .append("` AS SELECT * FROM (SELECT "); final int[] idx = new int[]{0}; - final StringBuilder keys = new StringBuilder(); data.getColumns() .stream() .filter(c -> Objects.nonNull(c.getIsPrimaryKey())) .filter(TableColumn::getIsPrimaryKey) - .forEach(c -> keys.append(idx[0]++ > 0 ? "," : "") + .forEach(c -> statement.append(idx[0]++ > 0 ? "," : "") .append("`") .append(c.getInternalName()) .append("`")); - statement.append(keys) - .append(", ROW_START AS inserted_at, IF(ROW_END > NOW(), NULL, ROW_END) AS deleted_at, COUNT(*) as total FROM `") + statement.append(", ROW_START AS inserted_at, IF(ROW_END > NOW(), NULL, ROW_END) AS deleted_at, COUNT(*) as total FROM `") .append(data.getInternalName()) - .append("` FOR SYSTEM_TIME ALL GROUP BY ") - .append(keys) - .append(", inserted_at, deleted_at ORDER BY deleted_at DESC LIMIT 50) AS v ORDER BY v.inserted_at, v.deleted_at ASC"); + .append("` FOR SYSTEM_TIME ALL GROUP BY inserted_at, deleted_at ORDER BY deleted_at DESC LIMIT 50) AS v ORDER BY v.inserted_at, v.deleted_at ASC"); try { final PreparedStatement pstmt = connection.prepareStatement(statement.toString()); log.trace("prepared create sequence statement {}", statement); diff --git a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/ViewMapper.java b/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/ViewMapper.java index a864436151ec128c4a3effd1416a3d5baff04da2..ad8ee0e23eee9139485fcf9b014a11082cd040c6 100644 --- a/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/ViewMapper.java +++ b/dbrepo-metadata-service/repositories/src/main/java/at/tuwien/mapper/ViewMapper.java @@ -32,9 +32,7 @@ public interface ViewMapper { String nowhitespace = WHITESPACE.matcher(data).replaceAll("_"); String normalized = Normalizer.normalize(nowhitespace, Normalizer.Form.NFD); String slug = NONLATIN.matcher(normalized).replaceAll(""); - final String name = slug.toLowerCase(Locale.ENGLISH); - log.trace("mapped name {} to internal name {}", data, name); - return name; + return slug.toLowerCase(Locale.ENGLISH); } @Mappings({ diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/AccessEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/AccessEndpoint.java index cd0bc94d5865a493538444a88a9bbb74333c30ab..d3c26da5659dd1f6c84141a74854c76972c69c79 100644 --- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/AccessEndpoint.java +++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/AccessEndpoint.java @@ -10,6 +10,7 @@ import at.tuwien.mapper.DatabaseMapper; import at.tuwien.service.AccessService; import at.tuwien.utils.PrincipalUtil; import at.tuwien.utils.UserUtil; +import io.micrometer.observation.annotation.Observed; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; @@ -46,6 +47,7 @@ public class AccessEndpoint { @PostMapping("/{userId}") @Transactional + @Observed(name = "dbr_access_give") @PreAuthorize("hasAuthority('create-database-access')") @Operation(summary = "Give access to some database", security = @SecurityRequirement(name = "bearerAuth")) @ApiResponses(value = { @@ -89,6 +91,7 @@ public class AccessEndpoint { @PutMapping("/{userId}") @Transactional + @Observed(name = "dbr_access_modify") @PreAuthorize("hasAuthority('update-database-access')") @Operation(summary = "Modify access to some database", security = @SecurityRequirement(name = "bearerAuth")) @ApiResponses(value = { @@ -126,6 +129,7 @@ public class AccessEndpoint { @GetMapping @Transactional + @Observed(name = "dbr_access_check") @PreAuthorize("hasAuthority('check-database-access')") @Operation(summary = "Check access to some database", security = @SecurityRequirement(name = "bearerAuth")) @ApiResponses(value = { @@ -157,6 +161,7 @@ public class AccessEndpoint { @DeleteMapping("/{userId}") @Transactional + @Observed(name = "dbr_access_delete") @PreAuthorize("hasAuthority('delete-database-access')") @Operation(summary = "Revoke access to some database", security = @SecurityRequirement(name = "bearerAuth")) @ApiResponses(value = { diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/ContainerEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/ContainerEndpoint.java index a3d7b7f48b1fe458066e5a4a4d1dbef958abaaf7..e81247931d562290a34bad3591988f79de3b61d0 100644 --- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/ContainerEndpoint.java +++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/ContainerEndpoint.java @@ -14,6 +14,7 @@ import at.tuwien.service.UserService; import at.tuwien.service.impl.ContainerServiceImpl; import at.tuwien.utils.PrincipalUtil; import io.micrometer.core.annotation.Timed; +import io.micrometer.observation.annotation.Observed; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.Content; @@ -54,6 +55,7 @@ public class ContainerEndpoint { @GetMapping @Transactional(readOnly = true) + @Observed(name = "dbr_container_findall") @Operation(summary = "Find all containers") @ApiResponses(value = { @ApiResponse(responseCode = "200", @@ -76,6 +78,7 @@ public class ContainerEndpoint { @PostMapping @Transactional + @Observed(name = "dbr_container_create") @PreAuthorize("hasAuthority('create-container')") @Operation(summary = "Create container", security = @SecurityRequirement(name = "bearerAuth")) @ApiResponses(value = { @@ -118,6 +121,7 @@ public class ContainerEndpoint { @GetMapping("/{id}") @Transactional(readOnly = true) + @Observed(name = "dbr_container_find") @Operation(summary = "Find some container") @ApiResponses(value = { @ApiResponse(responseCode = "200", @@ -153,7 +157,7 @@ public class ContainerEndpoint { @DeleteMapping("/{id}") @Transactional - @Timed(value = "container.delete", description = "Time needed to delete the container") + @Observed(name = "dbr_container_delete") @PreAuthorize("hasAuthority('delete-container')") @Operation(summary = "Delete some container", security = @SecurityRequirement(name = "bearerAuth")) @ApiResponses(value = { diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/DatabaseEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/DatabaseEndpoint.java index 925a60168bf8fbea1c0ca933f0efb69129a64abb..bcd669c573f37062f918a69d66e166b16141a220 100644 --- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/DatabaseEndpoint.java +++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/DatabaseEndpoint.java @@ -14,6 +14,7 @@ import at.tuwien.service.*; import at.tuwien.utils.PrincipalUtil; import at.tuwien.utils.UserUtil; import io.micrometer.core.annotation.Timed; +import io.micrometer.observation.annotation.Observed; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.Content; @@ -68,7 +69,7 @@ public class DatabaseEndpoint { @GetMapping @Transactional(readOnly = true) - @Timed(value = "database.list", description = "Time needed to list the databases") + @Observed(name = "dbr_database_findall") @Operation(summary = "List databases") @ApiResponses(value = { @ApiResponse(responseCode = "200", @@ -100,7 +101,7 @@ public class DatabaseEndpoint { @RequestMapping(method = RequestMethod.HEAD) @Transactional(readOnly = true) - @Timed(value = "database.list", description = "Time needed to count the databases") + @Observed(name = "dbr_database_count") @Operation(summary = "Count databases") @ApiResponses(value = { @ApiResponse(responseCode = "200", @@ -135,7 +136,7 @@ public class DatabaseEndpoint { @PostMapping @Transactional(rollbackFor = Exception.class) @PreAuthorize("hasAuthority('create-database')") - @Timed(value = "database.create", description = "Time needed to create a database") + @Observed(name = "dbr_database_create") @Operation(summary = "Create database", security = @SecurityRequirement(name = "bearerAuth")) @ApiResponses(value = { @ApiResponse(responseCode = "201", @@ -205,7 +206,7 @@ public class DatabaseEndpoint { @PutMapping("/{id}/visibility") @Transactional @PreAuthorize("hasAuthority('modify-database-visibility')") - @Timed(value = "database.visibility", description = "Time needed to modify a database visibility") + @Observed(name = "dbr_database_visibility") @Operation(summary = "Update database", security = @SecurityRequirement(name = "bearerAuth")) @ApiResponses(value = { @ApiResponse(responseCode = "202", @@ -244,7 +245,7 @@ public class DatabaseEndpoint { @PutMapping("/{id}/transfer") @Transactional @PreAuthorize("hasAuthority('modify-database-owner')") - @Timed(value = "database.transfer", description = "Time needed to transfer a database ownership") + @Observed(name = "dbr_database_transfer") @Operation(summary = "Transfer database", security = @SecurityRequirement(name = "bearerAuth")) @ApiResponses(value = { @ApiResponse(responseCode = "202", @@ -282,7 +283,7 @@ public class DatabaseEndpoint { @GetMapping("/{id}") @Transactional(readOnly = true) - @Timed(value = "database.find", description = "Time needed to find a database") + @Observed(name = "dbr_database_find") @Operation(summary = "Find some database", security = @SecurityRequirement(name = "bearerAuth")) @ApiResponses(value = { @ApiResponse(responseCode = "200", @@ -322,7 +323,7 @@ public class DatabaseEndpoint { @DeleteMapping("/{id}") @Transactional(rollbackFor = Exception.class) @PreAuthorize("hasAuthority('delete-database')") - @Timed(value = "database.delete", description = "Time needed to delete a database") + @Observed(name = "dbr_database_delete") @Operation(summary = "Delete some database", security = @SecurityRequirement(name = "bearerAuth")) @ApiResponses(value = { @ApiResponse(responseCode = "201", diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/ExportEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/ExportEndpoint.java index 844667233a8ebe738e0313edacdd1978049c5b93..6c58d8509cfc45bd470addd602a291126110848e 100644 --- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/ExportEndpoint.java +++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/ExportEndpoint.java @@ -10,6 +10,7 @@ import at.tuwien.service.QueryService; import at.tuwien.utils.PrincipalUtil; import at.tuwien.utils.UserUtil; import io.micrometer.core.annotation.Timed; +import io.micrometer.observation.annotation.Observed; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; @@ -45,7 +46,7 @@ public class ExportEndpoint { @GetMapping @Transactional(readOnly = true) - @Timed(value = "table.export", description = "Time needed to export table data") + @Observed(name = "dbr_table_export") @Operation(summary = "Export table", security = @SecurityRequirement(name = "bearerAuth")) @ApiResponses(value = { @ApiResponse(responseCode = "201", diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/IdentifierEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/IdentifierEndpoint.java index 8687928087ee52c5bbc4513ed46999286eeacf0e..ef1006ed70994850ee6f8d32fd18b1c54336a09d 100644 --- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/IdentifierEndpoint.java +++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/IdentifierEndpoint.java @@ -15,6 +15,7 @@ import at.tuwien.service.UserService; import at.tuwien.utils.PrincipalUtil; import at.tuwien.utils.UserUtil; import io.micrometer.core.annotation.Timed; +import io.micrometer.observation.annotation.Observed; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.Content; @@ -58,7 +59,7 @@ public class IdentifierEndpoint { @GetMapping @Transactional(readOnly = true) - @Timed(value = "identifier.list", description = "Time needed to list the identifiers") + @Observed(name = "dbr_identifier_findall") @Operation(summary = "Find identifiers") @ApiResponses(value = { @ApiResponse(responseCode = "200", @@ -82,7 +83,7 @@ public class IdentifierEndpoint { @PostMapping @Transactional - @Timed(value = "identifier.create", description = "Time needed to create an identifier") + @Observed(name = "dbr_identifier_create") @PreAuthorize("hasAuthority('create-identifier') or hasAuthority('create-foreign-identifier')") @Operation(summary = "Create identifier", security = @SecurityRequirement(name = "bearerAuth")) @ApiResponses(value = { @@ -153,7 +154,7 @@ public class IdentifierEndpoint { } @GetMapping("/retrieve") - @Timed(value = "identifier.retrieve", description = "Retrieve person or organization metadata from identifier") + @Observed(name = "dbr_identifier_retrieve") @Operation(summary = "Retrieve metadata from identifier") @ApiResponses(value = { @ApiResponse(responseCode = "200", @@ -162,7 +163,7 @@ public class IdentifierEndpoint { mediaType = "application/json", schema = @Schema(implementation = IdentifierDto.class))}), }) - public ResponseEntity<ExternalMetadataDto> create(@NotNull @Valid @RequestParam String url) + public ResponseEntity<ExternalMetadataDto> retrieve(@NotNull @Valid @RequestParam String url) throws OrcidNotFoundException, RorNotFoundException, RemoteUnavailableException, DoiNotFoundException { return ResponseEntity.ok(metadataService.findByUrl(url)); } diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/ImageEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/ImageEndpoint.java index dc5305dd92ab6a36349e22f565138045f94979d2..6b98c6fb38b28f103d9c96495783d437a8c94182 100644 --- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/ImageEndpoint.java +++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/ImageEndpoint.java @@ -14,6 +14,7 @@ import at.tuwien.mapper.ImageMapper; import at.tuwien.service.impl.ImageServiceImpl; import at.tuwien.utils.PrincipalUtil; import io.micrometer.core.annotation.Timed; +import io.micrometer.observation.annotation.Observed; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.Content; @@ -53,7 +54,7 @@ public class ImageEndpoint { @GetMapping @Transactional(readOnly = true) - @Timed(value = "image.list", description = "Time needed to list the container images") + @Observed(name = "dbr_image_findall") @Operation(summary = "Find all images") @ApiResponses(value = { @ApiResponse(responseCode = "200", @@ -73,7 +74,7 @@ public class ImageEndpoint { @PostMapping @Transactional - @Timed(value = "image.create", description = "Time needed to create a container image") + @Observed(name = "dbr_image_create") @PreAuthorize("hasAuthority('create-image')") @Operation(summary = "Create image", security = @SecurityRequirement(name = "bearerAuth")) @ApiResponses(value = { @@ -120,7 +121,7 @@ public class ImageEndpoint { @GetMapping("/{id}") @Transactional(readOnly = true) - @Timed(value = "image.find", description = "Time needed to find a container image") + @Observed(name = "dbr_image_find") @Operation(summary = "Find some image") @ApiResponses(value = { @ApiResponse(responseCode = "200", @@ -145,7 +146,7 @@ public class ImageEndpoint { @PutMapping("/{id}") @Transactional - @Timed(value = "image.update", description = "Time needed to update a container image") + @Observed(name = "dbr_image_update") @PreAuthorize("hasAuthority('modify-image')") @Operation(summary = "Update some image", security = @SecurityRequirement(name = "bearerAuth")) @ApiResponses(value = { @@ -174,7 +175,7 @@ public class ImageEndpoint { @DeleteMapping("/{id}") @Transactional - @Timed(value = "image.delete", description = "Time needed to delete a container image") + @Observed(name = "dbr_image_delete") @PreAuthorize("hasAuthority('delete-image')") @Operation(summary = "Delete some image", security = @SecurityRequirement(name = "bearerAuth")) @ApiResponses(value = { diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/LicenseEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/LicenseEndpoint.java index 6663d1e89f8f850f99e881159c857d9a8603e65a..c7ad83c5f19ac25bec2cd86bdcbe5a311c0f06c4 100644 --- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/LicenseEndpoint.java +++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/LicenseEndpoint.java @@ -4,6 +4,7 @@ import at.tuwien.api.database.LicenseDto; import at.tuwien.mapper.LicenseMapper; import at.tuwien.service.LicenseService; import io.micrometer.core.annotation.Timed; +import io.micrometer.observation.annotation.Observed; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.Content; @@ -40,7 +41,7 @@ public class LicenseEndpoint { @GetMapping("/license") @Transactional(readOnly = true) - @Timed(value = "license.list", description = "Time needed to list the licenses") + @Observed(name = "dbr_license_findall") @Operation(summary = "Get all licenses") @ApiResponses(value = { @ApiResponse(responseCode = "200", diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/MaintenanceEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/MaintenanceEndpoint.java index 3dd488acc8277bdcfa5d60b1011f200f435815c2..b71109d62ba5cd75e113a9f3f6d89677f5bd3ae8 100644 --- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/MaintenanceEndpoint.java +++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/MaintenanceEndpoint.java @@ -7,6 +7,7 @@ import at.tuwien.api.maintenance.BannerMessageUpdateDto; import at.tuwien.exception.BannerMessageNotFoundException; import at.tuwien.mapper.BannerMessageMapper; import at.tuwien.service.BannerMessageService; +import io.micrometer.observation.annotation.Observed; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.Content; @@ -40,6 +41,7 @@ public class MaintenanceEndpoint { } @GetMapping("/message") + @Observed(name = "dbr_maintenance_findall") @Operation(summary = "Find maintenance messages") @ApiResponses(value = { @ApiResponse(responseCode = "200", @@ -59,6 +61,7 @@ public class MaintenanceEndpoint { } @GetMapping("/message/{id}") + @Observed(name = "dbr_maintenance_find") @Operation(summary = "Find one maintenance message") @ApiResponses(value = { @ApiResponse(responseCode = "200", @@ -76,6 +79,7 @@ public class MaintenanceEndpoint { } @GetMapping("/message/active") + @Observed(name = "dbr_maintenance_findactive") @Operation(summary = "Find active maintenance messages") @ApiResponses(value = { @ApiResponse(responseCode = "200", @@ -95,6 +99,7 @@ public class MaintenanceEndpoint { } @PostMapping("/message") + @Observed(name = "dbr_maintenance_create") @Operation(summary = "Create maintenance message") @PreAuthorize("hasAuthority('create-maintenance-message')") @ApiResponses(value = { @@ -113,6 +118,7 @@ public class MaintenanceEndpoint { } @PutMapping("/message/{id}") + @Observed(name = "dbr_maintenance_update") @Operation(summary = "Update maintenance message") @PreAuthorize("hasAuthority('update-maintenance-message')") @ApiResponses(value = { @@ -138,6 +144,7 @@ public class MaintenanceEndpoint { } @DeleteMapping("/message/{id}") + @Observed(name = "dbr_maintenance_delete") @Operation(summary = "Delete maintenance message") @PreAuthorize("hasAuthority('delete-maintenance-message')") @ApiResponses(value = { diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/MetadataEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/MetadataEndpoint.java index 568ab8a41676440430478db18b96dea0830b6fcc..6790db5c78df4a33c8b2fdbd2d1878c407efec16 100644 --- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/MetadataEndpoint.java +++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/MetadataEndpoint.java @@ -6,6 +6,7 @@ import at.tuwien.oaipmh.OaiListIdentifiersParameters; import at.tuwien.oaipmh.OaiRecordParameters; import at.tuwien.service.MetadataService; import io.micrometer.core.annotation.Timed; +import io.micrometer.observation.annotation.Observed; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.enums.ParameterIn; @@ -43,7 +44,7 @@ public class MetadataEndpoint { @ExampleObject(value = "GetRecord"), @ExampleObject(value = "ListMetadataFormats"), }) - @Timed(value = "repository.identify", description = "Time needed to identify the repository") + @Observed(name = "dbr_oai_identify") @Operation(summary = "Identify the repository") @ApiResponses(value = { @ApiResponse(responseCode = "200", @@ -56,7 +57,7 @@ public class MetadataEndpoint { } @GetMapping(params = "verb=Identify", produces = "text/xml;charset=UTF-8") - @Timed(value = "repository.identify", description = "Time needed to identify the repository") + @Observed(name = "dbr_oai_identify") @Operation(summary = "Identify the repository") @ApiResponses(value = { @ApiResponse(responseCode = "200", @@ -71,7 +72,7 @@ public class MetadataEndpoint { } @GetMapping(params = "verb=ListIdentifiers", produces = "text/xml;charset=UTF-8") - @Timed(value = "identifiers.list", description = "Time needed to list the identifiers") + @Observed(name = "dbr_oai_identifiers_list") @Operation(summary = "List the identifiers") public ResponseEntity<String> listIdentifiers(OaiListIdentifiersParameters parameters) { log.debug("endpoint list identifiers, verb=ListIdentifiers, parameters={}", parameters); @@ -81,7 +82,7 @@ public class MetadataEndpoint { } @GetMapping(params = "verb=GetRecord", produces = "text/xml;charset=UTF-8") - @Timed(value = "record.find", description = "Time needed to find a record") + @Observed(name = "dbr_oai_record_get") @Operation(summary = "Get the record") public ResponseEntity<String> getRecord(OaiRecordParameters parameters) { log.debug("endpoint get record, verb=GetRecord, parameters={}", parameters); @@ -116,7 +117,7 @@ public class MetadataEndpoint { } @GetMapping(params = "verb=ListMetadataFormats", produces = "text/xml;charset=UTF-8") - @Timed(value = "formats.list", description = "Time needed to list the metadata formats") + @Observed(name = "dbr_oai_metadataformats_list") @Operation(summary = "List the metadata formats") public ResponseEntity<String> listMetadataFormats() { log.debug("endpoint list metadata formats, verb=ListMetadataFormats"); diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/OntologyEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/OntologyEndpoint.java index 28f8d724da3a6938545532cdd18ea91f62fa5d90..dab0740a04654dcc86f18c5c99e8a56988e4a103 100644 --- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/OntologyEndpoint.java +++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/OntologyEndpoint.java @@ -9,6 +9,7 @@ import at.tuwien.service.EntityService; import at.tuwien.service.OntologyService; import at.tuwien.utils.PrincipalUtil; import io.micrometer.core.annotation.Timed; +import io.micrometer.observation.annotation.Observed; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.Content; @@ -46,7 +47,7 @@ public class OntologyEndpoint { } @GetMapping - @Timed(value = "semantics.ontology.list", description = "Time needed to list ontologies") + @Observed(name = "dbr_ontologies_findall") @Operation(summary = "List all ontologies") @ApiResponses(value = { @ApiResponse(responseCode = "200", @@ -66,7 +67,7 @@ public class OntologyEndpoint { } @GetMapping("/{id}") - @Timed(value = "semantics.ontology.find", description = "Time needed to find a specific ontology") + @Observed(name = "dbr_ontologies_find") @Operation(summary = "Find one ontology") @ApiResponses(value = { @ApiResponse(responseCode = "200", @@ -89,7 +90,7 @@ public class OntologyEndpoint { @PostMapping @PreAuthorize("hasAuthority('create-ontology')") - @Timed(value = "semantics.ontology.create", description = "Time needed to register a new ontology") + @Observed(name = "dbr_ontologies_create") @Operation(summary = "Register a new ontology", security = @SecurityRequirement(name = "bearerAuth")) @ApiResponses(value = { @ApiResponse(responseCode = "201", @@ -115,7 +116,7 @@ public class OntologyEndpoint { @PutMapping("/{id}") @PreAuthorize("hasAuthority('update-ontology')") - @Timed(value = "semantics.ontology.update", description = "Time needed to update a new ontology") + @Observed(name = "dbr_ontologies_update") @Operation(summary = "Update an ontology", security = @SecurityRequirement(name = "bearerAuth")) @ApiResponses(value = { @ApiResponse(responseCode = "202", @@ -141,7 +142,7 @@ public class OntologyEndpoint { @DeleteMapping("/{id}") @PreAuthorize("hasAuthority('delete-ontology')") - @Timed(value = "semantics.ontology.delete", description = "Time needed to delete an ontology") + @Observed(name = "dbr_ontologies_delete") @Operation(summary = "Delete an ontology", security = @SecurityRequirement(name = "bearerAuth")) @ApiResponses(value = { @ApiResponse(responseCode = "202", @@ -163,7 +164,7 @@ public class OntologyEndpoint { @GetMapping("/{id}/entity") @PreAuthorize("hasAuthority('execute-semantic-query')") - @Timed(value = "semantics.find", description = "Time needed to find entities") + @Observed(name = "dbr_ontologies_entities_find") @Operation(summary = "Find entities", security = @SecurityRequirement(name = "bearerAuth")) @ApiResponses(value = { @ApiResponse(responseCode = "200", diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/PersistenceEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/PersistenceEndpoint.java index 3284977e77735204bd3e21546a3f608fa400f843..0cd22a58a3c9ab11881d3372adb48c57d4c87665 100644 --- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/PersistenceEndpoint.java +++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/PersistenceEndpoint.java @@ -12,6 +12,7 @@ import at.tuwien.service.AccessService; import at.tuwien.service.IdentifierService; import at.tuwien.utils.UserUtil; import io.micrometer.core.annotation.Timed; +import io.micrometer.observation.annotation.Observed; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; @@ -56,7 +57,7 @@ public class PersistenceEndpoint { @GetMapping("/{pid}") @Transactional(readOnly = true) - @Timed(value = "pid.find", description = "Time needed to find a persisted identifier") + @Observed(name = "dbr_pid_find") @Operation(summary = "Find some identifier") @ApiResponses(value = { @ApiResponse(responseCode = "200", @@ -151,7 +152,7 @@ public class PersistenceEndpoint { @PutMapping("/{id}") @Transactional - @Timed(value = "identifier.update", description = "Time needed to update an identifier") + @Observed(name = "dbr_pid_update") @PreAuthorize("hasAuthority('modify-identifier-metadata')") @Operation(summary = "Update some identifier", security = @SecurityRequirement(name = "bearerAuth")) @ApiResponses(value = { @@ -207,7 +208,7 @@ public class PersistenceEndpoint { @DeleteMapping("/{id}") @Transactional - @Timed(value = "identifier.delete", description = "Time needed to delete an identifier") + @Observed(name = "dbr_pid_delete") @PreAuthorize("hasAuthority('delete-identifier')") @Operation(summary = "Delete some identifier", security = @SecurityRequirement(name = "bearerAuth")) @ApiResponses(value = { diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/QueryEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/QueryEndpoint.java index e312d1110b29bb368d7bbaf5cb4027d83aee7dcd..2eceec710136d91ddb996dee7d42c17a2b94bdba 100644 --- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/QueryEndpoint.java +++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/QueryEndpoint.java @@ -15,6 +15,7 @@ import at.tuwien.utils.PrincipalUtil; import at.tuwien.utils.UserUtil; import at.tuwien.validation.EndpointValidator; import io.micrometer.core.annotation.Timed; +import io.micrometer.observation.annotation.Observed; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.security.SecurityRequirement; import jakarta.validation.Valid; @@ -51,7 +52,7 @@ public class QueryEndpoint { @PostMapping @Transactional(readOnly = true) - @Timed(value = "query.execute", description = "Time needed to execute a query") + @Observed(name = "dbr_query_execute") @PreAuthorize("hasAuthority('execute-query')") @Operation(summary = "Execute query", security = @SecurityRequirement(name = "bearerAuth")) public ResponseEntity<QueryResultDto> execute(@NotNull @PathVariable("databaseId") Long databaseId, @@ -84,7 +85,7 @@ public class QueryEndpoint { @GetMapping("/{queryId}/data") @Transactional(readOnly = true) - @Timed(value = "query.reexecute", description = "Time needed to re-execute a query") + @Observed(name = "dbr_query_reexecute") @Operation(summary = "Re-execute some query", security = @SecurityRequirement(name = "bearerAuth")) public ResponseEntity<QueryResultDto> reExecute(@NotNull @PathVariable("databaseId") Long databaseId, @NotNull @PathVariable("queryId") Long queryId, @@ -112,7 +113,7 @@ public class QueryEndpoint { @GetMapping("/{queryId}/data/count") @Transactional(readOnly = true) - @Timed(value = "query.reexecute.count", description = "Time needed to re-execute a query") + @Observed(name = "dbr_query_reexecute_count") @Operation(summary = "Re-execute some query", security = @SecurityRequirement(name = "bearerAuth")) public ResponseEntity<Long> reExecuteCount(@NotNull @PathVariable("databaseId") Long databaseId, @NotNull @PathVariable("queryId") Long queryId, @@ -132,7 +133,7 @@ public class QueryEndpoint { @GetMapping("/{queryId}/export") @Transactional(readOnly = true) - @Timed(value = "query.export", description = "Time needed to export query data") + @Observed(name = "dbr_query_export") @Operation(summary = "Exports some query", security = @SecurityRequirement(name = "bearerAuth")) public ResponseEntity<?> export(@NotNull @PathVariable("databaseId") Long databaseId, @NotNull @PathVariable("queryId") Long queryId, diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/SemanticsEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/SemanticsEndpoint.java index bb2536451d7e27a237cf782876423f6671013241..f210fe1e527dd5822c0abb00a3e9f0a9d6f3307f 100644 --- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/SemanticsEndpoint.java +++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/SemanticsEndpoint.java @@ -16,6 +16,7 @@ import at.tuwien.mapper.SemanticMapper; import at.tuwien.service.EntityService; import at.tuwien.service.SemanticService; import io.micrometer.core.annotation.Timed; +import io.micrometer.observation.annotation.Observed; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.Content; @@ -56,7 +57,7 @@ public class SemanticsEndpoint { @GetMapping("/concept") @Transactional(readOnly = true) - @Timed(value = "semantics.concept.list", description = "Time needed to find all semantic concepts") + @Observed(name = "dbr_semantic_concepts_findall") @Operation(summary = "List semantic concepts") @ApiResponses(value = { @ApiResponse(responseCode = "200", @@ -79,7 +80,7 @@ public class SemanticsEndpoint { @PostMapping("/concept") @Transactional @PreAuthorize("hasAuthority('create-semantic-concept')") - @Timed(value = "semantics.concept.save", description = "Time needed to save a semantic concept") + @Observed(name = "dbr_semantic_concepts_save") @Operation(summary = "Create or update a semantic concept", security = @SecurityRequirement(name = "bearerAuth")) @ApiResponses(value = { @ApiResponse(responseCode = "202", @@ -88,7 +89,7 @@ public class SemanticsEndpoint { mediaType = "application/json", schema = @Schema(implementation = ConceptDto.class))}), }) - public ResponseEntity<ConceptDto> saveUnit(@NotNull @Valid @RequestBody ConceptSaveDto data) { + public ResponseEntity<ConceptDto> saveConcept(@NotNull @Valid @RequestBody ConceptSaveDto data) { log.debug("endpoint save concept, data={}", data); final ConceptDto dto = ontologyMapper.tableColumnConceptToConceptDto(semanticService.saveConcept(data)); log.trace("save concept resulted in dto {}", dto); @@ -98,7 +99,7 @@ public class SemanticsEndpoint { @GetMapping("/unit") @Transactional(readOnly = true) - @Timed(value = "semantics.concept.list", description = "Time needed to find all semantic units") + @Observed(name = "dbr_semantic_units_findall") @Operation(summary = "List semantic units") @ApiResponses(value = { @ApiResponse(responseCode = "200", @@ -121,7 +122,7 @@ public class SemanticsEndpoint { @PostMapping("/unit") @Transactional @PreAuthorize("hasAuthority('create-semantic-unit')") - @Timed(value = "semantics.unit.save", description = "Time needed to save a semantic unit") + @Observed(name = "dbr_semantic_units_save") @Operation(summary = "Save a semantic unit", security = @SecurityRequirement(name = "bearerAuth")) @ApiResponses(value = { @ApiResponse(responseCode = "202", @@ -141,7 +142,7 @@ public class SemanticsEndpoint { @GetMapping("/database/{databaseId}/table/{tableId}") @Transactional(readOnly = true) @PreAuthorize("hasAuthority('table-semantic-analyse')") - @Timed(value = "semantics.table.analyse", description = "Time needed to analyse table semantics") + @Observed(name = "dbr_semantic_table_analyse") @Operation(summary = "Suggest table semantics", security = @SecurityRequirement(name = "bearerAuth")) @ApiResponses(value = { @ApiResponse(responseCode = "200", @@ -173,7 +174,7 @@ public class SemanticsEndpoint { @GetMapping("/database/{databaseId}/table/{tableId}/column/{columnId}") @Transactional(readOnly = true) @PreAuthorize("hasAuthority('table-semantic-analyse')") - @Timed(value = "semantics.table.columnanalyse", description = "Time needed to analyse table column semantics") + @Observed(name = "dbr_semantic_column_analyse") @Operation(summary = "Suggest table column semantics", security = @SecurityRequirement(name = "bearerAuth")) @ApiResponses(value = { @ApiResponse(responseCode = "200", diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/StoreEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/StoreEndpoint.java index c23a662a6e11424cf936ac3faaf641f633e23a6a..435718e4573037f041d6c98c9ca2671ccfaa1c6e 100644 --- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/StoreEndpoint.java +++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/StoreEndpoint.java @@ -19,6 +19,7 @@ import at.tuwien.utils.PrincipalUtil; import at.tuwien.utils.UserUtil; import at.tuwien.validation.EndpointValidator; import io.micrometer.core.annotation.Timed; +import io.micrometer.observation.annotation.Observed; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.Content; @@ -71,7 +72,7 @@ public class StoreEndpoint { @GetMapping @Transactional(readOnly = true) - @Timed(value = "store.list", description = "Time needed to list queries from the query store") + @Observed(name = "dbr_queries_findall") @Operation(summary = "Find queries", security = @SecurityRequirement(name = "bearerAuth")) @ApiResponses(value = { @ApiResponse(responseCode = "200", @@ -138,7 +139,7 @@ public class StoreEndpoint { @GetMapping("/{queryId}") @Transactional(readOnly = true) - @Timed(value = "store.find", description = "Time needed to find a query from the query store") + @Observed(name = "dbr_queries_find") @Operation(summary = "Find some query", security = @SecurityRequirement(name = "bearerAuth")) @ApiResponses(value = { @ApiResponse(responseCode = "200", @@ -196,7 +197,7 @@ public class StoreEndpoint { @PutMapping("/{queryId}") @Transactional(readOnly = true) @PreAuthorize("hasAuthority('persist-query')") - @Timed(value = "store.persist", description = "Time needed to persist a query in the query store") + @Observed(name = "dbr_query_persist") @Operation(summary = "Persist some query", security = @SecurityRequirement(name = "bearerAuth")) @ApiResponses(value = { @ApiResponse(responseCode = "200", diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/TableColumnEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/TableColumnEndpoint.java index e69cb22ca2bc713f36fad4b0d5e97ccd34fc52f3..ebe9b7375d672f0d57fcae8e6927f5bdb74a23da 100644 --- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/TableColumnEndpoint.java +++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/TableColumnEndpoint.java @@ -11,6 +11,7 @@ import at.tuwien.utils.PrincipalUtil; import at.tuwien.utils.UserUtil; import at.tuwien.validation.EndpointValidator; import io.micrometer.core.annotation.Timed; +import io.micrometer.observation.annotation.Observed; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; @@ -48,7 +49,7 @@ public class TableColumnEndpoint { @PutMapping @Transactional @PreAuthorize("hasAuthority('modify-table-column-semantics') or hasAuthority('modify-foreign-table-column-semantics')") - @Timed(value = "semantics.column_update", description = "Time needed to update a table column semantic mapping") + @Observed(name = "dbr_semantics_column_save") @Operation(summary = "Update a table column semantic mapping", security = @SecurityRequirement(name = "bearerAuth")) @ApiResponses(value = { @ApiResponse(responseCode = "202", diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/TableDataEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/TableDataEndpoint.java index 7a75c659ee41316537fb7bdff292c42e0bf817b8..61fbb537ddf8169a3aedac513c888313b4db2a82 100644 --- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/TableDataEndpoint.java +++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/TableDataEndpoint.java @@ -14,6 +14,7 @@ import at.tuwien.utils.PrincipalUtil; import at.tuwien.utils.UserUtil; import at.tuwien.validation.EndpointValidator; import io.micrometer.core.annotation.Timed; +import io.micrometer.observation.annotation.Observed; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.security.SecurityRequirement; import jakarta.validation.Valid; @@ -48,7 +49,7 @@ public class TableDataEndpoint { @PostMapping @Transactional - @Timed(value = "data.insert", description = "Time needed to insert data into a table") + @Observed(name = "dbr_table_data_insert") @PreAuthorize("hasAuthority('insert-table-data')") @Operation(summary = "Insert data", security = @SecurityRequirement(name = "bearerAuth")) public ResponseEntity<Void> insert(@NotNull @PathVariable("databaseId") Long databaseId, @@ -71,7 +72,7 @@ public class TableDataEndpoint { @Transactional @Deprecated @PreAuthorize("hasAuthority('insert-table-data')") - @Timed(value = "data.update", description = "Time needed to update data in a table") + @Observed(name = "dbr_table_data_update") @Operation(summary = "Update data", security = @SecurityRequirement(name = "bearerAuth")) public ResponseEntity<Void> update(@NotNull @PathVariable("databaseId") Long databaseId, @NotNull @PathVariable("tableId") Long tableId, @@ -92,7 +93,7 @@ public class TableDataEndpoint { @DeleteMapping @Transactional @PreAuthorize("hasAuthority('delete-table-data')") - @Timed(value = "data.delete", description = "Time needed to delete data into a table") + @Observed(name = "dbr_table_data_delete") @Operation(summary = "Delete data", security = @SecurityRequirement(name = "bearerAuth")) public ResponseEntity<Void> delete(@NotNull @PathVariable("databaseId") Long databaseId, @NotNull @PathVariable("tableId") Long tableId, @@ -113,7 +114,7 @@ public class TableDataEndpoint { @PostMapping("/import") @Transactional @PreAuthorize("hasAuthority('insert-table-data')") - @Timed(value = "data.insertbulk", description = "Time needed to insert data from .csv into a table") + @Observed(name = "dbr_table_data_import") @Operation(summary = "Insert data from csv", security = @SecurityRequirement(name = "bearerAuth")) public ResponseEntity<Void> importCsv(@NotNull @PathVariable("databaseId") Long databaseId, @NotNull @PathVariable("tableId") Long tableId, @@ -133,7 +134,7 @@ public class TableDataEndpoint { @RequestMapping(method = {RequestMethod.GET, RequestMethod.HEAD}) @Transactional(readOnly = true) - @Timed(value = "data.all", description = "Time needed to find all data from a table") + @Observed(name = "dbr_table_data_findall") @Operation(summary = "Find data", security = @SecurityRequirement(name = "bearerAuth")) public ResponseEntity<QueryResultDto> getAll(@NotNull @PathVariable("databaseId") Long databaseId, @NotNull @PathVariable("tableId") Long tableId, @@ -165,7 +166,7 @@ public class TableDataEndpoint { @GetMapping("/count") @Transactional(readOnly = true) - @Timed(value = "data.all.count", description = "Time needed to get count of all data from a table") + @Observed(name = "dbr_table_data_countall") @Operation(summary = "Find data", security = @SecurityRequirement(name = "bearerAuth")) public ResponseEntity<Long> getCount(@NotNull @PathVariable("databaseId") Long databaseId, @NotNull @PathVariable("tableId") Long tableId, diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java index 3b29e987bcca3b23a06fc663e793d7069c93db1d..10d0ae805ff6e6fbe1303571d469e0e5d35c2fb9 100644 --- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java +++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java @@ -14,6 +14,7 @@ import at.tuwien.service.TableService; import at.tuwien.utils.PrincipalUtil; import at.tuwien.validation.EndpointValidator; import io.micrometer.core.annotation.Timed; +import io.micrometer.observation.annotation.Observed; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.Content; @@ -59,7 +60,7 @@ public class TableEndpoint { @GetMapping @Transactional(readOnly = true) - @Timed(value = "table.list", description = "Time needed to list the tables") + @Observed(name = "dbr_tables_findall") @Operation(summary = "List all tables", security = @SecurityRequirement(name = "bearerAuth")) @ApiResponses(value = { @ApiResponse(responseCode = "200", @@ -95,7 +96,7 @@ public class TableEndpoint { @PostMapping @Transactional @PreAuthorize("hasAuthority('create-table')") - @Timed(value = "table.create", description = "Time needed to create a table") + @Observed(name = "dbr_table_create") @Operation(summary = "Create a table", security = @SecurityRequirement(name = "bearerAuth")) @ApiResponses(value = { @ApiResponse(responseCode = "201", @@ -141,6 +142,11 @@ public class TableEndpoint { TableNameExistsException, ContainerNotFoundException, UserNotFoundException, QueryMalformedException, NotAllowedException, AccessDeniedException { log.debug("endpoint create table, databaseId={}, createDto={}, {}", databaseId, createDto, PrincipalUtil.formatForDebug(principal)); + /* checks */ + if (createDto.getName().isBlank()) { + log.error("Failed create table: table name is blank"); + throw new TableMalformedException("Failed create table: table name is blank"); + } endpointValidator.validateOnlyAccess(databaseId, principal, true); endpointValidator.validateColumnCreateConstraints(createDto); final Table table = tableService.createTable(databaseId, createDto, principal); @@ -153,7 +159,7 @@ public class TableEndpoint { @GetMapping("/{tableId}") @Transactional(readOnly = true) - @Timed(value = "table.find", description = "Time needed to find a table") + @Observed(name = "dbr_tables_find") @Operation(summary = "Get information about table", security = @SecurityRequirement(name = "bearerAuth")) @ApiResponses(value = { @ApiResponse(responseCode = "200", @@ -196,7 +202,7 @@ public class TableEndpoint { @DeleteMapping("/{tableId}") @Transactional @PreAuthorize("hasAuthority('delete-table')") - @Timed(value = "table.delete", description = "Time needed to delete a table") + @Observed(name = "dbr_table_delete") @Operation(summary = "Delete a table", security = @SecurityRequirement(name = "bearerAuth")) @ApiResponses(value = { @ApiResponse(responseCode = "202", diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/TableHistoryEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/TableHistoryEndpoint.java index 90a61181ba259160d6c208da198c2782ebd59f8e..8e8c79d3f41f57f2f0175a8d2cba98f0cce579e2 100644 --- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/TableHistoryEndpoint.java +++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/TableHistoryEndpoint.java @@ -6,6 +6,7 @@ import at.tuwien.exception.*; import at.tuwien.service.TableService; import at.tuwien.utils.PrincipalUtil; import io.micrometer.core.annotation.Timed; +import io.micrometer.observation.annotation.Observed; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.Content; @@ -38,7 +39,7 @@ public class TableHistoryEndpoint { @RequestMapping(method = {RequestMethod.GET, RequestMethod.HEAD}) @Transactional(readOnly = true) - @Timed(value = "history.list", description = "Time needed to retrieve table history") + @Observed(name = "dbr_table_history_findall") @Operation(summary = "Find all history", security = @SecurityRequirement(name = "bearerAuth")) @ApiResponses(value = { @ApiResponse(responseCode = "200", diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/UserEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/UserEndpoint.java index 5ea977c641d54c9339b7f79e9eb7cffa191dbb37..9e3f3d71e7b59e91fbc1e318ff49a61d2c47ba20 100644 --- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/UserEndpoint.java +++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/UserEndpoint.java @@ -13,6 +13,7 @@ import at.tuwien.service.UserService; import at.tuwien.utils.PrincipalUtil; import at.tuwien.utils.UserUtil; import io.micrometer.core.annotation.Timed; +import io.micrometer.observation.annotation.Observed; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.Content; @@ -58,7 +59,7 @@ public class UserEndpoint { @GetMapping @Transactional(readOnly = true) - @Timed(value = "user.list", description = "Time needed to list all users in the metadata database") + @Observed(name = "dbr_users_findall") @Operation(summary = "Find all users") @ApiResponses(value = { @ApiResponse(responseCode = "200", @@ -80,7 +81,7 @@ public class UserEndpoint { @PostMapping @Transactional(rollbackFor = Exception.class) @PreAuthorize("!isAuthenticated()") - @Timed(value = "user.create", description = "Time needed to create a user in the metadata database") + @Observed(name = "dbr_user_create") @Operation(summary = "Create user") @ApiResponses(value = { @ApiResponse(responseCode = "201", @@ -146,7 +147,7 @@ public class UserEndpoint { @GetMapping("/{id}") @Transactional @PreAuthorize("isAuthenticated() or hasAuthority('find-user')") - @Timed(value = "user.info", description = "Time needed to get information of a user in the metadata database") + @Observed(name = "dbr_user_find") @Operation(summary = "Get a user info", security = @SecurityRequirement(name = "bearerAuth")) @ApiResponses(value = { @ApiResponse(responseCode = "200", @@ -188,7 +189,7 @@ public class UserEndpoint { @PutMapping("/{id}") @Transactional @PreAuthorize("hasAuthority('modify-user-information')") - @Timed(value = "user.modify", description = "Time needed to modify a user in the metadata database") + @Observed(name = "dbr_user_modify") @Operation(summary = "Modify user information", security = @SecurityRequirement(name = "bearerAuth")) @ApiResponses(value = { @ApiResponse(responseCode = "202", @@ -230,7 +231,7 @@ public class UserEndpoint { @PutMapping("/{id}/theme") @Transactional @PreAuthorize("hasAuthority('modify-user-theme')") - @Timed(value = "user.theme", description = "Time needed to modify a user theme in the metadata database") + @Observed(name = "dbr_user_theme_modify") @Operation(summary = "Modify user theme", security = @SecurityRequirement(name = "bearerAuth")) @ApiResponses(value = { @ApiResponse(responseCode = "202", @@ -270,7 +271,7 @@ public class UserEndpoint { @PutMapping("/{id}/password") @Transactional @PreAuthorize("isAuthenticated()") - @Timed(value = "user.password", description = "Time needed to modify a user password in the metadata database") + @Observed(name = "dbr_user_password_modify") @Operation(summary = "Modify user password", security = @SecurityRequirement(name = "bearerAuth")) @ApiResponses(value = { @ApiResponse(responseCode = "202", diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/ViewEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/ViewEndpoint.java index 365d991997f238840f22dd3e14de14bcc02813b7..5dbf52cd292dafaf49441a687ac440cdd1758001 100644 --- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/ViewEndpoint.java +++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/ViewEndpoint.java @@ -16,6 +16,7 @@ import at.tuwien.utils.PrincipalUtil; import at.tuwien.utils.UserUtil; import at.tuwien.validation.EndpointValidator; import io.micrometer.core.annotation.Timed; +import io.micrometer.observation.annotation.Observed; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.Content; @@ -61,7 +62,7 @@ public class ViewEndpoint { @GetMapping @Transactional(readOnly = true) - @Timed(value = "view.list", description = "Time needed to list all views in a database") + @Observed(name = "dbr_views_findall") @Operation(summary = "Find all views", security = @SecurityRequirement(name = "bearerAuth")) @ApiResponses(value = { @ApiResponse(responseCode = "200", @@ -97,7 +98,7 @@ public class ViewEndpoint { @PostMapping @Transactional @PreAuthorize("hasAuthority('create-database-view')") - @Timed(value = "view.create", description = "Time needed to create a view") + @Observed(name = "dbr_view_create") @Operation(summary = "Create a view", security = @SecurityRequirement(name = "bearerAuth")) @ApiResponses(value = { @ApiResponse(responseCode = "201", @@ -164,7 +165,7 @@ public class ViewEndpoint { @GetMapping("/{viewId}") @Transactional(readOnly = true) - @Timed(value = "view.find", description = "Time needed to find a view") + @Observed(name = "dbr_view_find") @Operation(summary = "Find one view", security = @SecurityRequirement(name = "bearerAuth")) @ApiResponses(value = { @ApiResponse(responseCode = "200", @@ -198,7 +199,7 @@ public class ViewEndpoint { @DeleteMapping("/{viewId}") @Transactional @PreAuthorize("hasAuthority('delete-database-view')") - @Timed(value = "view.delete", description = "Time needed to delete a view") + @Observed(name = "dbr_view_delete") @Operation(summary = "Delete one view", security = @SecurityRequirement(name = "bearerAuth")) @ApiResponses(value = { @ApiResponse(responseCode = "200", @@ -259,7 +260,7 @@ public class ViewEndpoint { @GetMapping("/{viewId}/data") @Transactional(readOnly = true) - @Timed(value = "view.data", description = "Time needed to retrieve data from a view") + @Observed(name = "dbr_view_data_findall") @Operation(summary = "Find view data", security = @SecurityRequirement(name = "bearerAuth")) @ApiResponses(value = { @ApiResponse(responseCode = "200", @@ -352,7 +353,7 @@ public class ViewEndpoint { @GetMapping("/{viewId}/data/count") @Transactional(readOnly = true) - @Timed(value = "view.data.count", description = "Time needed to retrieve data count from a view") + @Observed(name = "dbr_view_data_count") @Operation(summary = "Find view data count", security = @SecurityRequirement(name = "bearerAuth")) public ResponseEntity<Long> count(@NotNull @PathVariable("databaseId") Long databaseId, @NotNull @PathVariable("viewId") Long viewId, diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/validation/EndpointValidator.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/validation/EndpointValidator.java index 6169e677d4ae1b2b3120262bc91235cb3c084380..3176f6327d630e022e1baa489effc539b61119d2 100644 --- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/validation/EndpointValidator.java +++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/validation/EndpointValidator.java @@ -92,17 +92,37 @@ public class EndpointValidator { .filter(c -> needSize.contains(c.getType())) .findFirst(); if (optional0.isPresent()) { + log.error("Validation failed: column {} needs size parameter", optional0.get().getName() ); throw new TableMalformedException("Validation failed: column " + optional0.get().getName() + " needs size parameter"); } /* check size and d */ final Optional<ColumnCreateDto> optional1 = data.getColumns() .stream() - .filter(c -> Objects.isNull(c.getSize()) || Objects.isNull(c.getD())) .filter(c -> needSizeAndD.contains(c.getType())) + .filter(c -> Objects.isNull(c.getSize()) || Objects.isNull(c.getD())) .findFirst(); if (optional1.isPresent()) { + log.error("Validation failed: column {} needs size and d parameter", optional1.get().getName()); throw new TableMalformedException("Validation failed: column " + optional1.get().getName() + " needs size and d parameter"); } + final Optional<ColumnCreateDto> optional1a = data.getColumns() + .stream() + .filter(c -> needSizeAndD.contains(c.getType())) + .filter(c -> c.getSize() > 65 || c.getD() > 38) + .findFirst(); + if (optional1a.isPresent()) { + log.error("Validation failed: column {} needs size (max 65) and d (max 30)", optional1a.get().getName()); + throw new TableMalformedException("Validation failed: column " + optional1a.get().getName() + " needs size (max 65) and d (max 30)"); + } + final Optional<ColumnCreateDto> optional1b = data.getColumns() + .stream() + .filter(c -> needSizeAndD.contains(c.getType())) + .filter(c -> c.getSize() < c.getD()) + .findFirst(); + if (optional1b.isPresent()) { + log.error("Validation failed: column {} needs size >= d", optional1b.get().getName()); + throw new TableMalformedException("Validation failed: column " + optional1b.get().getName() + " needs size >= d"); + } /* check enum */ final Optional<ColumnCreateDto> optional2 = data.getColumns() .stream() @@ -110,6 +130,7 @@ public class EndpointValidator { .filter(c -> c.getEnums() == null || c.getEnums().isEmpty()) .findFirst(); if (optional2.isPresent()) { + log.error("Validation failed: column {} needs at least 1 allowed enum value", optional2.get().getName()); throw new TableMalformedException("Validation failed: column " + optional2.get().getName() + " needs at least 1 allowed enum value"); } /* check set */ @@ -119,6 +140,7 @@ public class EndpointValidator { .filter(c -> c.getEnums() == null || c.getSets().isEmpty()) .findFirst(); if (optional3.isPresent()) { + log.error("Validation failed: column {} needs at least 1 allowed set value", optional3.get().getName()); throw new TableMalformedException("Validation failed: column " + optional3.get().getName() + " needs at least 1 allowed set value"); } /* check date */ @@ -128,6 +150,7 @@ public class EndpointValidator { .filter(c -> Objects.isNull(c.getDfid())) .findFirst(); if (optional4.isPresent()) { + log.error("Validation failed: column {} needs a format", optional4.get().getName()); throw new TableMalformedException("Validation failed: column " + optional4.get().getName() + " needs a format"); } } diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/SemanticsEndpointUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/SemanticsEndpointUnitTest.java index cdee187b74e2906d0eda8f050145bfce82945f26..957825376ee00951ce0bc3eda07a89807ddbc354 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/SemanticsEndpointUnitTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/SemanticsEndpointUnitTest.java @@ -246,7 +246,7 @@ public class SemanticsEndpointUnitTest extends BaseUnitTest { } /* test */ - final ResponseEntity<ConceptDto> response = semanticsEndpoint.saveUnit(saveDto); + final ResponseEntity<ConceptDto> response = semanticsEndpoint.saveConcept(saveDto); assertEquals(HttpStatus.ACCEPTED, response.getStatusCode()); final ConceptDto body = response.getBody(); assertNotNull(body); diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/TableEndpointUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/TableEndpointUnitTest.java index 9005b65ff7911f7669d98056a899d36d3fe698fa..5ee6b057a2cb95b3d968e48d6c39fdc9144e8ec7 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/TableEndpointUnitTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/TableEndpointUnitTest.java @@ -6,6 +6,8 @@ import at.tuwien.annotations.MockOpensearch; import at.tuwien.api.database.table.TableBriefDto; import at.tuwien.api.database.table.TableCreateDto; import at.tuwien.api.database.table.TableDto; +import at.tuwien.api.database.table.columns.ColumnCreateDto; +import at.tuwien.api.database.table.columns.ColumnTypeDto; import at.tuwien.entities.database.Database; import at.tuwien.entities.database.DatabaseAccess; import at.tuwien.entities.database.table.Table; @@ -155,6 +157,109 @@ public class TableEndpointUnitTest extends BaseUnitTest { }); } + @Test + @WithMockUser(username = USER_3_USERNAME, authorities = {"create-table"}) + public void create_publicDecimalColumnSizeMissing_fails() { + final TableCreateDto request = TableCreateDto.builder() + .name("Some Table") + .description("Some Description") + .columns(List.of(ColumnCreateDto.builder() + .name("ID") + .type(ColumnTypeDto.DECIMAL) + .build())) + .constraints(null) + .build(); + + /* test */ + assertThrows(TableMalformedException.class, () -> { + generic_create(DATABASE_3_ID, DATABASE_3, request, USER_1_ID, USER_1_PRINCIPAL, DATABASE_3_USER_1_WRITE_OWN_ACCESS); + }); + } + + @Test + @WithMockUser(username = USER_3_USERNAME, authorities = {"create-table"}) + public void create_publicDecimalColumnSizeTooSmall_fails() { + final TableCreateDto request = TableCreateDto.builder() + .name("Some Table") + .description("Some Description") + .columns(List.of(ColumnCreateDto.builder() + .name("ID") + .type(ColumnTypeDto.DECIMAL) + .size(-1) + .d(0) + .build())) + .constraints(null) + .build(); + + /* test */ + assertThrows(TableMalformedException.class, () -> { + generic_create(DATABASE_3_ID, DATABASE_3, request, USER_1_ID, USER_1_PRINCIPAL, DATABASE_3_USER_1_WRITE_OWN_ACCESS); + }); + } + + @Test + @WithMockUser(username = USER_3_USERNAME, authorities = {"create-table"}) + public void create_publicDecimalColumnSizeTooBig_fails() { + final TableCreateDto request = TableCreateDto.builder() + .name("Some Table") + .description("Some Description") + .columns(List.of(ColumnCreateDto.builder() + .name("ID") + .type(ColumnTypeDto.DECIMAL) + .size(66) + .d(0) + .build())) + .constraints(null) + .build(); + + /* test */ + assertThrows(TableMalformedException.class, () -> { + generic_create(DATABASE_3_ID, DATABASE_3, request, USER_1_ID, USER_1_PRINCIPAL, DATABASE_3_USER_1_WRITE_OWN_ACCESS); + }); + } + + @Test + @WithMockUser(username = USER_3_USERNAME, authorities = {"create-table"}) + public void create_publicDecimalColumnDTooBig_fails() { + final TableCreateDto request = TableCreateDto.builder() + .name("Some Table") + .description("Some Description") + .columns(List.of(ColumnCreateDto.builder() + .name("ID") + .type(ColumnTypeDto.DECIMAL) + .size(0) + .d(39) + .build())) + .constraints(null) + .build(); + + /* test */ + assertThrows(TableMalformedException.class, () -> { + generic_create(DATABASE_3_ID, DATABASE_3, request, USER_1_ID, USER_1_PRINCIPAL, DATABASE_3_USER_1_WRITE_OWN_ACCESS); + }); + } + + @Test + @WithMockUser(username = USER_3_USERNAME, authorities = {"create-table"}) + public void create_publicDecimalColumnDBiggerSize_fails() { + final TableCreateDto request = TableCreateDto.builder() + .name("Some Table") + .description("Some Description") + .columns(List.of(ColumnCreateDto.builder() + .name("ID") + .type(ColumnTypeDto.DECIMAL) + .size(9) + .d(10) + .build())) + .constraints(null) + .build(); + + /* test */ + assertThrows(TableMalformedException.class, () -> { + generic_create(DATABASE_3_ID, DATABASE_3, request, USER_1_ID, USER_1_PRINCIPAL, DATABASE_3_USER_1_WRITE_OWN_ACCESS); + }); + } + @Test @WithAnonymousUser public void findById_publicAnonymous_succeeds() throws DatabaseNotFoundException, TableNotFoundException, diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mvc/ActuatorEndpointMvcTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mvc/ActuatorEndpointMvcTest.java new file mode 100644 index 0000000000000000000000000000000000000000..11d52c79efd91d66aec071b20d9b7f409d507dd0 --- /dev/null +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mvc/ActuatorEndpointMvcTest.java @@ -0,0 +1,50 @@ +package at.tuwien.mvc; + +import at.tuwien.BaseUnitTest; +import at.tuwien.annotations.MockAmqp; +import at.tuwien.annotations.MockOpensearch; +import lombok.extern.log4j.Log4j2; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.actuate.observability.AutoConfigureObservability; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.web.servlet.MockMvc; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@Log4j2 +@ExtendWith(SpringExtension.class) +@AutoConfigureMockMvc +@SpringBootTest +@AutoConfigureObservability +@MockAmqp +@MockOpensearch +public class ActuatorEndpointMvcTest extends BaseUnitTest { + + @Autowired + private MockMvc mockMvc; + + @Test + public void actuatorInfo_succeeds() throws Exception { + + /* test */ + this.mockMvc.perform(get("/actuator/info")) + .andDo(print()) + .andExpect(status().isOk()); + } + + @Test + public void actuatorPrometheus_succeeds() throws Exception { + + /* test */ + this.mockMvc.perform(get("/actuator/prometheus")) + .andDo(print()) + .andExpect(status().isOk()); + } + +} diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mvc/PrometheusEndpointMvcTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mvc/PrometheusEndpointMvcTest.java new file mode 100644 index 0000000000000000000000000000000000000000..95043859e12ab016b3a23f537c909a673f126a3b --- /dev/null +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mvc/PrometheusEndpointMvcTest.java @@ -0,0 +1,803 @@ +package at.tuwien.mvc; + +import at.tuwien.BaseUnitTest; +import at.tuwien.annotations.MockAmqp; +import at.tuwien.annotations.MockOpensearch; +import at.tuwien.api.container.ContainerCreateRequestDto; +import at.tuwien.api.database.*; +import at.tuwien.api.database.query.ExecuteStatementDto; +import at.tuwien.api.database.query.ImportDto; +import at.tuwien.api.database.query.QueryPersistDto; +import at.tuwien.api.database.table.TableCsvDeleteDto; +import at.tuwien.api.database.table.TableCsvDto; +import at.tuwien.api.database.table.TableCsvUpdateDto; +import at.tuwien.config.MetricsConfig; +import at.tuwien.endpoints.*; +import io.micrometer.observation.tck.TestObservationRegistry; +import lombok.extern.log4j.Log4j2; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.actuate.observability.AutoConfigureObservability; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Import; +import org.springframework.security.test.context.support.WithAnonymousUser; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.web.servlet.MockMvc; + +import java.util.List; + +import static io.micrometer.observation.tck.TestObservationRegistryAssert.assertThat; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@Log4j2 +@ExtendWith(SpringExtension.class) +@AutoConfigureMockMvc +@SpringBootTest +@Import(MetricsConfig.class) +@AutoConfigureObservability +@MockAmqp +@MockOpensearch +public class PrometheusEndpointMvcTest extends BaseUnitTest { + + @Autowired + private MockMvc mockMvc; + + @Autowired + private TestObservationRegistry registry; + + @Autowired + private AccessEndpoint accessEndpoint; + + @Autowired + private ContainerEndpoint containerEndpoint; + + @Autowired + private DatabaseEndpoint databaseEndpoint; + + @Autowired + private ExportEndpoint exportEndpoint; + + @Autowired + private IdentifierEndpoint identifierEndpoint; + + @Autowired + private ImageEndpoint imageEndpoint; + + @Autowired + private LicenseEndpoint licenseEndpoint; + + @Autowired + private MaintenanceEndpoint maintenanceEndpoint; + + @Autowired + private MetadataEndpoint metadataEndpoint; + + @Autowired + private OntologyEndpoint ontologyEndpoint; + + @Autowired + private PersistenceEndpoint persistenceEndpoint; + + @Autowired + private QueryEndpoint queryEndpoint; + + @Autowired + private SemanticsEndpoint semanticsEndpoint; + + @Autowired + private StoreEndpoint storeEndpoint; + + @Autowired + private TableColumnEndpoint tableColumnEndpoint; + + @Autowired + private TableDataEndpoint tableDataEndpoint; + + @Autowired + private TableEndpoint tableEndpoint; + + @Autowired + private TableHistoryEndpoint tableHistoryEndpoint; + + @Autowired + private UserEndpoint userEndpoint; + + @Autowired + private ViewEndpoint viewEndpoint; + + @TestConfiguration + static class ObservationTestConfiguration { + + @Bean + public TestObservationRegistry observationRegistry() { + return TestObservationRegistry.create(); + } + } + + @Test + public void prometheus_succeeds() throws Exception { + + /* test */ + this.mockMvc.perform(get("/actuator/prometheus")) + .andDo(print()) + .andExpect(status().isOk()); + } + + @Test + @WithMockUser(username = USER_1_USERNAME, authorities = {"create-database-access", "update-database-access", "check-database-access", "delete-database-access"}) + public void prometheusAccessEndpoint_succeeds() { + + /* mock */ + try { + accessEndpoint.create(DATABASE_1_ID, USER_1_ID, DatabaseGiveAccessDto.builder().type(AccessTypeDto.READ).build(), USER_1_PRINCIPAL); + } catch (Exception e) { + /* ignore */ + } + try { + accessEndpoint.update(DATABASE_1_ID, USER_1_ID, DatabaseModifyAccessDto.builder().type(AccessTypeDto.READ).build(), USER_1_PRINCIPAL); + } catch (Exception e) { + /* ignore */ + } + try { + accessEndpoint.find(DATABASE_1_ID, USER_1_PRINCIPAL); + } catch (Exception e) { + /* ignore */ + } + try { + accessEndpoint.revoke(DATABASE_1_ID, USER_1_ID, USER_1_PRINCIPAL); + } catch (Exception e) { + /* ignore */ + } + + /* test */ + for (String metric : List.of("dbr_access_give", "dbr_access_modify", "dbr_access_check", "dbr_access_delete")) { + assertThat(registry) + .hasObservationWithNameEqualTo(metric); + } + } + + @Test + @WithMockUser(username = USER_1_USERNAME, authorities = {"create-container", "delete-container"}) + public void prometheusContainerEndpoint_succeeds() { + + /* mock */ + try { + containerEndpoint.findAll(USER_1_PRINCIPAL, null); + } catch (Exception e) { + /* ignore */ + } + try { + containerEndpoint.create(ContainerCreateRequestDto.builder().name(CONTAINER_1_NAME).imageId(IMAGE_1_ID).build(), USER_1_PRINCIPAL); + } catch (Exception e) { + /* ignore */ + } + try { + containerEndpoint.findById(CONTAINER_1_ID); + } catch (Exception e) { + /* ignore */ + } + try { + containerEndpoint.delete(CONTAINER_1_ID, USER_1_PRINCIPAL); + } catch (Exception e) { + /* ignore */ + } + + /* test */ + for (String metric : List.of("dbr_container_findall", "dbr_container_create", "dbr_container_find", "dbr_container_delete")) { + assertThat(registry) + .hasObservationWithNameEqualTo(metric); + } + } + + @Test + @WithMockUser(username = USER_1_USERNAME, authorities = {"create-database", "modify-database-visibility", "modify-database-owner", "delete-database"}) + public void prometheusDatabaseEndpoint_succeeds() { + + /* mock */ + try { + databaseEndpoint.list(USER_1_PRINCIPAL, null); + } catch (Exception e) { + /* ignore */ + } + try { + databaseEndpoint.count(USER_1_PRINCIPAL, null); + } catch (Exception e) { + /* ignore */ + } + try { + databaseEndpoint.create(DATABASE_1_CREATE, USER_1_PRINCIPAL); + } catch (Exception e) { + /* ignore */ + } + try { + databaseEndpoint.visibility(DATABASE_1_ID, DatabaseModifyVisibilityDto.builder().isPublic(true).build(), USER_1_PRINCIPAL); + } catch (Exception e) { + /* ignore */ + } + try { + databaseEndpoint.transfer(DATABASE_1_ID, DatabaseTransferDto.builder().username(USER_2_USERNAME).build(), USER_1_PRINCIPAL); + } catch (Exception e) { + /* ignore */ + } + try { + databaseEndpoint.findById(DATABASE_1_ID, USER_1_PRINCIPAL); + } catch (Exception e) { + /* ignore */ + } + try { + databaseEndpoint.delete(DATABASE_1_ID, USER_1_PRINCIPAL); + } catch (Exception e) { + /* ignore */ + } + + /* test */ + for (String metric : List.of("dbr_database_findall", "dbr_database_count", "dbr_database_create", "dbr_database_visibility", "dbr_database_transfer", "dbr_database_find", "dbr_database_delete")) { + assertThat(registry) + .hasObservationWithNameEqualTo(metric); + } + } + + @Test + @WithMockUser(username = USER_1_USERNAME) + public void prometheusExportEndpoint_succeeds() { + + /* mock */ + try { + exportEndpoint.export(DATABASE_1_ID, TABLE_1_ID, null, USER_1_PRINCIPAL); + } catch (Exception e) { + /* ignore */ + } + + /* test */ + for (String metric : List.of("dbr_table_export")) { + assertThat(registry) + .hasObservationWithNameEqualTo(metric); + } + } + + @Test + @WithMockUser(username = USER_1_USERNAME, authorities = {"create-identifier", "create-foreign-identifier"}) + public void prometheusIdentifierEndpoint_succeeds() { + + /* mock */ + try { + identifierEndpoint.list(DATABASE_1_ID, null, null, IDENTIFIER_1_TYPE_DTO); + } catch (Exception e) { + /* ignore */ + } + try { + identifierEndpoint.create(IDENTIFIER_1_DTO_REQUEST, USER_1_PRINCIPAL); + } catch (Exception e) { + /* ignore */ + } + try { + identifierEndpoint.retrieve(USER_1_ORCID_URL); + } catch (Exception e) { + /* ignore */ + } + + /* test */ + for (String metric : List.of("dbr_identifier_findall", "dbr_identifier_create", "dbr_identifier_retrieve")) { + assertThat(registry) + .hasObservationWithNameEqualTo(metric); + } + } + + @Test + @WithMockUser(username = USER_1_USERNAME, authorities = {"create-image", "modify-image", "delete-image"}) + public void prometheusImageEndpoint_succeeds() { + + /* mock */ + try { + imageEndpoint.findAll(USER_1_PRINCIPAL); + } catch (Exception e) { + /* ignore */ + } + try { + imageEndpoint.create(IMAGE_1_CREATE_DTO, USER_1_PRINCIPAL); + } catch (Exception e) { + /* ignore */ + } + try { + imageEndpoint.findById(IMAGE_1_ID); + } catch (Exception e) { + /* ignore */ + } + try { + imageEndpoint.update(IMAGE_1_ID, IMAGE_1_CHANGE_DTO, USER_1_PRINCIPAL); + } catch (Exception e) { + /* ignore */ + } + try { + imageEndpoint.delete(IMAGE_1_ID, USER_1_PRINCIPAL); + } catch (Exception e) { + /* ignore */ + } + + /* test */ + for (String metric : List.of("dbr_image_findall", "dbr_image_create", "dbr_image_find", "dbr_image_update", "dbr_image_delete")) { + assertThat(registry) + .hasObservationWithNameEqualTo(metric); + } + } + + @Test + @WithMockUser(username = USER_1_USERNAME) + public void prometheusLicenseEndpoint_succeeds() { + + /* mock */ + try { + licenseEndpoint.list(); + } catch (Exception e) { + /* ignore */ + } + + /* test */ + assertThat(registry) + .hasObservationWithNameEqualTo("dbr_license_findall"); + } + + @Test + @WithMockUser(username = USER_1_USERNAME, authorities = {"create-maintenance-message", "update-maintenance-message", "delete-maintenance-message"}) + public void prometheusMaintenanceEndpoint_succeeds() { + + /* mock */ + try { + maintenanceEndpoint.list(); + } catch (Exception e) { + /* ignore */ + } + try { + maintenanceEndpoint.find(BANNER_MESSAGE_1_ID); + } catch (Exception e) { + /* ignore */ + } + try { + maintenanceEndpoint.active(); + } catch (Exception e) { + /* ignore */ + } + try { + maintenanceEndpoint.create(BANNER_MESSAGE_1_CREATE_DTO); + } catch (Exception e) { + /* ignore */ + } + try { + maintenanceEndpoint.update(BANNER_MESSAGE_1_ID, BANNER_MESSAGE_1_UPDATE_DTO); + } catch (Exception e) { + /* ignore */ + } + try { + maintenanceEndpoint.delete(BANNER_MESSAGE_1_ID); + } catch (Exception e) { + /* ignore */ + } + + /* test */ + for (String metric : List.of("dbr_maintenance_findall", "dbr_maintenance_find", "dbr_maintenance_findactive", "dbr_maintenance_create", "dbr_maintenance_update", "dbr_maintenance_delete")) { + assertThat(registry) + .hasObservationWithNameEqualTo(metric); + } + } + + @Test + @WithMockUser(username = USER_1_USERNAME) + public void prometheusMetadataEndpoint_succeeds() { + + /* mock */ + try { + metadataEndpoint.identify(); + } catch (Exception e) { + /* ignore */ + } + try { + metadataEndpoint.listIdentifiers(null); + } catch (Exception e) { + /* ignore */ + } + try { + metadataEndpoint.getRecord(null); + } catch (Exception e) { + /* ignore */ + } + try { + metadataEndpoint.listMetadataFormats(); + } catch (Exception e) { + /* ignore */ + } + + /* test */ + for (String metric : List.of("dbr_oai_identify", "dbr_oai_identifiers_list", "dbr_oai_record_get", "dbr_oai_metadataformats_list")) { + assertThat(registry) + .hasObservationWithNameEqualTo(metric); + } + } + + @Test + @WithMockUser(username = USER_1_USERNAME, authorities = {"create-ontology", "update-ontology", "delete-ontology", "execute-semantic-query"}) + public void prometheusOntologyEndpoint_succeeds() { + + /* mock */ + try { + ontologyEndpoint.findAll(); + } catch (Exception e) { + /* ignore */ + } + try { + ontologyEndpoint.find(ONTOLOGY_1_ID); + } catch (Exception e) { + /* ignore */ + } + try { + ontologyEndpoint.create(ONTOLOGY_1_CREATE_DTO, USER_1_PRINCIPAL); + } catch (Exception e) { + /* ignore */ + } + try { + ontologyEndpoint.update(ONTOLOGY_1_ID, ONTOLOGY_1_MODIFY_DTO, USER_1_PRINCIPAL); + } catch (Exception e) { + /* ignore */ + } + try { + ontologyEndpoint.delete(ONTOLOGY_1_ID); + } catch (Exception e) { + /* ignore */ + } + try { + ontologyEndpoint.find(ONTOLOGY_1_ID, "thing", null); + } catch (Exception e) { + /* ignore */ + } + + /* test */ + for (String metric : List.of("dbr_ontologies_findall", "dbr_ontologies_find", "dbr_ontologies_create", "dbr_ontologies_update", "dbr_ontologies_delete", "dbr_ontologies_entities_find")) { + assertThat(registry) + .hasObservationWithNameEqualTo(metric); + } + } + + @Test + @WithMockUser(username = USER_1_USERNAME, authorities = {"modify-identifier-metadata", "delete-identifier"}) + public void prometheusPersistenceEndpoint_succeeds() { + + /* mock */ + try { + persistenceEndpoint.find(IDENTIFIER_1_ID, null, USER_1_PRINCIPAL); + } catch (Exception e) { + /* ignore */ + } + try { + persistenceEndpoint.update(IDENTIFIER_1_ID, IDENTIFIER_1_DTO_REQUEST, USER_1_PRINCIPAL); + } catch (Exception e) { + /* ignore */ + } + try { + persistenceEndpoint.delete(IDENTIFIER_1_ID); + } catch (Exception e) { + /* ignore */ + } + + /* test */ + for (String metric : List.of("dbr_pid_find", "dbr_pid_update", "dbr_pid_delete")) { + assertThat(registry) + .hasObservationWithNameEqualTo(metric); + } + } + + @Test + @WithMockUser(username = USER_1_USERNAME, authorities = {"execute-query"}) + public void prometheusQueryEndpoint_succeeds() { + + /* mock */ + try { + queryEndpoint.execute(DATABASE_1_ID, ExecuteStatementDto.builder().statement("SELECT 1").build(), null, null, USER_1_PRINCIPAL, null, null); + } catch (Exception e) { + /* ignore */ + } + try { + queryEndpoint.reExecute(DATABASE_1_ID, QUERY_1_ID, USER_1_PRINCIPAL, null, null, null, null); + } catch (Exception e) { + /* ignore */ + } + try { + queryEndpoint.reExecuteCount(DATABASE_1_ID, QUERY_1_ID, USER_1_PRINCIPAL); + } catch (Exception e) { + /* ignore */ + } + try { + queryEndpoint.export(DATABASE_1_ID, QUERY_1_ID, null, USER_1_PRINCIPAL); + } catch (Exception e) { + /* ignore */ + } + + /* test */ + for (String metric : List.of("dbr_query_execute", "dbr_query_reexecute", "dbr_query_reexecute_count", "dbr_query_export")) { + assertThat(registry) + .hasObservationWithNameEqualTo(metric); + } + } + + @Test + @WithMockUser(username = USER_1_USERNAME, authorities = {"create-semantic-concept", "create-semantic-unit", "table-semantic-analyse"}) + public void prometheusSemanticsEndpoint_succeeds() { + + /* mock */ + try { + semanticsEndpoint.findAllConcepts(); + } catch (Exception e) { + /* ignore */ + } + try { + semanticsEndpoint.saveConcept(COLUMN_CONCEPT_FAIR_DATA_SAVE_DTO); + } catch (Exception e) { + /* ignore */ + } + try { + semanticsEndpoint.findAllUnits(); + } catch (Exception e) { + /* ignore */ + } + try { + semanticsEndpoint.saveUnit(UNIT_1_SAVE_DTO); + } catch (Exception e) { + /* ignore */ + } + try { + semanticsEndpoint.analyseTable(DATABASE_1_ID, TABLE_1_ID); + } catch (Exception e) { + /* ignore */ + } + try { + semanticsEndpoint.analyseTableColumn(DATABASE_1_ID, TABLE_1_ID, COLUMN_1_1_ID); + } catch (Exception e) { + /* ignore */ + } + + /* test */ + for (String metric : List.of("dbr_semantic_concepts_findall", "dbr_semantic_concepts_save", "dbr_semantic_units_findall", "dbr_semantic_units_save", "dbr_semantic_table_analyse", "dbr_semantic_column_analyse")) { + assertThat(registry) + .hasObservationWithNameEqualTo(metric); + } + } + + @Test + @WithMockUser(username = USER_1_USERNAME, authorities = {"persist-query"}) + public void prometheusStoreEndpoint_succeeds() { + + /* mock */ + try { + storeEndpoint.findAll(DATABASE_1_ID, true, USER_1_PRINCIPAL); + } catch (Exception e) { + /* ignore */ + } + try { + storeEndpoint.find(DATABASE_1_ID, QUERY_1_ID, USER_1_PRINCIPAL); + } catch (Exception e) { + /* ignore */ + } + try { + storeEndpoint.persist(DATABASE_1_ID, QUERY_1_ID, QueryPersistDto.builder().persist(true).build(), USER_1_PRINCIPAL); + } catch (Exception e) { + /* ignore */ + } + + /* test */ + for (String metric : List.of("dbr_queries_findall", "dbr_queries_find", "dbr_query_persist")) { + assertThat(registry) + .hasObservationWithNameEqualTo(metric); + } + } + + @Test + @WithMockUser(username = USER_1_USERNAME, authorities = {"modify-table-column-semantics", "modify-foreign-table-column-semantics"}) + public void prometheusTableColumnEndpoint_succeeds() { + + /* mock */ + try { + tableColumnEndpoint.update(DATABASE_1_ID, TABLE_1_ID, COLUMN_1_4_ID, COLUMN_1_4_SEMANTICS_UPDATE_DTO, USER_1_PRINCIPAL, "s3cr3t"); + } catch (Exception e) { + /* ignore */ + } + + /* test */ + assertThat(registry) + .hasObservationWithNameEqualTo("dbr_semantics_column_save"); + } + + @Test + @WithMockUser(username = USER_1_USERNAME, authorities = {"insert-table-data", "delete-table-data"}) + public void prometheusTableDataEndpoint_succeeds() { + + /* mock */ + try { + tableDataEndpoint.insert(DATABASE_1_ID, TABLE_1_ID, TableCsvDto.builder().build(), USER_1_PRINCIPAL); + } catch (Exception e) { + /* ignore */ + } + try { + tableDataEndpoint.update(DATABASE_1_ID, TABLE_1_ID, TableCsvUpdateDto.builder().build(), USER_1_PRINCIPAL); + } catch (Exception e) { + /* ignore */ + } + try { + tableDataEndpoint.delete(DATABASE_1_ID, TABLE_1_ID, TableCsvDeleteDto.builder().build(), USER_1_PRINCIPAL); + } catch (Exception e) { + /* ignore */ + } + try { + tableDataEndpoint.importCsv(DATABASE_1_ID, TABLE_1_ID, ImportDto.builder().build(), USER_1_PRINCIPAL); + } catch (Exception e) { + /* ignore */ + } + try { + tableDataEndpoint.getAll(DATABASE_1_ID, TABLE_1_ID, USER_1_PRINCIPAL, null, null, null, null, null); + } catch (Exception e) { + /* ignore */ + } + try { + tableDataEndpoint.getCount(DATABASE_1_ID, TABLE_1_ID, USER_1_PRINCIPAL, null); + } catch (Exception e) { + /* ignore */ + } + + /* test */ + for (String metric : List.of("dbr_table_data_insert", "dbr_table_data_update", "dbr_table_data_delete", "dbr_table_data_import", "dbr_table_data_findall", "dbr_table_data_countall")) { + assertThat(registry) + .hasObservationWithNameEqualTo(metric); + } + } + + @Test + @WithMockUser(username = USER_1_USERNAME, authorities = {"create-table", "delete-table"}) + public void prometheusTableEndpoint_succeeds() { + + /* mock */ + try { + tableEndpoint.list(DATABASE_1_ID, USER_1_PRINCIPAL); + } catch (Exception e) { + /* ignore */ + } + try { + tableEndpoint.create(DATABASE_1_ID, TABLE_3_CREATE_DTO, USER_1_PRINCIPAL); + } catch (Exception e) { + /* ignore */ + } + try { + tableEndpoint.findById(DATABASE_1_ID, TABLE_1_ID, USER_1_PRINCIPAL); + } catch (Exception e) { + /* ignore */ + } + try { + tableEndpoint.delete(DATABASE_1_ID, TABLE_1_ID, USER_1_PRINCIPAL); + } catch (Exception e) { + /* ignore */ + } + + /* test */ + for (String metric : List.of("dbr_tables_findall", "dbr_table_create", "dbr_tables_find", "dbr_table_delete")) { + assertThat(registry) + .hasObservationWithNameEqualTo(metric); + } + } + + @Test + @WithMockUser(username = USER_1_USERNAME) + public void prometheusTableHistoryEndpoint_succeeds() { + + /* mock */ + try { + tableHistoryEndpoint.getAll(DATABASE_1_ID, TABLE_1_ID, USER_1_PRINCIPAL); + } catch (Exception e) { + /* ignore */ + } + + /* test */ + assertThat(registry) + .hasObservationWithNameEqualTo("dbr_table_history_findall"); + } + + @Test + @WithMockUser(username = USER_1_USERNAME, authorities = {"find-user", "modify-user-information", "modify-user-theme"}) + public void prometheusUserEndpoint_succeeds() { + + /* mock */ + try { + userEndpoint.findAll(); + } catch (Exception e) { + /* ignore */ + } + try { + userEndpoint.find(USER_1_ID, USER_1_PRINCIPAL); + } catch (Exception e) { + /* ignore */ + } + try { + userEndpoint.modify(USER_1_ID, USER_1_UPDATE_DTO, USER_1_PRINCIPAL); + } catch (Exception e) { + /* ignore */ + } + try { + userEndpoint.theme(USER_1_ID, USER_1_THEME_SET_DTO, USER_1_PRINCIPAL); + } catch (Exception e) { + /* ignore */ + } + try { + userEndpoint.password(USER_1_ID, USER_1_PASSWORD_DTO, USER_1_PRINCIPAL); + } catch (Exception e) { + /* ignore */ + } + + /* test */ + for (String metric : List.of("dbr_users_findall", "dbr_user_find", "dbr_user_modify", "dbr_user_theme_modify", "dbr_user_password_modify")) { + assertThat(registry) + .hasObservationWithNameEqualTo(metric); + } + } + + @Test + @WithAnonymousUser + public void prometheusUserEndpoint2_succeeds() { + + /* mock */ + try { + userEndpoint.create(USER_1_SIGNUP_REQUEST_DTO); + } catch (Exception e) { + /* ignore */ + } + + /* test */ + assertThat(registry) + .hasObservationWithNameEqualTo("dbr_user_create"); + } + + @Test + @WithMockUser(username = USER_1_USERNAME, authorities = {"create-database-view", "delete-database-view"}) + public void prometheusViewEndpoint_succeeds() { + + /* mock */ + try { + viewEndpoint.findAll(DATABASE_1_ID, USER_1_PRINCIPAL); + } catch (Exception e) { + /* ignore */ + } + try { + viewEndpoint.create(DATABASE_1_ID, VIEW_1_CREATE_DTO, USER_1_PRINCIPAL); + } catch (Exception e) { + /* ignore */ + } + try { + viewEndpoint.find(DATABASE_1_ID, VIEW_1_ID, USER_1_PRINCIPAL); + } catch (Exception e) { + /* ignore */ + } + try { + viewEndpoint.delete(DATABASE_1_ID, VIEW_1_ID, USER_1_PRINCIPAL); + } catch (Exception e) { + /* ignore */ + } + try { + viewEndpoint.data(DATABASE_1_ID, VIEW_1_ID, USER_1_PRINCIPAL, null, null); + } catch (Exception e) { + /* ignore */ + } + try { + viewEndpoint.count(DATABASE_1_ID, VIEW_1_ID, USER_1_PRINCIPAL); + } catch (Exception e) { + /* ignore */ + } + + /* test */ + for (String metric : List.of("dbr_views_findall", "dbr_view_create", "dbr_view_find", "dbr_view_delete", "dbr_view_data_findall", "dbr_view_data_count")) { + assertThat(registry) + .hasObservationWithNameEqualTo(metric); + } + } + +} diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/ContainerServiceIntegrationTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/ContainerServiceIntegrationTest.java index 9efb25050e5538a366d027d44742700a03fdd287..9d4e8f39eaa3bb02e22c4fdaf2ae518dad96285f 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/ContainerServiceIntegrationTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/ContainerServiceIntegrationTest.java @@ -109,7 +109,7 @@ public class ContainerServiceIntegrationTest extends BaseUnitTest { public void create_notFound_fails() { final ContainerCreateRequestDto request = ContainerCreateRequestDto.builder() .name(CONTAINER_3_NAME) - .imageId(IMAGE_2_ID) + .imageId(9999L) .build(); /* test */ diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/ImageServiceIntegrationTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/ImageServiceIntegrationTest.java index d80f6787e49e6a97ea5e95f1a7e7e8cc058e3c88..d8557922fd1a9bd00ebf9bbfdc9603d7b3720425 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/ImageServiceIntegrationTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/ImageServiceIntegrationTest.java @@ -48,12 +48,12 @@ public class ImageServiceIntegrationTest extends BaseUnitTest { @Test public void create_succeeds() throws ImageAlreadyExistsException { final ImageCreateDto request = ImageCreateDto.builder() - .name(IMAGE_2_NAME) - .version(IMAGE_2_VERSION) - .jdbcMethod(IMAGE_2_JDBC) - .dialect(IMAGE_2_DIALECT) - .driverClass(IMAGE_2_DRIVER) - .defaultPort(IMAGE_2_PORT) + .name(IMAGE_1_NAME) + .version("11.1.3") + .jdbcMethod(IMAGE_1_JDBC) + .dialect(IMAGE_1_DIALECT) + .driverClass(IMAGE_1_DRIVER) + .defaultPort(IMAGE_1_PORT) .build(); final Principal principal = new BasicUserPrincipal(USER_1_USERNAME); diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/TableServiceIntegrationWriteTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/TableServiceIntegrationWriteTest.java index 11fb73a8e07ca567de5155118b96d3643b2f3408..c3149bd713ea0fe616724f72468e7d7774dc7417 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/TableServiceIntegrationWriteTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/TableServiceIntegrationWriteTest.java @@ -114,31 +114,6 @@ public class TableServiceIntegrationWriteTest extends BaseUnitTest { tableService.createTable(DATABASE_1_ID, TABLE_3_CREATE_DTO, USER_1_PRINCIPAL); } - @Test - public void create_failedBefore_succeeds() throws UserNotFoundException, TableMalformedException, - QueryMalformedException, DatabaseNotFoundException, ImageNotSupportedException, TableNameExistsException, - ContainerNotFoundException, SQLException { - - /* mock */ - when(tableidxRepository.save(any(TableDto.class))) - .thenReturn(null); - when(tableColumnidxRepository.saveAll(anyList())) - .thenReturn(List.of()); - - /* test */ - try { - tableService.createTable(DATABASE_1_ID, TABLE_3_INVALID_CREATE_DTO, USER_1_PRINCIPAL); - } catch (TableMalformedException e) { - /* ignore */ - } - assertFalse(MariaDbConfig.tableExists(DATABASE_1, "traffic_zu_rich")); - final Table response = tableService.createTable(DATABASE_1_ID, TABLE_3_CREATE_DTO, USER_1_PRINCIPAL); - assertTrue(MariaDbConfig.tableExists(DATABASE_1, "traffic_zu_rich")); - assertEquals(TABLE_3_NAME, response.getName()); - assertEquals(TABLE_3_INTERNALNAME, response.getInternalName()); - assertEquals(TABLE_3_DESCRIPTION, response.getDescription()); - } - @Test public void create_withConstraints_succeeds() throws UserNotFoundException, TableMalformedException, QueryMalformedException, DatabaseNotFoundException, ImageNotSupportedException, TableNameExistsException, diff --git a/dbrepo-metadata-service/rest-service/src/test/resources/application.properties b/dbrepo-metadata-service/rest-service/src/test/resources/application.properties index 7c4376e9dcb7f15176aecfe79a6b971c1d690010..cc1a895c65c7189221a4e062dcfdb20370b6e5fb 100644 --- a/dbrepo-metadata-service/rest-service/src/test/resources/application.properties +++ b/dbrepo-metadata-service/rest-service/src/test/resources/application.properties @@ -17,7 +17,7 @@ spring.jpa.hibernate.ddl-auto=create # logging logging.level.root=error logging.level.at.tuwien.=debug -logging.level.at.tuwien.gateway.impl.=trace +logging.level.at.tuwien.mapper.=trace # rabbitmq spring.rabbitmq.host=localhost diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/config/MetricsConfig.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/config/MetricsConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..450be2f7df8b52fe493dd498dc0422350bb3ff39 --- /dev/null +++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/config/MetricsConfig.java @@ -0,0 +1,15 @@ +package at.tuwien.config; + +import io.micrometer.observation.ObservationRegistry; +import io.micrometer.observation.aop.ObservedAspect; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class MetricsConfig { + + @Bean + public ObservedAspect observedAspect(ObservationRegistry observationRegistry) { + return new ObservedAspect(observationRegistry); + } +} diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/QueryServiceImpl.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/QueryServiceImpl.java index e2cf9491d49448784eaa11fe51a1abf2e5aae173..ca2aca3e1d9b6e83e01511d95cf5403e18b48163 100644 --- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/QueryServiceImpl.java +++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/QueryServiceImpl.java @@ -401,40 +401,17 @@ public class QueryServiceImpl extends HibernateConnector implements QueryService /* find */ final Database database = databaseService.find(databaseId); final Table table = tableService.find(databaseId, tableId); - /* preparing the statements */ - final ComboPooledDataSource dataSource = getPrivilegedDataSource(database.getContainer().getImage(), - database.getContainer(), database); - /* Create a temporary table, insert there, transfer with update on duplicate key and lastly drops the temporary table */ - try { - final Connection connection = dataSource.getConnection(); - queryMapper.dropTemporaryTableSQL(connection, table) - .executeUpdate(); - } catch (SQLException e) { - log.error("Failed to drop temporary table: {}", e.getMessage()); - throw new TableMalformedException("Failed to drop temporary table", e); - } - try { - final Connection connection = dataSource.getConnection(); - queryMapper.generateTemporaryTableSQL(connection, table) - .executeUpdate(); - } catch (SQLException e) { - log.error("Failed to create temporary table: {}", e.getMessage()); - dataSource.close(); - throw new TableMalformedException("Failed to create temporary table", e); - } /* import .csv from blob storage to sidecar */ dataDbSidecarGateway.importFile(database.getContainer().getSidecarHost(), database.getContainer().getSidecarPort(), data.getLocation()); /* import .csv from sidecar to database */ + final ComboPooledDataSource dataSource = getPrivilegedDataSource(database.getContainer().getImage(), + database.getContainer(), database); try { final Connection connection = dataSource.getConnection(); - queryMapper.pathToRawInsertQuery(connection, table, data) - .executeUpdate(); - queryMapper.generateInsertFromTemporaryTableSQL(connection, table) - .executeUpdate(); + queryMapper.importCsvQuery(connection, table, data); } catch (SQLException e) { - log.error("Failed to insert temporary table: {}", e.getMessage()); - dataSource.close(); - throw new TableMalformedException("Failed to insert temporary table", e); + log.error("Failed to import .csv: {}", e.getMessage()); + throw new TableMalformedException("Failed to import .csv", e); } finally { dataSource.close(); } 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 2bb3da5b1c5d8f521eb8b91af1b24793e1514487..fa5e9d60f43bd61e3cbd915e097d7dcad7d5bb35 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 @@ -158,11 +158,6 @@ public class TableServiceImpl extends HibernateConnector implements TableService public Table createTable(Long databaseId, TableCreateDto createDto, Principal principal) throws ImageNotSupportedException, DatabaseNotFoundException, TableMalformedException, TableNameExistsException, QueryMalformedException { - /* checks */ - if (createDto.getName().isBlank()) { - log.error("Failed create table: table name is blank"); - throw new TableMalformedException("Failed create table: table name is blank"); - } /* find */ final Database database = databaseService.find(databaseId); if (!database.getContainer().getImage().getName().equals("mariadb")) { @@ -177,27 +172,11 @@ public class TableServiceImpl extends HibernateConnector implements TableService } /* run query */ final ComboPooledDataSource dataSource = getPrivilegedDataSource(database.getContainer().getImage(), database.getContainer(), database); - final TableCreateRawQuery query; + final Boolean generatedSequence; try { final Connection connection = dataSource.getConnection(); - query = tableMapper.tableToCreateTableRawQuery(connection, createDto); - if (query.getGenerated()) { - /* in case the id column needs to be generated, we need to generate the sequence too */ - final PreparedStatement preparedStatement10 = tableMapper.tableToCreateSequenceRawQuery(connection, database, createDto); - preparedStatement10.executeUpdate(); - log.debug("created id sequence"); - } - final PreparedStatement preparedStatement11 = query.getPreparedStatement(); - preparedStatement11.executeUpdate(); + generatedSequence = tableMapper.tableToCreateTableRawQuery(connection, createDto); } catch (Exception e) { - try { - final Connection connection = dataSource.getConnection(); - final PreparedStatement preparedStatement11 = tableMapper.tableToDropSequenceRawQuery(connection, database, createDto); - preparedStatement11.executeUpdate(); - log.debug("successfully rolled back creation of id sequence"); - } catch (SQLException ex) { - log.error("Failed to rollback creation of id sequence"); - } log.error("Failed to create table, reason: {}", e.getMessage()); throw new TableMalformedException("Failed to create table", e); } finally { @@ -219,7 +198,7 @@ public class TableServiceImpl extends HibernateConnector implements TableService entity.setColumns(createDto.getColumns() .stream() .map(column -> tableMapper.columnCreateDtoToTableColumn(column, database.getContainer().getImage())) - .map(column -> tableMapper.tableColumnToTableColumn(entity, column, query)) + .map(column -> tableMapper.tableColumnToTableColumn(entity, column, generatedSequence)) .toList()); /* set the ordinal position for the columns */ entity.getColumns() 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 ae3ae81be32720f67f7303a1920ecb153b2265ea..fe9a744280e4a0ffb7e3e198952cd729e45076d4 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 @@ -4,10 +4,7 @@ import at.tuwien.api.amqp.*; import at.tuwien.api.auth.SignupRequestDto; import at.tuwien.api.container.ContainerBriefDto; import at.tuwien.api.container.ContainerDto; -import at.tuwien.api.container.image.ImageBriefDto; -import at.tuwien.api.container.image.ImageCreateDto; -import at.tuwien.api.container.image.ImageDateDto; -import at.tuwien.api.container.image.ImageDto; +import at.tuwien.api.container.image.*; import at.tuwien.api.database.*; import at.tuwien.api.database.query.QueryBriefDto; import at.tuwien.api.database.query.QueryDto; @@ -698,6 +695,14 @@ public abstract class BaseTest { .defaultPort(IMAGE_1_PORT) .build(); + public final static ImageChangeDto IMAGE_1_CHANGE_DTO = ImageChangeDto.builder() + .registry(IMAGE_1_REGISTRY) + .dialect(IMAGE_1_DIALECT) + .jdbcMethod(IMAGE_1_JDBC) + .driverClass(IMAGE_1_DRIVER) + .defaultPort(IMAGE_1_PORT) + .build(); + public final static Long IMAGE_DATE_2_ID = 2L; public final static Long IMAGE_DATE_2_IMAGE_ID = IMAGE_1_ID; public final static String IMAGE_DATE_2_UNIX_FORMAT = "dd.MM.yy"; @@ -809,16 +814,6 @@ public abstract class BaseTest { .version(IMAGE_1_VERSION) .build(); - public final static Long IMAGE_2_ID = 2L; - public final static String IMAGE_2_NAME = "mariadb"; - public final static String IMAGE_2_VERSION = "8.0"; - public final static Integer IMAGE_2_PORT = 3306; - public final static String IMAGE_2_DIALECT = "org.hibernate.dialect.MySQLDialect"; - public final static String IMAGE_2_DRIVER = "com.mysql.jdbc.Driver"; - public final static String IMAGE_2_JDBC = "mysql"; - public final static Long IMAGE_2_SIZE = 12000L; - public final static Instant IMAGE_2_BUILT = Instant.now().minus(38, HOURS); - public final static Long CONTAINER_1_ID = 1L; public final static ContainerImage CONTAINER_1_IMAGE = IMAGE_1; public final static ImageBriefDto CONTAINER_1_IMAGE_BRIEF_DTO = IMAGE_1_BRIEF_DTO; diff --git a/dbrepo-search-service/Pipfile b/dbrepo-search-service/Pipfile index 72a500d3490b5ce8cd5fc17043eb57a67a1dec75..222a8d4021ee537a688e9a0b11089155f582bb77 100644 --- a/dbrepo-search-service/Pipfile +++ b/dbrepo-search-service/Pipfile @@ -15,6 +15,7 @@ prometheus-flask-exporter = "~=0.22" python-dotenv = "~=1.0" sqlalchemy-utils = "*" testcontainers-opensearch = "*" +pytest = "*" [dev-packages] diff --git a/dbrepo-search-service/Pipfile.lock b/dbrepo-search-service/Pipfile.lock index 558d19b36bbdac4c91b1e17c59540257f336ee0f..58ff692b38ad8a2cae0fc719af1147352d8f4a64 100644 --- a/dbrepo-search-service/Pipfile.lock +++ b/dbrepo-search-service/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "0714f11db1b9d6f958d181b61296d04b4bb003d42ed87f059f78b570cab1028f" + "sha256": "d126ace88662c624b1574018644daa91049070c38a8aabb0c48aed5257f0a973" }, "pipfile-spec": 6, "requires": { @@ -26,115 +26,115 @@ }, "blinker": { "hashes": [ - "sha256:4afd3de66ef3a9f8067559fb7a1cbe555c17dcbe15971b05d1b625c3e7abe213", - "sha256:c3d739772abb7bc2860abf5f2ec284223d9ad5c76da018234f6f50d6f31ab1f0" + "sha256:c3f865d4d54db7abc53758a01601cf343fe55b84c1de4e3fa910e420b438d5b9", + "sha256:e6820ff6fa4e4d1d8e2747c2283749c3f547e4fee112b98555cdcdae32996182" ], - "markers": "python_version >= '3.7'", - "version": "==1.6.2" + "markers": "python_version >= '3.8'", + "version": "==1.7.0" }, "certifi": { "hashes": [ - "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082", - "sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9" + "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1", + "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474" ], "markers": "python_version >= '3.6'", - "version": "==2023.7.22" + "version": "==2023.11.17" }, "charset-normalizer": { "hashes": [ - "sha256:02673e456dc5ab13659f85196c534dc596d4ef260e4d86e856c3b2773ce09843", - "sha256:02af06682e3590ab952599fbadac535ede5d60d78848e555aa58d0c0abbde786", - "sha256:03680bb39035fbcffe828eae9c3f8afc0428c91d38e7d61aa992ef7a59fb120e", - "sha256:0570d21da019941634a531444364f2482e8db0b3425fcd5ac0c36565a64142c8", - "sha256:09c77f964f351a7369cc343911e0df63e762e42bac24cd7d18525961c81754f4", - "sha256:0d3d5b7db9ed8a2b11a774db2bbea7ba1884430a205dbd54a32d61d7c2a190fa", - "sha256:1063da2c85b95f2d1a430f1c33b55c9c17ffaf5e612e10aeaad641c55a9e2b9d", - "sha256:12ebea541c44fdc88ccb794a13fe861cc5e35d64ed689513a5c03d05b53b7c82", - "sha256:153e7b6e724761741e0974fc4dcd406d35ba70b92bfe3fedcb497226c93b9da7", - "sha256:15b26ddf78d57f1d143bdf32e820fd8935d36abe8a25eb9ec0b5a71c82eb3895", - "sha256:1872d01ac8c618a8da634e232f24793883d6e456a66593135aeafe3784b0848d", - "sha256:187d18082694a29005ba2944c882344b6748d5be69e3a89bf3cc9d878e548d5a", - "sha256:1b2919306936ac6efb3aed1fbf81039f7087ddadb3160882a57ee2ff74fd2382", - "sha256:232ac332403e37e4a03d209a3f92ed9071f7d3dbda70e2a5e9cff1c4ba9f0678", - "sha256:23e8565ab7ff33218530bc817922fae827420f143479b753104ab801145b1d5b", - "sha256:24817cb02cbef7cd499f7c9a2735286b4782bd47a5b3516a0e84c50eab44b98e", - "sha256:249c6470a2b60935bafd1d1d13cd613f8cd8388d53461c67397ee6a0f5dce741", - "sha256:24a91a981f185721542a0b7c92e9054b7ab4fea0508a795846bc5b0abf8118d4", - "sha256:2502dd2a736c879c0f0d3e2161e74d9907231e25d35794584b1ca5284e43f596", - "sha256:250c9eb0f4600361dd80d46112213dff2286231d92d3e52af1e5a6083d10cad9", - "sha256:278c296c6f96fa686d74eb449ea1697f3c03dc28b75f873b65b5201806346a69", - "sha256:2935ffc78db9645cb2086c2f8f4cfd23d9b73cc0dc80334bc30aac6f03f68f8c", - "sha256:2f4a0033ce9a76e391542c182f0d48d084855b5fcba5010f707c8e8c34663d77", - "sha256:30a85aed0b864ac88309b7d94be09f6046c834ef60762a8833b660139cfbad13", - "sha256:380c4bde80bce25c6e4f77b19386f5ec9db230df9f2f2ac1e5ad7af2caa70459", - "sha256:3ae38d325b512f63f8da31f826e6cb6c367336f95e418137286ba362925c877e", - "sha256:3b447982ad46348c02cb90d230b75ac34e9886273df3a93eec0539308a6296d7", - "sha256:3debd1150027933210c2fc321527c2299118aa929c2f5a0a80ab6953e3bd1908", - "sha256:4162918ef3098851fcd8a628bf9b6a98d10c380725df9e04caf5ca6dd48c847a", - "sha256:468d2a840567b13a590e67dd276c570f8de00ed767ecc611994c301d0f8c014f", - "sha256:4cc152c5dd831641e995764f9f0b6589519f6f5123258ccaca8c6d34572fefa8", - "sha256:542da1178c1c6af8873e143910e2269add130a299c9106eef2594e15dae5e482", - "sha256:557b21a44ceac6c6b9773bc65aa1b4cc3e248a5ad2f5b914b91579a32e22204d", - "sha256:5707a746c6083a3a74b46b3a631d78d129edab06195a92a8ece755aac25a3f3d", - "sha256:588245972aca710b5b68802c8cad9edaa98589b1b42ad2b53accd6910dad3545", - "sha256:5adf257bd58c1b8632046bbe43ee38c04e1038e9d37de9c57a94d6bd6ce5da34", - "sha256:619d1c96099be5823db34fe89e2582b336b5b074a7f47f819d6b3a57ff7bdb86", - "sha256:63563193aec44bce707e0c5ca64ff69fa72ed7cf34ce6e11d5127555756fd2f6", - "sha256:67b8cc9574bb518ec76dc8e705d4c39ae78bb96237cb533edac149352c1f39fe", - "sha256:6a685067d05e46641d5d1623d7c7fdf15a357546cbb2f71b0ebde91b175ffc3e", - "sha256:70f1d09c0d7748b73290b29219e854b3207aea922f839437870d8cc2168e31cc", - "sha256:750b446b2ffce1739e8578576092179160f6d26bd5e23eb1789c4d64d5af7dc7", - "sha256:7966951325782121e67c81299a031f4c115615e68046f79b85856b86ebffc4cd", - "sha256:7b8b8bf1189b3ba9b8de5c8db4d541b406611a71a955bbbd7385bbc45fcb786c", - "sha256:7f5d10bae5d78e4551b7be7a9b29643a95aded9d0f602aa2ba584f0388e7a557", - "sha256:805dfea4ca10411a5296bcc75638017215a93ffb584c9e344731eef0dcfb026a", - "sha256:81bf654678e575403736b85ba3a7867e31c2c30a69bc57fe88e3ace52fb17b89", - "sha256:82eb849f085624f6a607538ee7b83a6d8126df6d2f7d3b319cb837b289123078", - "sha256:85a32721ddde63c9df9ebb0d2045b9691d9750cb139c161c80e500d210f5e26e", - "sha256:86d1f65ac145e2c9ed71d8ffb1905e9bba3a91ae29ba55b4c46ae6fc31d7c0d4", - "sha256:86f63face3a527284f7bb8a9d4f78988e3c06823f7bea2bd6f0e0e9298ca0403", - "sha256:8eaf82f0eccd1505cf39a45a6bd0a8cf1c70dcfc30dba338207a969d91b965c0", - "sha256:93aa7eef6ee71c629b51ef873991d6911b906d7312c6e8e99790c0f33c576f89", - "sha256:96c2b49eb6a72c0e4991d62406e365d87067ca14c1a729a870d22354e6f68115", - "sha256:9cf3126b85822c4e53aa28c7ec9869b924d6fcfb76e77a45c44b83d91afd74f9", - "sha256:9fe359b2e3a7729010060fbca442ca225280c16e923b37db0e955ac2a2b72a05", - "sha256:a0ac5e7015a5920cfce654c06618ec40c33e12801711da6b4258af59a8eff00a", - "sha256:a3f93dab657839dfa61025056606600a11d0b696d79386f974e459a3fbc568ec", - "sha256:a4b71f4d1765639372a3b32d2638197f5cd5221b19531f9245fcc9ee62d38f56", - "sha256:aae32c93e0f64469f74ccc730a7cb21c7610af3a775157e50bbd38f816536b38", - "sha256:aaf7b34c5bc56b38c931a54f7952f1ff0ae77a2e82496583b247f7c969eb1479", - "sha256:abecce40dfebbfa6abf8e324e1860092eeca6f7375c8c4e655a8afb61af58f2c", - "sha256:abf0d9f45ea5fb95051c8bfe43cb40cda383772f7e5023a83cc481ca2604d74e", - "sha256:ac71b2977fb90c35d41c9453116e283fac47bb9096ad917b8819ca8b943abecd", - "sha256:ada214c6fa40f8d800e575de6b91a40d0548139e5dc457d2ebb61470abf50186", - "sha256:b09719a17a2301178fac4470d54b1680b18a5048b481cb8890e1ef820cb80455", - "sha256:b1121de0e9d6e6ca08289583d7491e7fcb18a439305b34a30b20d8215922d43c", - "sha256:b3b2316b25644b23b54a6f6401074cebcecd1244c0b8e80111c9a3f1c8e83d65", - "sha256:b3d9b48ee6e3967b7901c052b670c7dda6deb812c309439adaffdec55c6d7b78", - "sha256:b5bcf60a228acae568e9911f410f9d9e0d43197d030ae5799e20dca8df588287", - "sha256:b8f3307af845803fb0b060ab76cf6dd3a13adc15b6b451f54281d25911eb92df", - "sha256:c2af80fb58f0f24b3f3adcb9148e6203fa67dd3f61c4af146ecad033024dde43", - "sha256:c350354efb159b8767a6244c166f66e67506e06c8924ed74669b2c70bc8735b1", - "sha256:c5a74c359b2d47d26cdbbc7845e9662d6b08a1e915eb015d044729e92e7050b7", - "sha256:c71f16da1ed8949774ef79f4a0260d28b83b3a50c6576f8f4f0288d109777989", - "sha256:d47ecf253780c90ee181d4d871cd655a789da937454045b17b5798da9393901a", - "sha256:d7eff0f27edc5afa9e405f7165f85a6d782d308f3b6b9d96016c010597958e63", - "sha256:d97d85fa63f315a8bdaba2af9a6a686e0eceab77b3089af45133252618e70884", - "sha256:db756e48f9c5c607b5e33dd36b1d5872d0422e960145b08ab0ec7fd420e9d649", - "sha256:dc45229747b67ffc441b3de2f3ae5e62877a282ea828a5bdb67883c4ee4a8810", - "sha256:e0fc42822278451bc13a2e8626cf2218ba570f27856b536e00cfa53099724828", - "sha256:e39c7eb31e3f5b1f88caff88bcff1b7f8334975b46f6ac6e9fc725d829bc35d4", - "sha256:e46cd37076971c1040fc8c41273a8b3e2c624ce4f2be3f5dfcb7a430c1d3acc2", - "sha256:e5c1502d4ace69a179305abb3f0bb6141cbe4714bc9b31d427329a95acfc8bdd", - "sha256:edfe077ab09442d4ef3c52cb1f9dab89bff02f4524afc0acf2d46be17dc479f5", - "sha256:effe5406c9bd748a871dbcaf3ac69167c38d72db8c9baf3ff954c344f31c4cbe", - "sha256:f0d1e3732768fecb052d90d62b220af62ead5748ac51ef61e7b32c266cac9293", - "sha256:f5969baeaea61c97efa706b9b107dcba02784b1601c74ac84f2a532ea079403e", - "sha256:f8888e31e3a85943743f8fc15e71536bda1c81d5aa36d014a3c0c44481d7db6e", - "sha256:fc52b79d83a3fe3a360902d3f5d79073a993597d48114c29485e9431092905d8" + "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027", + "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087", + "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786", + "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8", + "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09", + "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185", + "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574", + "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e", + "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519", + "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898", + "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269", + "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3", + "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f", + "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6", + "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8", + "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a", + "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73", + "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc", + "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714", + "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2", + "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc", + "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce", + "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d", + "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e", + "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6", + "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269", + "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96", + "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d", + "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a", + "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4", + "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77", + "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d", + "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0", + "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed", + "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068", + "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac", + "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25", + "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8", + "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab", + "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26", + "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2", + "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db", + "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f", + "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5", + "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99", + "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c", + "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d", + "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811", + "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa", + "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a", + "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03", + "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b", + "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04", + "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c", + "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001", + "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458", + "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389", + "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99", + "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985", + "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537", + "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238", + "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f", + "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d", + "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796", + "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a", + "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143", + "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8", + "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c", + "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5", + "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5", + "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711", + "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4", + "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6", + "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c", + "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7", + "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4", + "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b", + "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae", + "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12", + "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c", + "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae", + "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8", + "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887", + "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b", + "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4", + "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f", + "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5", + "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33", + "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519", + "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561" ], "markers": "python_full_version >= '3.7.0'", - "version": "==3.3.0" + "version": "==3.3.2" }, "click": { "hashes": [ @@ -154,19 +154,28 @@ }, "elastic-transport": { "hashes": [ - "sha256:c718ce40e8217b6045604961463c10da69a152dda07af4e25b3feae8d7965fc0", - "sha256:e5548997113c5d9566c9a1a51ed67bce50a4871bc0e44b692166461279e4167e" + "sha256:ca51d08a4d16611701a57fb70592dbc7cb68c40fef4ac1becfe4aea100fe82ef", + "sha256:e73ac3c7ad4e9209436207143d797d3f6b62a399a34d2729e069e44c9ea2cadc" ], "markers": "python_version >= '3.6'", - "version": "==8.4.1" + "version": "==8.10.0" }, "elasticsearch": { "hashes": [ - "sha256:1a7f1b71dda0bc24c97d3e61c3b0d8165d5ab024f9f1538f35ed5894c6831ba2", - "sha256:4a721a9ff9d669ed7140a043d5b84fb50eb7bda6aaf2eafb7bee49177dc316e8" + "sha256:26b72957ee617c9f0b23ac872e1c133cf9d7f5d439c615daaa11016265da36ab", + "sha256:9e08413beaff3a46bc10c6c57069a84704df6aaa93085c737df07f58a2811b78" ], "index": "pypi", - "version": "==8.10.0" + "markers": "python_version >= '3.6'", + "version": "==8.11.0" + }, + "exceptiongroup": { + "hashes": [ + "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14", + "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68" + ], + "markers": "python_version < '3.11'", + "version": "==1.2.0" }, "flasgger": { "hashes": [ @@ -181,6 +190,7 @@ "sha256:f69fcd559dc907ed196ab9df0e48471709175e696d6e698dd4dbe940f96ce66b" ], "index": "pypi", + "markers": "python_version >= '3.8'", "version": "==2.3.3" }, "flask-cors": { @@ -197,6 +207,7 @@ "sha256:eaec42af107dcb919785a4b3766c09ffba9f286b92a8d58603933f28fd4db6a3" ], "index": "pypi", + "markers": "python_version >= '3.7' and python_version < '4'", "version": "==4.5.3" }, "flask-sqlalchemy": { @@ -205,76 +216,71 @@ "sha256:e4b68bb881802dda1a7d878b2fc84c06d1ee57fb40b874d3dc97dabfa36b8312" ], "index": "pypi", + "markers": "python_version >= '3.8'", "version": "==3.1.1" }, "greenlet": { "hashes": [ - "sha256:02a807b2a58d5cdebb07050efe3d7deaf915468d112dfcf5e426d0564aa3aa4a", - "sha256:0b72b802496cccbd9b31acea72b6f87e7771ccfd7f7927437d592e5c92ed703c", - "sha256:0d3f83ffb18dc57243e0151331e3c383b05e5b6c5029ac29f754745c800f8ed9", - "sha256:10b5582744abd9858947d163843d323d0b67be9432db50f8bf83031032bc218d", - "sha256:123910c58234a8d40eaab595bc56a5ae49bdd90122dde5bdc012c20595a94c14", - "sha256:19834e3f91f485442adc1ee440171ec5d9a4840a1f7bd5ed97833544719ce10b", - "sha256:1d363666acc21d2c204dd8705c0e0457d7b2ee7a76cb16ffc099d6799744ac99", - "sha256:211ef8d174601b80e01436f4e6905aca341b15a566f35a10dd8d1e93f5dbb3b7", - "sha256:269d06fa0f9624455ce08ae0179430eea61085e3cf6457f05982b37fd2cefe17", - "sha256:2e7dcdfad252f2ca83c685b0fa9fba00e4d8f243b73839229d56ee3d9d219314", - "sha256:334ef6ed8337bd0b58bb0ae4f7f2dcc84c9f116e474bb4ec250a8bb9bd797a66", - "sha256:343675e0da2f3c69d3fb1e894ba0a1acf58f481f3b9372ce1eb465ef93cf6fed", - "sha256:37f60b3a42d8b5499be910d1267b24355c495064f271cfe74bf28b17b099133c", - "sha256:38ad562a104cd41e9d4644f46ea37167b93190c6d5e4048fcc4b80d34ecb278f", - "sha256:3c0d36f5adc6e6100aedbc976d7428a9f7194ea79911aa4bf471f44ee13a9464", - "sha256:3fd2b18432e7298fcbec3d39e1a0aa91ae9ea1c93356ec089421fabc3651572b", - "sha256:4a1a6244ff96343e9994e37e5b4839f09a0207d35ef6134dce5c20d260d0302c", - "sha256:4cd83fb8d8e17633ad534d9ac93719ef8937568d730ef07ac3a98cb520fd93e4", - "sha256:527cd90ba3d8d7ae7dceb06fda619895768a46a1b4e423bdb24c1969823b8362", - "sha256:553d6fb2324e7f4f0899e5ad2c427a4579ed4873f42124beba763f16032959af", - "sha256:56867a3b3cf26dc8a0beecdb4459c59f4c47cdd5424618c08515f682e1d46692", - "sha256:621fcb346141ae08cb95424ebfc5b014361621b8132c48e538e34c3c93ac7365", - "sha256:63acdc34c9cde42a6534518e32ce55c30f932b473c62c235a466469a710bfbf9", - "sha256:6512592cc49b2c6d9b19fbaa0312124cd4c4c8a90d28473f86f92685cc5fef8e", - "sha256:6672fdde0fd1a60b44fb1751a7779c6db487e42b0cc65e7caa6aa686874e79fb", - "sha256:6a5b2d4cdaf1c71057ff823a19d850ed5c6c2d3686cb71f73ae4d6382aaa7a06", - "sha256:6a68d670c8f89ff65c82b936275369e532772eebc027c3be68c6b87ad05ca695", - "sha256:6bb36985f606a7c49916eff74ab99399cdfd09241c375d5a820bb855dfb4af9f", - "sha256:73b2f1922a39d5d59cc0e597987300df3396b148a9bd10b76a058a2f2772fc04", - "sha256:7709fd7bb02b31908dc8fd35bfd0a29fc24681d5cc9ac1d64ad07f8d2b7db62f", - "sha256:8060b32d8586e912a7b7dac2d15b28dbbd63a174ab32f5bc6d107a1c4143f40b", - "sha256:80dcd3c938cbcac986c5c92779db8e8ce51a89a849c135172c88ecbdc8c056b7", - "sha256:813720bd57e193391dfe26f4871186cf460848b83df7e23e6bef698a7624b4c9", - "sha256:831d6f35037cf18ca5e80a737a27d822d87cd922521d18ed3dbc8a6967be50ce", - "sha256:871b0a8835f9e9d461b7fdaa1b57e3492dd45398e87324c047469ce2fc9f516c", - "sha256:952256c2bc5b4ee8df8dfc54fc4de330970bf5d79253c863fb5e6761f00dda35", - "sha256:96d9ea57292f636ec851a9bb961a5cc0f9976900e16e5d5647f19aa36ba6366b", - "sha256:9a812224a5fb17a538207e8cf8e86f517df2080c8ee0f8c1ed2bdaccd18f38f4", - "sha256:9adbd8ecf097e34ada8efde9b6fec4dd2a903b1e98037adf72d12993a1c80b51", - "sha256:9de687479faec7db5b198cc365bc34addd256b0028956501f4d4d5e9ca2e240a", - "sha256:a048293392d4e058298710a54dfaefcefdf49d287cd33fb1f7d63d55426e4355", - "sha256:aa15a2ec737cb609ed48902b45c5e4ff6044feb5dcdfcf6fa8482379190330d7", - "sha256:abe1ef3d780de56defd0c77c5ba95e152f4e4c4e12d7e11dd8447d338b85a625", - "sha256:ad6fb737e46b8bd63156b8f59ba6cdef46fe2b7db0c5804388a2d0519b8ddb99", - "sha256:b1660a15a446206c8545edc292ab5c48b91ff732f91b3d3b30d9a915d5ec4779", - "sha256:b505fcfc26f4148551826a96f7317e02c400665fa0883fe505d4fcaab1dabfdd", - "sha256:b822fab253ac0f330ee807e7485769e3ac85d5eef827ca224feaaefa462dc0d0", - "sha256:bdd696947cd695924aecb3870660b7545a19851f93b9d327ef8236bfc49be705", - "sha256:bdfaeecf8cc705d35d8e6de324bf58427d7eafb55f67050d8f28053a3d57118c", - "sha256:be557119bf467d37a8099d91fbf11b2de5eb1fd5fc5b91598407574848dc910f", - "sha256:c3692ecf3fe754c8c0f2c95ff19626584459eab110eaab66413b1e7425cd84e9", - "sha256:c6b5ce7f40f0e2f8b88c28e6691ca6806814157ff05e794cdd161be928550f4c", - "sha256:c94e4e924d09b5a3e37b853fe5924a95eac058cb6f6fb437ebb588b7eda79870", - "sha256:cc3e2679ea13b4de79bdc44b25a0c4fcd5e94e21b8f290791744ac42d34a0353", - "sha256:d1e22c22f7826096ad503e9bb681b05b8c1f5a8138469b255eb91f26a76634f2", - "sha256:d5539f6da3418c3dc002739cb2bb8d169056aa66e0c83f6bacae0cd3ac26b423", - "sha256:d55db1db455c59b46f794346efce896e754b8942817f46a1bada2d29446e305a", - "sha256:e09dea87cc91aea5500262993cbd484b41edf8af74f976719dd83fe724644cd6", - "sha256:e52a712c38e5fb4fd68e00dc3caf00b60cb65634d50e32281a9d6431b33b4af1", - "sha256:e693e759e172fa1c2c90d35dea4acbdd1d609b6936115d3739148d5e4cd11947", - "sha256:ecf94aa539e97a8411b5ea52fc6ccd8371be9550c4041011a091eb8b3ca1d810", - "sha256:f351479a6914fd81a55c8e68963609f792d9b067fb8a60a042c585a621e0de4f", - "sha256:f47932c434a3c8d3c86d865443fadc1fbf574e9b11d6650b656e602b1797908a" + "sha256:0a02d259510b3630f330c86557331a3b0e0c79dac3d166e449a39363beaae174", + "sha256:0b6f9f8ca7093fd4433472fd99b5650f8a26dcd8ba410e14094c1e44cd3ceddd", + "sha256:100f78a29707ca1525ea47388cec8a049405147719f47ebf3895e7509c6446aa", + "sha256:1757936efea16e3f03db20efd0cd50a1c86b06734f9f7338a90c4ba85ec2ad5a", + "sha256:19075157a10055759066854a973b3d1325d964d498a805bb68a1f9af4aaef8ec", + "sha256:19bbdf1cce0346ef7341705d71e2ecf6f41a35c311137f29b8a2dc2341374565", + "sha256:20107edf7c2c3644c67c12205dc60b1bb11d26b2610b276f97d666110d1b511d", + "sha256:22f79120a24aeeae2b4471c711dcf4f8c736a2bb2fabad2a67ac9a55ea72523c", + "sha256:2847e5d7beedb8d614186962c3d774d40d3374d580d2cbdab7f184580a39d234", + "sha256:28e89e232c7593d33cac35425b58950789962011cc274aa43ef8865f2e11f46d", + "sha256:329c5a2e5a0ee942f2992c5e3ff40be03e75f745f48847f118a3cfece7a28546", + "sha256:337322096d92808f76ad26061a8f5fccb22b0809bea39212cd6c406f6a7060d2", + "sha256:3fcc780ae8edbb1d050d920ab44790201f027d59fdbd21362340a85c79066a74", + "sha256:41bdeeb552d814bcd7fb52172b304898a35818107cc8778b5101423c9017b3de", + "sha256:4eddd98afc726f8aee1948858aed9e6feeb1758889dfd869072d4465973f6bfd", + "sha256:52e93b28db27ae7d208748f45d2db8a7b6a380e0d703f099c949d0f0d80b70e9", + "sha256:55d62807f1c5a1682075c62436702aaba941daa316e9161e4b6ccebbbf38bda3", + "sha256:5805e71e5b570d490938d55552f5a9e10f477c19400c38bf1d5190d760691846", + "sha256:599daf06ea59bfedbec564b1692b0166a0045f32b6f0933b0dd4df59a854caf2", + "sha256:60d5772e8195f4e9ebf74046a9121bbb90090f6550f81d8956a05387ba139353", + "sha256:696d8e7d82398e810f2b3622b24e87906763b6ebfd90e361e88eb85b0e554dc8", + "sha256:6e6061bf1e9565c29002e3c601cf68569c450be7fc3f7336671af7ddb4657166", + "sha256:80ac992f25d10aaebe1ee15df45ca0d7571d0f70b645c08ec68733fb7a020206", + "sha256:816bd9488a94cba78d93e1abb58000e8266fa9cc2aa9ccdd6eb0696acb24005b", + "sha256:85d2b77e7c9382f004b41d9c72c85537fac834fb141b0296942d52bf03fe4a3d", + "sha256:87c8ceb0cf8a5a51b8008b643844b7f4a8264a2c13fcbcd8a8316161725383fe", + "sha256:89ee2e967bd7ff85d84a2de09df10e021c9b38c7d91dead95b406ed6350c6997", + "sha256:8bef097455dea90ffe855286926ae02d8faa335ed8e4067326257cb571fc1445", + "sha256:8d11ebbd679e927593978aa44c10fc2092bc454b7d13fdc958d3e9d508aba7d0", + "sha256:91e6c7db42638dc45cf2e13c73be16bf83179f7859b07cfc139518941320be96", + "sha256:97e7ac860d64e2dcba5c5944cfc8fa9ea185cd84061c623536154d5a89237884", + "sha256:990066bff27c4fcf3b69382b86f4c99b3652bab2a7e685d968cd4d0cfc6f67c6", + "sha256:9fbc5b8f3dfe24784cee8ce0be3da2d8a79e46a276593db6868382d9c50d97b1", + "sha256:ac4a39d1abae48184d420aa8e5e63efd1b75c8444dd95daa3e03f6c6310e9619", + "sha256:b2c02d2ad98116e914d4f3155ffc905fd0c025d901ead3f6ed07385e19122c94", + "sha256:b2d3337dcfaa99698aa2377c81c9ca72fcd89c07e7eb62ece3f23a3fe89b2ce4", + "sha256:b489c36d1327868d207002391f662a1d163bdc8daf10ab2e5f6e41b9b96de3b1", + "sha256:b641161c302efbb860ae6b081f406839a8b7d5573f20a455539823802c655f63", + "sha256:b8ba29306c5de7717b5761b9ea74f9c72b9e2b834e24aa984da99cbfc70157fd", + "sha256:b9934adbd0f6e476f0ecff3c94626529f344f57b38c9a541f87098710b18af0a", + "sha256:ce85c43ae54845272f6f9cd8320d034d7a946e9773c693b27d620edec825e376", + "sha256:cf868e08690cb89360eebc73ba4be7fb461cfbc6168dd88e2fbbe6f31812cd57", + "sha256:d2905ce1df400360463c772b55d8e2518d0e488a87cdea13dd2c71dcb2a1fa16", + "sha256:d57e20ba591727da0c230ab2c3f200ac9d6d333860d85348816e1dca4cc4792e", + "sha256:d6a8c9d4f8692917a3dc7eb25a6fb337bff86909febe2f793ec1928cd97bedfc", + "sha256:d923ff276f1c1f9680d32832f8d6c040fe9306cbfb5d161b0911e9634be9ef0a", + "sha256:daa7197b43c707462f06d2c693ffdbb5991cbb8b80b5b984007de431493a319c", + "sha256:dbd4c177afb8a8d9ba348d925b0b67246147af806f0b104af4d24f144d461cd5", + "sha256:dc4d815b794fd8868c4d67602692c21bf5293a75e4b607bb92a11e821e2b859a", + "sha256:e9d21aaa84557d64209af04ff48e0ad5e28c5cca67ce43444e939579d085da72", + "sha256:ea6b8aa9e08eea388c5f7a276fabb1d4b6b9d6e4ceb12cc477c3d352001768a9", + "sha256:eabe7090db68c981fca689299c2d116400b553f4b713266b130cfc9e2aa9c5a9", + "sha256:f2f6d303f3dee132b322a14cd8765287b8f86cdc10d2cb6a6fae234ea488888e", + "sha256:f33f3258aae89da191c6ebaa3bc517c6c4cbc9b9f689e5d8452f7aedbb913fa8", + "sha256:f7bfb769f7efa0eefcd039dd19d843a4fbfbac52f1878b1da2ed5793ec9b1a65", + "sha256:f89e21afe925fcfa655965ca8ea10f24773a1791400989ff32f467badfe4a064", + "sha256:fa24255ae3c0ab67e613556375a4341af04a084bd58764731972bcbc8baeba36" ], "markers": "platform_machine == 'aarch64' or (platform_machine == 'ppc64le' or (platform_machine == 'x86_64' or (platform_machine == 'amd64' or (platform_machine == 'AMD64' or (platform_machine == 'win32' or platform_machine == 'WIN32')))))", - "version": "==3.0.0" + "version": "==3.0.1" }, "idna": { "hashes": [ @@ -284,6 +290,14 @@ "markers": "python_version >= '3.5'", "version": "==3.4" }, + "iniconfig": { + "hashes": [ + "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", + "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374" + ], + "markers": "python_version >= '3.7'", + "version": "==2.0.0" + }, "itsdangerous": { "hashes": [ "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44", @@ -302,19 +316,19 @@ }, "jsonschema": { "hashes": [ - "sha256:cd5f1f9ed9444e554b38ba003af06c0a8c2868131e56bfbef0550fb450c0330e", - "sha256:ec84cc37cfa703ef7cd4928db24f9cb31428a5d0fa77747b8b51a847458e0bbf" + "sha256:4f614fd46d8d61258610998997743ec5492a648b33cf478c1ddc23ed4598a5fa", + "sha256:ed6231f0429ecf966f5bc8dfef245998220549cbbcf140f913b7464c52c3b6b3" ], "markers": "python_version >= '3.8'", - "version": "==4.19.1" + "version": "==4.20.0" }, "jsonschema-specifications": { "hashes": [ - "sha256:05adf340b659828a004220a9613be00fa3f223f2b82002e273dee62fd50524b1", - "sha256:c91a50404e88a1f6ba40636778e2ee08f6e24c5613fe4c53ac24578a5a7f72bb" + "sha256:c9b234904ffe02f079bf91b14d79987faa685fd4b39c377a0996954c0090b9ca", + "sha256:f596778ab612b3fd29f72ea0d990393d0540a5aab18bf0407a46632eab540779" ], "markers": "python_version >= '3.8'", - "version": "==2023.7.1" + "version": "==2023.11.1" }, "markupsafe": { "hashes": [ @@ -392,11 +406,12 @@ }, "opensearch-py": { "hashes": [ - "sha256:eafbc5d56a7ca696afba7d77bcda1bbb849050cbf9265d57d8476576cb576395", - "sha256:f82a2e914835f7d645a632777de9a62d0c0de60ffd2f8cdae2ccfa4cfc40a185" + "sha256:564f175af134aa885f4ced6846eb4532e08b414fff0a7976f76b276fe0e69158", + "sha256:7867319132133e2974c09f76a54eb1d502b989229be52da583d93ddc743ea111" ], "index": "pypi", - "version": "==2.3.1" + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' and python_version < '4'", + "version": "==2.4.2" }, "packaging": { "hashes": [ @@ -406,21 +421,29 @@ "markers": "python_version >= '3.7'", "version": "==23.2" }, + "pluggy": { + "hashes": [ + "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12", + "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7" + ], + "markers": "python_version >= '3.8'", + "version": "==1.3.0" + }, "prometheus-client": { "hashes": [ - "sha256:21e674f39831ae3f8acde238afd9a27a37d0d2fb5a28ea094f0ce25d2cbf2091", - "sha256:e537f37160f6807b8202a6fc4764cdd19bac5480ddd3e0d463c3002b34462101" + "sha256:4585b0d1223148c27a225b10dbec5ae9bc4c81a99a3fa80774fa6209935324e1", + "sha256:c88b1e6ecf6b41cd8fb5731c7ae919bf66df6ec6fafa555cd6c0e16ca169ae92" ], - "markers": "python_version >= '3.6'", - "version": "==0.17.1" + "markers": "python_version >= '3.8'", + "version": "==0.19.0" }, "prometheus-flask-exporter": { "hashes": [ - "sha256:959b69f1e740b6736ea53fe5f28dc2ab6229b2ebeade6582b3dbb5d74c7d58e4", - "sha256:e130179c26d5a9b903c12c0d8826127ae491b04b298cae0b92b98677dcf2c06f" + "sha256:7a026b4fdd54ebeddb77589333efe3a1ec43c7c717468825b0b3e9b6c33f7e9e", + "sha256:e4e6beb1b8e1e164da6d70fe1edefc95ef184f113b5047f66f4b7262233da9c0" ], "index": "pypi", - "version": "==0.22.4" + "version": "==0.23.0" }, "pyjwt": { "hashes": [ @@ -430,6 +453,15 @@ "markers": "python_version >= '3.7'", "version": "==2.8.0" }, + "pytest": { + "hashes": [ + "sha256:0d009c083ea859a71b76adf7c1d502e4bc170b80a8ef002da5806527b9591fac", + "sha256:d989d136982de4e3b29dabcc838ad581c64e8ed52c11fbe86ddebd9da0818cd5" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==7.4.3" + }, "python-dateutil": { "hashes": [ "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86", @@ -444,6 +476,7 @@ "sha256:f5971a9226b701070a4bf2c38c89e5a3f0d64de8debda981d1db98583009122a" ], "index": "pypi", + "markers": "python_version >= '3.8'", "version": "==1.0.0" }, "pyyaml": { @@ -504,11 +537,11 @@ }, "referencing": { "hashes": [ - "sha256:449b6669b6121a9e96a7f9e410b245d471e8d48964c67113ce9afe50c8dd7bdf", - "sha256:794ad8003c65938edcdbc027f1933215e0d0ccc0291e3ce20a4d87432b59efc0" + "sha256:381b11e53dd93babb55696c71cf42aef2d36b8a150c49bf0bc301e36d536c882", + "sha256:cc28f2c88fbe7b961a7817a0abc034c09a1e36358f82fedb4ffdf29a25398863" ], "markers": "python_version >= '3.8'", - "version": "==0.30.2" + "version": "==0.31.0" }, "requests": { "hashes": [ @@ -520,106 +553,108 @@ }, "rpds-py": { "hashes": [ - "sha256:015de2ce2af1586ff5dc873e804434185199a15f7d96920ce67e50604592cae9", - "sha256:061c3ff1f51ecec256e916cf71cc01f9975af8fb3af9b94d3c0cc8702cfea637", - "sha256:08a80cf4884920863623a9ee9a285ee04cef57ebedc1cc87b3e3e0f24c8acfe5", - "sha256:09362f86ec201288d5687d1dc476b07bf39c08478cde837cb710b302864e7ec9", - "sha256:0bb4f48bd0dd18eebe826395e6a48b7331291078a879295bae4e5d053be50d4c", - "sha256:106af1653007cc569d5fbb5f08c6648a49fe4de74c2df814e234e282ebc06957", - "sha256:11fdd1192240dda8d6c5d18a06146e9045cb7e3ba7c06de6973000ff035df7c6", - "sha256:16a472300bc6c83fe4c2072cc22b3972f90d718d56f241adabc7ae509f53f154", - "sha256:176287bb998fd1e9846a9b666e240e58f8d3373e3bf87e7642f15af5405187b8", - "sha256:177914f81f66c86c012311f8c7f46887ec375cfcfd2a2f28233a3053ac93a569", - "sha256:177c9dd834cdf4dc39c27436ade6fdf9fe81484758885f2d616d5d03c0a83bd2", - "sha256:187700668c018a7e76e89424b7c1042f317c8df9161f00c0c903c82b0a8cac5c", - "sha256:1d9b5ee46dcb498fa3e46d4dfabcb531e1f2e76b477e0d99ef114f17bbd38453", - "sha256:22da15b902f9f8e267020d1c8bcfc4831ca646fecb60254f7bc71763569f56b1", - "sha256:24cd91a03543a0f8d09cb18d1cb27df80a84b5553d2bd94cba5979ef6af5c6e7", - "sha256:255f1a10ae39b52122cce26ce0781f7a616f502feecce9e616976f6a87992d6b", - "sha256:271c360fdc464fe6a75f13ea0c08ddf71a321f4c55fc20a3fe62ea3ef09df7d9", - "sha256:2ed83d53a8c5902ec48b90b2ac045e28e1698c0bea9441af9409fc844dc79496", - "sha256:2f3e1867dd574014253b4b8f01ba443b9c914e61d45f3674e452a915d6e929a3", - "sha256:35fbd23c1c8732cde7a94abe7fb071ec173c2f58c0bd0d7e5b669fdfc80a2c7b", - "sha256:37d0c59548ae56fae01c14998918d04ee0d5d3277363c10208eef8c4e2b68ed6", - "sha256:39d05e65f23a0fe897b6ac395f2a8d48c56ac0f583f5d663e0afec1da89b95da", - "sha256:3ad59efe24a4d54c2742929001f2d02803aafc15d6d781c21379e3f7f66ec842", - "sha256:3aed39db2f0ace76faa94f465d4234aac72e2f32b009f15da6492a561b3bbebd", - "sha256:3bbac1953c17252f9cc675bb19372444aadf0179b5df575ac4b56faaec9f6294", - "sha256:40bc802a696887b14c002edd43c18082cb7b6f9ee8b838239b03b56574d97f71", - "sha256:42f712b4668831c0cd85e0a5b5a308700fe068e37dcd24c0062904c4e372b093", - "sha256:448a66b8266de0b581246ca7cd6a73b8d98d15100fb7165974535fa3b577340e", - "sha256:485301ee56ce87a51ccb182a4b180d852c5cb2b3cb3a82f7d4714b4141119d8c", - "sha256:485747ee62da83366a44fbba963c5fe017860ad408ccd6cd99aa66ea80d32b2e", - "sha256:4cf0855a842c5b5c391dd32ca273b09e86abf8367572073bd1edfc52bc44446b", - "sha256:4eca20917a06d2fca7628ef3c8b94a8c358f6b43f1a621c9815243462dcccf97", - "sha256:4ed172d0c79f156c1b954e99c03bc2e3033c17efce8dd1a7c781bc4d5793dfac", - "sha256:5267cfda873ad62591b9332fd9472d2409f7cf02a34a9c9cb367e2c0255994bf", - "sha256:52b5cbc0469328e58180021138207e6ec91d7ca2e037d3549cc9e34e2187330a", - "sha256:53d7a3cd46cdc1689296348cb05ffd4f4280035770aee0c8ead3bbd4d6529acc", - "sha256:563646d74a4b4456d0cf3b714ca522e725243c603e8254ad85c3b59b7c0c4bf0", - "sha256:570cc326e78ff23dec7f41487aa9c3dffd02e5ee9ab43a8f6ccc3df8f9327623", - "sha256:5aca759ada6b1967fcfd4336dcf460d02a8a23e6abe06e90ea7881e5c22c4de6", - "sha256:5de11c041486681ce854c814844f4ce3282b6ea1656faae19208ebe09d31c5b8", - "sha256:5e271dd97c7bb8eefda5cca38cd0b0373a1fea50f71e8071376b46968582af9b", - "sha256:642ed0a209ced4be3a46f8cb094f2d76f1f479e2a1ceca6de6346a096cd3409d", - "sha256:6446002739ca29249f0beaaf067fcbc2b5aab4bc7ee8fb941bd194947ce19aff", - "sha256:691d50c99a937709ac4c4cd570d959a006bd6a6d970a484c84cc99543d4a5bbb", - "sha256:69b857a7d8bd4f5d6e0db4086da8c46309a26e8cefdfc778c0c5cc17d4b11e08", - "sha256:6ac3fefb0d168c7c6cab24fdfc80ec62cd2b4dfd9e65b84bdceb1cb01d385c33", - "sha256:6c9141af27a4e5819d74d67d227d5047a20fa3c7d4d9df43037a955b4c748ec5", - "sha256:7170cbde4070dc3c77dec82abf86f3b210633d4f89550fa0ad2d4b549a05572a", - "sha256:763ad59e105fca09705d9f9b29ecffb95ecdc3b0363be3bb56081b2c6de7977a", - "sha256:77076bdc8776a2b029e1e6ffbe6d7056e35f56f5e80d9dc0bad26ad4a024a762", - "sha256:7cd020b1fb41e3ab7716d4d2c3972d4588fdfbab9bfbbb64acc7078eccef8860", - "sha256:821392559d37759caa67d622d0d2994c7a3f2fb29274948ac799d496d92bca73", - "sha256:829e91f3a8574888b73e7a3feb3b1af698e717513597e23136ff4eba0bc8387a", - "sha256:850c272e0e0d1a5c5d73b1b7871b0a7c2446b304cec55ccdb3eaac0d792bb065", - "sha256:87d9b206b1bd7a0523375dc2020a6ce88bca5330682ae2fe25e86fd5d45cea9c", - "sha256:8bd01ff4032abaed03f2db702fa9a61078bee37add0bd884a6190b05e63b028c", - "sha256:8d54bbdf5d56e2c8cf81a1857250f3ea132de77af543d0ba5dce667183b61fec", - "sha256:8efaeb08ede95066da3a3e3c420fcc0a21693fcd0c4396d0585b019613d28515", - "sha256:8f94fdd756ba1f79f988855d948ae0bad9ddf44df296770d9a58c774cfbcca72", - "sha256:95cde244e7195b2c07ec9b73fa4c5026d4a27233451485caa1cd0c1b55f26dbd", - "sha256:975382d9aa90dc59253d6a83a5ca72e07f4ada3ae3d6c0575ced513db322b8ec", - "sha256:9dd9d9d9e898b9d30683bdd2b6c1849449158647d1049a125879cb397ee9cd12", - "sha256:a019a344312d0b1f429c00d49c3be62fa273d4a1094e1b224f403716b6d03be1", - "sha256:a4d9bfda3f84fc563868fe25ca160c8ff0e69bc4443c5647f960d59400ce6557", - "sha256:a657250807b6efd19b28f5922520ae002a54cb43c2401e6f3d0230c352564d25", - "sha256:a771417c9c06c56c9d53d11a5b084d1de75de82978e23c544270ab25e7c066ff", - "sha256:aad6ed9e70ddfb34d849b761fb243be58c735be6a9265b9060d6ddb77751e3e8", - "sha256:ae87137951bb3dc08c7d8bfb8988d8c119f3230731b08a71146e84aaa919a7a9", - "sha256:af247fd4f12cca4129c1b82090244ea5a9d5bb089e9a82feb5a2f7c6a9fe181d", - "sha256:b5d4bdd697195f3876d134101c40c7d06d46c6ab25159ed5cbd44105c715278a", - "sha256:b9255e7165083de7c1d605e818025e8860636348f34a79d84ec533546064f07e", - "sha256:c22211c165166de6683de8136229721f3d5c8606cc2c3d1562da9a3a5058049c", - "sha256:c55f9821f88e8bee4b7a72c82cfb5ecd22b6aad04033334f33c329b29bfa4da0", - "sha256:c7aed97f2e676561416c927b063802c8a6285e9b55e1b83213dfd99a8f4f9e48", - "sha256:cd2163f42868865597d89399a01aa33b7594ce8e2c4a28503127c81a2f17784e", - "sha256:ce5e7504db95b76fc89055c7f41e367eaadef5b1d059e27e1d6eabf2b55ca314", - "sha256:cff7351c251c7546407827b6a37bcef6416304fc54d12d44dbfecbb717064717", - "sha256:d27aa6bbc1f33be920bb7adbb95581452cdf23005d5611b29a12bb6a3468cc95", - "sha256:d3b52a67ac66a3a64a7e710ba629f62d1e26ca0504c29ee8cbd99b97df7079a8", - "sha256:de61e424062173b4f70eec07e12469edde7e17fa180019a2a0d75c13a5c5dc57", - "sha256:e10e6a1ed2b8661201e79dff5531f8ad4cdd83548a0f81c95cf79b3184b20c33", - "sha256:e1a0ffc39f51aa5f5c22114a8f1906b3c17eba68c5babb86c5f77d8b1bba14d1", - "sha256:e22491d25f97199fc3581ad8dd8ce198d8c8fdb8dae80dea3512e1ce6d5fa99f", - "sha256:e626b864725680cd3904414d72e7b0bd81c0e5b2b53a5b30b4273034253bb41f", - "sha256:e8c71ea77536149e36c4c784f6d420ffd20bea041e3ba21ed021cb40ce58e2c9", - "sha256:e8d0f0eca087630d58b8c662085529781fd5dc80f0a54eda42d5c9029f812599", - "sha256:ea65b59882d5fa8c74a23f8960db579e5e341534934f43f3b18ec1839b893e41", - "sha256:ea93163472db26ac6043e8f7f93a05d9b59e0505c760da2a3cd22c7dd7111391", - "sha256:eab75a8569a095f2ad470b342f2751d9902f7944704f0571c8af46bede438475", - "sha256:ed8313809571a5463fd7db43aaca68ecb43ca7a58f5b23b6e6c6c5d02bdc7882", - "sha256:ef5fddfb264e89c435be4adb3953cef5d2936fdeb4463b4161a6ba2f22e7b740", - "sha256:ef750a20de1b65657a1425f77c525b0183eac63fe7b8f5ac0dd16f3668d3e64f", - "sha256:efb9ece97e696bb56e31166a9dd7919f8f0c6b31967b454718c6509f29ef6fee", - "sha256:f4c179a7aeae10ddf44c6bac87938134c1379c49c884529f090f9bf05566c836", - "sha256:f602881d80ee4228a2355c68da6b296a296cd22bbb91e5418d54577bbf17fa7c", - "sha256:fc2200e79d75b5238c8d69f6a30f8284290c777039d331e7340b6c17cad24a5a", - "sha256:fcc1ebb7561a3e24a6588f7c6ded15d80aec22c66a070c757559b57b17ffd1cb" + "sha256:0290712eb5603a725769b5d857f7cf15cf6ca93dda3128065bbafe6fdb709beb", + "sha256:032c242a595629aacace44128f9795110513ad27217b091e834edec2fb09e800", + "sha256:08832078767545c5ee12561ce980714e1e4c6619b5b1e9a10248de60cddfa1fd", + "sha256:08b335fb0c45f0a9e2478a9ece6a1bfb00b6f4c4780f9be3cf36479c5d8dd374", + "sha256:0b70c1f800059c92479dc94dda41288fd6607f741f9b1b8f89a21a86428f6383", + "sha256:0d9f8930092558fd15c9e07198625efb698f7cc00b3dc311c83eeec2540226a8", + "sha256:181ee352691c4434eb1c01802e9daa5edcc1007ff15023a320e2693fed6a661b", + "sha256:19f5aa7f5078d35ed8e344bcba40f35bc95f9176dddb33fc4f2084e04289fa63", + "sha256:1a3b2583c86bbfbf417304eeb13400ce7f8725376dc7d3efbf35dc5d7052ad48", + "sha256:1c9a1dc5e898ce30e2f9c0aa57181cddd4532b22b7780549441d6429d22d3b58", + "sha256:1f36a1e80ef4ed1996445698fd91e0d3e54738bf597c9995118b92da537d7a28", + "sha256:20147996376be452cd82cd6c17701daba69a849dc143270fa10fe067bb34562a", + "sha256:249c8e0055ca597707d71c5ad85fd2a1c8fdb99386a8c6c257e1b47b67a9bec1", + "sha256:2647192facf63be9ed2d7a49ceb07efe01dc6cfb083bd2cc53c418437400cb99", + "sha256:264f3a5906c62b9df3a00ad35f6da1987d321a053895bd85f9d5c708de5c0fbf", + "sha256:2abd669a39be69cdfe145927c7eb53a875b157740bf1e2d49e9619fc6f43362e", + "sha256:2b2415d5a7b7ee96aa3a54d4775c1fec140476a17ee12353806297e900eaeddc", + "sha256:2c173f529666bab8e3f948b74c6d91afa22ea147e6ebae49a48229d9020a47c4", + "sha256:2da81c1492291c1a90987d76a47c7b2d310661bf7c93a9de0511e27b796a8b46", + "sha256:2eca04a365be380ca1f8fa48b334462e19e3382c0bb7386444d8ca43aa01c481", + "sha256:37b08df45f02ff1866043b95096cbe91ac99de05936dd09d6611987a82a3306a", + "sha256:37f79f4f1f06cc96151f4a187528c3fd4a7e1065538a4af9eb68c642365957f7", + "sha256:3dd5fb7737224e1497c886fb3ca681c15d9c00c76171f53b3c3cc8d16ccfa7fb", + "sha256:3e3ac5b602fea378243f993d8b707189f9061e55ebb4e56cb9fdef8166060f28", + "sha256:3f55ae773abd96b1de25fc5c3fb356f491bd19116f8f854ba705beffc1ddc3c5", + "sha256:4011d5c854aa804c833331d38a2b6f6f2fe58a90c9f615afdb7aa7cf9d31f721", + "sha256:4145172ab59b6c27695db6d78d040795f635cba732cead19c78cede74800949a", + "sha256:42b9535aa22ab023704cfc6533e968f7e420affe802d85e956d8a7b4c0b0b5ea", + "sha256:46a07a258bda12270de02b34c4884f200f864bba3dcd6e3a37fef36a168b859d", + "sha256:4f13d3f6585bd07657a603780e99beda96a36c86acaba841f131e81393958336", + "sha256:528e2afaa56d815d2601b857644aeb395afe7e59212ab0659906dc29ae68d9a6", + "sha256:545e94c84575057d3d5c62634611858dac859702b1519b6ffc58eca7fb1adfcf", + "sha256:577d40a72550eac1386b77b43836151cb61ff6700adacda2ad4d883ca5a0b6f2", + "sha256:5967fa631d0ed9f8511dede08bc943a9727c949d05d1efac4ac82b2938024fb7", + "sha256:5b769396eb358d6b55dbf78f3f7ca631ca1b2fe02136faad5af74f0111b4b6b7", + "sha256:63c9e2794329ef070844ff9bfc012004aeddc0468dc26970953709723f76c8a5", + "sha256:6574f619e8734140d96c59bfa8a6a6e7a3336820ccd1bfd95ffa610673b650a2", + "sha256:6bfe72b249264cc1ff2f3629be240d7d2fdc778d9d298087cdec8524c91cd11f", + "sha256:736817dbbbd030a69a1faf5413a319976c9c8ba8cdcfa98c022d3b6b2e01eca6", + "sha256:74a2044b870df7c9360bb3ce7e12f9ddf8e72e49cd3a353a1528cbf166ad2383", + "sha256:74be3b215a5695690a0f1a9f68b1d1c93f8caad52e23242fcb8ba56aaf060281", + "sha256:76a8374b294e4ccb39ccaf11d39a0537ed107534139c00b4393ca3b542cc66e5", + "sha256:7ba239bb37663b2b4cd08e703e79e13321512dccd8e5f0e9451d9e53a6b8509a", + "sha256:7c40851b659d958c5245c1236e34f0d065cc53dca8d978b49a032c8e0adfda6e", + "sha256:7cf241dbb50ea71c2e628ab2a32b5bfcd36e199152fc44e5c1edb0b773f1583e", + "sha256:7cfae77da92a20f56cf89739a557b76e5c6edc094f6ad5c090b9e15fbbfcd1a4", + "sha256:7d152ec7bb431040af2500e01436c9aa0d993f243346f0594a15755016bf0be1", + "sha256:80080972e1d000ad0341c7cc58b6855c80bd887675f92871221451d13a975072", + "sha256:82dbcd6463e580bcfb7561cece35046aaabeac5a9ddb775020160b14e6c58a5d", + "sha256:8308a8d49d1354278d5c068c888a58d7158a419b2e4d87c7839ed3641498790c", + "sha256:839676475ac2ccd1532d36af3d10d290a2ca149b702ed464131e450a767550df", + "sha256:83feb0f682d75a09ddc11aa37ba5c07dd9b824b22915207f6176ea458474ff75", + "sha256:88956c993a20201744282362e3fd30962a9d86dc4f1dcf2bdb31fab27821b61f", + "sha256:8a6ad8429340e0a4de89353447c6441329def3632e7b2293a7d6e873217d3c2b", + "sha256:8ba9fbc5d6e36bfeb5292530321cc56c4ef3f98048647fabd8f57543c34174ec", + "sha256:8c1f6c8df23be165eb0cb78f305483d00c6827a191e3a38394c658d5b9c80bbd", + "sha256:91276caef95556faeb4b8f09fe4439670d3d6206fee78d47ddb6e6de837f0b4d", + "sha256:960e7e460fda2d0af18c75585bbe0c99f90b8f09963844618a621b804f8c3abe", + "sha256:9656a09653b18b80764647d585750df2dff8928e03a706763ab40ec8c4872acc", + "sha256:9cd935c0220d012a27c20135c140f9cdcbc6249d5954345c81bfb714071b985c", + "sha256:a2b3c79586636f1fa69a7bd59c87c15fca80c0d34b5c003d57f2f326e5276575", + "sha256:a4b9d3f5c48bbe8d9e3758e498b3c34863f2c9b1ac57a4e6310183740e59c980", + "sha256:a8c2bf286e5d755a075e5e97ba56b3de08cccdad6b323ab0b21cc98875176b03", + "sha256:a90031658805c63fe488f8e9e7a88b260ea121ba3ee9cdabcece9c9ddb50da39", + "sha256:ad666a904212aa9a6c77da7dce9d5170008cda76b7776e6731928b3f8a0d40fa", + "sha256:af2d1648eb625a460eee07d3e1ea3a4a6e84a1fb3a107f6a8e95ac19f7dcce67", + "sha256:b3d4b390ee70ca9263b331ccfaf9819ee20e90dfd0201a295e23eb64a005dbef", + "sha256:ba4432301ad7eeb1b00848cf46fae0e5fecfd18a8cb5fdcf856c67985f79ecc7", + "sha256:bc3179e0815827cf963e634095ae5715ee73a5af61defbc8d6ca79f1bdae1d1d", + "sha256:c5fd099acaee2325f01281a130a39da08d885e4dedf01b84bf156ec2737d78fe", + "sha256:c797ea56f36c6f248656f0223b11307fdf4a1886f3555eba371f34152b07677f", + "sha256:cd4ea56c9542ad0091dfdef3e8572ae7a746e1e91eb56c9e08b8d0808b40f1d1", + "sha256:cdd6f8738e1f1d9df5b1603bb03cb30e442710e5672262b95d0f9fcb4edb0dab", + "sha256:d0580faeb9def6d0beb7aa666294d5604e569c4e24111ada423cf9936768d95c", + "sha256:d11afdc5992bbd7af60ed5eb519873690d921425299f51d80aa3099ed49f2bcc", + "sha256:d1d388d2f5f5a6065cf83c54dd12112b7389095669ff395e632003ae8999c6b8", + "sha256:d20da6b4c7aa9ee75ad0730beaba15d65157f5beeaca54a038bb968f92bf3ce3", + "sha256:d22e0660de24bd8e9ac82f4230a22a5fe4e397265709289d61d5fb333839ba50", + "sha256:d22f2cb82e0b40e427a74a93c9a4231335bbc548aed79955dde0b64ea7f88146", + "sha256:d4fa1eeb9bea6d9b64ac91ec51ee94cc4fc744955df5be393e1c923c920db2b0", + "sha256:d9793d46d3e6522ae58e9321032827c9c0df1e56cbe5d3de965facb311aed6aa", + "sha256:dab979662da1c9fbb464e310c0b06cb5f1d174d09a462553af78f0bfb3e01920", + "sha256:db8d0f0ad92f74feb61c4e4a71f1d573ef37c22ef4dc19cab93e501bfdad8cbd", + "sha256:df2af1180b8eeececf4f819d22cc0668bfadadfd038b19a90bd2fb2ee419ec6f", + "sha256:dfb5d2ab183c0efe5e7b8917e4eaa2e837aacafad8a69b89aa6bc81550eed857", + "sha256:e04f8c76b8d5c70695b4e8f1d0b391d8ef91df00ef488c6c1ffb910176459bc6", + "sha256:e4a45ba34f904062c63049a760790c6a2fa7a4cc4bd160d8af243b12371aaa05", + "sha256:e9be1f7c5f9673616f875299339984da9447a40e3aea927750c843d6e5e2e029", + "sha256:edc91c50e17f5cd945d821f0f1af830522dba0c10267c3aab186dc3dbaab8def", + "sha256:ee70ee5f4144a45a9e6169000b5b525d82673d5dab9f7587eccc92794814e7ac", + "sha256:f1059ca9a51c936c9a8d46fbc2c9a6b4c15ab3f13a97f1ad32f024b39666ba85", + "sha256:f47eef55297799956464efc00c74ae55c48a7b68236856d56183fe1ddf866205", + "sha256:f4ae6f423cb7d1c6256b7482025ace2825728f53b7ac58bcd574de6ee9d242c2", + "sha256:f4b15a163448ec79241fb2f1bc5a8ae1a4a304f7a48d948d208a2935b26bf8a5", + "sha256:f55601fb58f92e4f4f1d05d80c24cb77505dc42103ddfd63ddfdc51d3da46fa2", + "sha256:fa84bbe22ffa108f91631935c28a623001e335d66e393438258501e618fb0dde", + "sha256:faa12a9f34671a30ea6bb027f04ec4e1fb8fa3fb3ed030893e729d4d0f3a9791", + "sha256:fcfd5f91b882eedf8d9601bd21261d6ce0e61a8c66a7152d1f5df08d3f643ab1", + "sha256:fe30ef31172bdcf946502a945faad110e8fff88c32c4bec9a593df0280e64d8a" ], "markers": "python_version >= '3.8'", - "version": "==0.10.3" + "version": "==0.13.1" }, "six": { "hashes": [ @@ -631,50 +666,58 @@ }, "sqlalchemy": { "hashes": [ - "sha256:014794b60d2021cc8ae0f91d4d0331fe92691ae5467a00841f7130fe877b678e", - "sha256:0268256a34806e5d1c8f7ee93277d7ea8cc8ae391f487213139018b6805aeaf6", - "sha256:05b971ab1ac2994a14c56b35eaaa91f86ba080e9ad481b20d99d77f381bb6258", - "sha256:141675dae56522126986fa4ca713739d00ed3a6f08f3c2eb92c39c6dfec463ce", - "sha256:1e7dc99b23e33c71d720c4ae37ebb095bebebbd31a24b7d99dfc4753d2803ede", - "sha256:2e617727fe4091cedb3e4409b39368f424934c7faa78171749f704b49b4bb4ce", - "sha256:3cf229704074bce31f7f47d12883afee3b0a02bb233a0ba45ddbfe542939cca4", - "sha256:3eb7c03fe1cd3255811cd4e74db1ab8dca22074d50cd8937edf4ef62d758cdf4", - "sha256:3f7d57a7e140efe69ce2d7b057c3f9a595f98d0bbdfc23fd055efdfbaa46e3a5", - "sha256:419b1276b55925b5ac9b4c7044e999f1787c69761a3c9756dec6e5c225ceca01", - "sha256:44ac5c89b6896f4740e7091f4a0ff2e62881da80c239dd9408f84f75a293dae9", - "sha256:4615623a490e46be85fbaa6335f35cf80e61df0783240afe7d4f544778c315a9", - "sha256:50a69067af86ec7f11a8e50ba85544657b1477aabf64fa447fd3736b5a0a4f67", - "sha256:513fd5b6513d37e985eb5b7ed89da5fd9e72354e3523980ef00d439bc549c9e9", - "sha256:6ff3dc2f60dbf82c9e599c2915db1526d65415be323464f84de8db3e361ba5b9", - "sha256:73c079e21d10ff2be54a4699f55865d4b275fd6c8bd5d90c5b1ef78ae0197301", - "sha256:7614f1eab4336df7dd6bee05bc974f2b02c38d3d0c78060c5faa4cd1ca2af3b8", - "sha256:785e2f2c1cb50d0a44e2cdeea5fd36b5bf2d79c481c10f3a88a8be4cfa2c4615", - "sha256:7ca38746eac23dd7c20bec9278d2058c7ad662b2f1576e4c3dbfcd7c00cc48fa", - "sha256:7f0c4ee579acfe6c994637527c386d1c22eb60bc1c1d36d940d8477e482095d4", - "sha256:87bf91ebf15258c4701d71dcdd9c4ba39521fb6a37379ea68088ce8cd869b446", - "sha256:89e274604abb1a7fd5c14867a412c9d49c08ccf6ce3e1e04fffc068b5b6499d4", - "sha256:8c323813963b2503e54d0944813cd479c10c636e3ee223bcbd7bd478bf53c178", - "sha256:a95aa0672e3065d43c8aa80080cdd5cc40fe92dc873749e6c1cf23914c4b83af", - "sha256:af520a730d523eab77d754f5cf44cc7dd7ad2d54907adeb3233177eeb22f271b", - "sha256:b19ae41ef26c01a987e49e37c77b9ad060c59f94d3b3efdfdbf4f3daaca7b5fe", - "sha256:b4eae01faee9f2b17f08885e3f047153ae0416648f8e8c8bd9bc677c5ce64be9", - "sha256:b69f1f754d92eb1cc6b50938359dead36b96a1dcf11a8670bff65fd9b21a4b09", - "sha256:b977bfce15afa53d9cf6a632482d7968477625f030d86a109f7bdfe8ce3c064a", - "sha256:bf8eebccc66829010f06fbd2b80095d7872991bfe8415098b9fe47deaaa58063", - "sha256:c111cd40910ffcb615b33605fc8f8e22146aeb7933d06569ac90f219818345ef", - "sha256:c2d494b6a2a2d05fb99f01b84cc9af9f5f93bf3e1e5dbdafe4bed0c2823584c1", - "sha256:c9cba4e7369de663611ce7460a34be48e999e0bbb1feb9130070f0685e9a6b66", - "sha256:cca720d05389ab1a5877ff05af96551e58ba65e8dc65582d849ac83ddde3e231", - "sha256:ccb99c3138c9bde118b51a289d90096a3791658da9aea1754667302ed6564f6e", - "sha256:d59cb9e20d79686aa473e0302e4a82882d7118744d30bb1dfb62d3c47141b3ec", - "sha256:e36339a68126ffb708dc6d1948161cea2a9e85d7d7b0c54f6999853d70d44430", - "sha256:ea7da25ee458d8f404b93eb073116156fd7d8c2a776d8311534851f28277b4ce", - "sha256:f9fefd6298433b6e9188252f3bff53b9ff0443c8fde27298b8a2b19f6617eeb9", - "sha256:fb87f763b5d04a82ae84ccff25554ffd903baafba6698e18ebaf32561f2fe4aa", - "sha256:fc6b15465fabccc94bf7e38777d665b6a4f95efd1725049d6184b3a39fd54880" + "sha256:0666031df46b9badba9bed00092a1ffa3aa063a5e68fa244acd9f08070e936d3", + "sha256:0a8c6aa506893e25a04233bc721c6b6cf844bafd7250535abb56cb6cc1368884", + "sha256:0e680527245895aba86afbd5bef6c316831c02aa988d1aad83c47ffe92655e74", + "sha256:14aebfe28b99f24f8a4c1346c48bc3d63705b1f919a24c27471136d2f219f02d", + "sha256:1e018aba8363adb0599e745af245306cb8c46b9ad0a6fc0a86745b6ff7d940fc", + "sha256:227135ef1e48165f37590b8bfc44ed7ff4c074bf04dc8d6f8e7f1c14a94aa6ca", + "sha256:31952bbc527d633b9479f5f81e8b9dfada00b91d6baba021a869095f1a97006d", + "sha256:3e983fa42164577d073778d06d2cc5d020322425a509a08119bdcee70ad856bf", + "sha256:42d0b0290a8fb0165ea2c2781ae66e95cca6e27a2fbe1016ff8db3112ac1e846", + "sha256:42ede90148b73fe4ab4a089f3126b2cfae8cfefc955c8174d697bb46210c8306", + "sha256:4895a63e2c271ffc7a81ea424b94060f7b3b03b4ea0cd58ab5bb676ed02f4221", + "sha256:4af79c06825e2836de21439cb2a6ce22b2ca129bad74f359bddd173f39582bf5", + "sha256:5f94aeb99f43729960638e7468d4688f6efccb837a858b34574e01143cf11f89", + "sha256:616fe7bcff0a05098f64b4478b78ec2dfa03225c23734d83d6c169eb41a93e55", + "sha256:62d9e964870ea5ade4bc870ac4004c456efe75fb50404c03c5fd61f8bc669a72", + "sha256:638c2c0b6b4661a4fd264f6fb804eccd392745c5887f9317feb64bb7cb03b3ea", + "sha256:63bfc3acc970776036f6d1d0e65faa7473be9f3135d37a463c5eba5efcdb24c8", + "sha256:6463aa765cf02b9247e38b35853923edbf2f6fd1963df88706bc1d02410a5577", + "sha256:64ac935a90bc479fee77f9463f298943b0e60005fe5de2aa654d9cdef46c54df", + "sha256:683ef58ca8eea4747737a1c35c11372ffeb84578d3aab8f3e10b1d13d66f2bc4", + "sha256:75eefe09e98043cff2fb8af9796e20747ae870c903dc61d41b0c2e55128f958d", + "sha256:787af80107fb691934a01889ca8f82a44adedbf5ef3d6ad7d0f0b9ac557e0c34", + "sha256:7c424983ab447dab126c39d3ce3be5bee95700783204a72549c3dceffe0fc8f4", + "sha256:7e0dc9031baa46ad0dd5a269cb7a92a73284d1309228be1d5935dac8fb3cae24", + "sha256:87a3d6b53c39cd173990de2f5f4b83431d534a74f0e2f88bd16eabb5667e65c6", + "sha256:89a01238fcb9a8af118eaad3ffcc5dedaacbd429dc6fdc43fe430d3a941ff965", + "sha256:9585b646ffb048c0250acc7dad92536591ffe35dba624bb8fd9b471e25212a35", + "sha256:964971b52daab357d2c0875825e36584d58f536e920f2968df8d581054eada4b", + "sha256:967c0b71156f793e6662dd839da54f884631755275ed71f1539c95bbada9aaab", + "sha256:9ca922f305d67605668e93991aaf2c12239c78207bca3b891cd51a4515c72e22", + "sha256:a86cb7063e2c9fb8e774f77fbf8475516d270a3e989da55fa05d08089d77f8c4", + "sha256:aeb397de65a0a62f14c257f36a726945a7f7bb60253462e8602d9b97b5cbe204", + "sha256:b41f5d65b54cdf4934ecede2f41b9c60c9f785620416e8e6c48349ab18643855", + "sha256:bd45a5b6c68357578263d74daab6ff9439517f87da63442d244f9f23df56138d", + "sha256:c14eba45983d2f48f7546bb32b47937ee2cafae353646295f0e99f35b14286ab", + "sha256:c1bda93cbbe4aa2aa0aa8655c5aeda505cd219ff3e8da91d1d329e143e4aff69", + "sha256:c4722f3bc3c1c2fcc3702dbe0016ba31148dd6efcd2a2fd33c1b4897c6a19693", + "sha256:c80c38bd2ea35b97cbf7c21aeb129dcbebbf344ee01a7141016ab7b851464f8e", + "sha256:cabafc7837b6cec61c0e1e5c6d14ef250b675fa9c3060ed8a7e38653bd732ff8", + "sha256:cc1d21576f958c42d9aec68eba5c1a7d715e5fc07825a629015fe8e3b0657fb0", + "sha256:d0f7fb0c7527c41fa6fcae2be537ac137f636a41b4c5a4c58914541e2f436b45", + "sha256:d4041ad05b35f1f4da481f6b811b4af2f29e83af253bf37c3c4582b2c68934ab", + "sha256:d5578e6863eeb998980c212a39106ea139bdc0b3f73291b96e27c929c90cd8e1", + "sha256:e3b5036aa326dc2df50cba3c958e29b291a80f604b1afa4c8ce73e78e1c9f01d", + "sha256:e599a51acf3cc4d31d1a0cf248d8f8d863b6386d2b6782c5074427ebb7803bda", + "sha256:f3420d00d2cb42432c1d0e44540ae83185ccbbc67a6054dcc8ab5387add6620b", + "sha256:f48ed89dd11c3c586f45e9eec1e437b355b3b6f6884ea4a4c3111a3358fd0c18", + "sha256:f508ba8f89e0a5ecdfd3761f82dda2a3d7b678a626967608f4273e0dba8f07ac", + "sha256:fd54601ef9cc455a0c61e5245f690c8a3ad67ddb03d3b91c361d076def0b4c60" ], "markers": "python_version >= '3.7'", - "version": "==2.0.21" + "version": "==2.0.23" }, "sqlalchemy-utils": { "hashes": [ @@ -682,6 +725,7 @@ "sha256:a2181bff01eeb84479e38571d2c0718eb52042f9afd8c194d0d02877e84b7d74" ], "index": "pypi", + "markers": "python_version >= '3.6'", "version": "==0.41.1" }, "testcontainers-core": { @@ -696,8 +740,17 @@ "sha256:0bdf270b5b7f53915832f7c31dd2bd3ffdc20b534ea6b32231cc7003049bd0e1" ], "index": "pypi", + "markers": "python_version >= '3.7'", "version": "==0.0.1rc1" }, + "tomli": { + "hashes": [ + "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", + "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f" + ], + "markers": "python_version < '3.11'", + "version": "==2.0.1" + }, "typing-extensions": { "hashes": [ "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0", @@ -708,108 +761,103 @@ }, "urllib3": { "hashes": [ - "sha256:24d6a242c28d29af46c3fae832c36db3bbebcc533dd1bb549172cd739c82df21", - "sha256:94a757d178c9be92ef5539b8840d48dc9cf1b2709c9d6b588232a055c524458b" + "sha256:55901e917a5896a349ff771be919f8bd99aff50b79fe58fec595eb37bbc56bb3", + "sha256:df7aa8afb0148fa78488e7899b2c59b5f4ffcfa82e6c54ccb9dd37c1d7b52d54" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==1.26.17" + "markers": "python_version >= '3.8'", + "version": "==2.1.0" }, "websocket-client": { "hashes": [ - "sha256:3aad25d31284266bcfcfd1fd8a743f63282305a364b8d0948a43bd606acc652f", - "sha256:6cfc30d051ebabb73a5fa246efdcc14c8fbebbd0330f8984ac3bb6d9edd2ad03" + "sha256:084072e0a7f5f347ef2ac3d8698a5e0b4ffbfcab607628cadabc650fc9a83a24", + "sha256:b3324019b3c28572086c4a319f91d1dcd44e6e11cd340232978c684a7650d0df" ], "markers": "python_version >= '3.8'", - "version": "==1.6.3" + "version": "==1.6.4" }, "werkzeug": { "hashes": [ - "sha256:3ffff4dcc32db52ef3cc94dff3000a3c2846890f3a5a51800a27b909c5e770f0", - "sha256:cbb2600f7eabe51dbc0502f58be0b3e1b96b893b05695ea2b35b43d4de2d9962" + "sha256:507e811ecea72b18a404947aded4b3390e1db8f826b494d76550ef45bb3b1dcc", + "sha256:90a285dc0e42ad56b34e696398b8122ee4c681833fb35b8334a095d82c56da10" ], "markers": "python_version >= '3.8'", - "version": "==3.0.0" + "version": "==3.0.1" }, "wrapt": { "hashes": [ - "sha256:02fce1852f755f44f95af51f69d22e45080102e9d00258053b79367d07af39c0", - "sha256:077ff0d1f9d9e4ce6476c1a924a3332452c1406e59d90a2cf24aeb29eeac9420", - "sha256:078e2a1a86544e644a68422f881c48b84fef6d18f8c7a957ffd3f2e0a74a0d4a", - "sha256:0970ddb69bba00670e58955f8019bec4a42d1785db3faa043c33d81de2bf843c", - "sha256:1286eb30261894e4c70d124d44b7fd07825340869945c79d05bda53a40caa079", - "sha256:21f6d9a0d5b3a207cdf7acf8e58d7d13d463e639f0c7e01d82cdb671e6cb7923", - "sha256:230ae493696a371f1dbffaad3dafbb742a4d27a0afd2b1aecebe52b740167e7f", - "sha256:26458da5653aa5b3d8dc8b24192f574a58984c749401f98fff994d41d3f08da1", - "sha256:2cf56d0e237280baed46f0b5316661da892565ff58309d4d2ed7dba763d984b8", - "sha256:2e51de54d4fb8fb50d6ee8327f9828306a959ae394d3e01a1ba8b2f937747d86", - "sha256:2fbfbca668dd15b744418265a9607baa970c347eefd0db6a518aaf0cfbd153c0", - "sha256:38adf7198f8f154502883242f9fe7333ab05a5b02de7d83aa2d88ea621f13364", - "sha256:3a8564f283394634a7a7054b7983e47dbf39c07712d7b177b37e03f2467a024e", - "sha256:3abbe948c3cbde2689370a262a8d04e32ec2dd4f27103669a45c6929bcdbfe7c", - "sha256:3bbe623731d03b186b3d6b0d6f51865bf598587c38d6f7b0be2e27414f7f214e", - "sha256:40737a081d7497efea35ab9304b829b857f21558acfc7b3272f908d33b0d9d4c", - "sha256:41d07d029dd4157ae27beab04d22b8e261eddfc6ecd64ff7000b10dc8b3a5727", - "sha256:46ed616d5fb42f98630ed70c3529541408166c22cdfd4540b88d5f21006b0eff", - "sha256:493d389a2b63c88ad56cdc35d0fa5752daac56ca755805b1b0c530f785767d5e", - "sha256:4ff0d20f2e670800d3ed2b220d40984162089a6e2c9646fdb09b85e6f9a8fc29", - "sha256:54accd4b8bc202966bafafd16e69da9d5640ff92389d33d28555c5fd4f25ccb7", - "sha256:56374914b132c702aa9aa9959c550004b8847148f95e1b824772d453ac204a72", - "sha256:578383d740457fa790fdf85e6d346fda1416a40549fe8db08e5e9bd281c6a475", - "sha256:58d7a75d731e8c63614222bcb21dd992b4ab01a399f1f09dd82af17bbfc2368a", - "sha256:5c5aa28df055697d7c37d2099a7bc09f559d5053c3349b1ad0c39000e611d317", - "sha256:5fc8e02f5984a55d2c653f5fea93531e9836abbd84342c1d1e17abc4a15084c2", - "sha256:63424c681923b9f3bfbc5e3205aafe790904053d42ddcc08542181a30a7a51bd", - "sha256:64b1df0f83706b4ef4cfb4fb0e4c2669100fd7ecacfb59e091fad300d4e04640", - "sha256:74934ebd71950e3db69960a7da29204f89624dde411afbfb3b4858c1409b1e98", - "sha256:75669d77bb2c071333417617a235324a1618dba66f82a750362eccbe5b61d248", - "sha256:75760a47c06b5974aa5e01949bf7e66d2af4d08cb8c1d6516af5e39595397f5e", - "sha256:76407ab327158c510f44ded207e2f76b657303e17cb7a572ffe2f5a8a48aa04d", - "sha256:76e9c727a874b4856d11a32fb0b389afc61ce8aaf281ada613713ddeadd1cfec", - "sha256:77d4c1b881076c3ba173484dfa53d3582c1c8ff1f914c6461ab70c8428b796c1", - "sha256:780c82a41dc493b62fc5884fb1d3a3b81106642c5c5c78d6a0d4cbe96d62ba7e", - "sha256:7dc0713bf81287a00516ef43137273b23ee414fe41a3c14be10dd95ed98a2df9", - "sha256:7eebcdbe3677e58dd4c0e03b4f2cfa346ed4049687d839adad68cc38bb559c92", - "sha256:896689fddba4f23ef7c718279e42f8834041a21342d95e56922e1c10c0cc7afb", - "sha256:96177eb5645b1c6985f5c11d03fc2dbda9ad24ec0f3a46dcce91445747e15094", - "sha256:96e25c8603a155559231c19c0349245eeb4ac0096fe3c1d0be5c47e075bd4f46", - "sha256:9d37ac69edc5614b90516807de32d08cb8e7b12260a285ee330955604ed9dd29", - "sha256:9ed6aa0726b9b60911f4aed8ec5b8dd7bf3491476015819f56473ffaef8959bd", - "sha256:a487f72a25904e2b4bbc0817ce7a8de94363bd7e79890510174da9d901c38705", - "sha256:a4cbb9ff5795cd66f0066bdf5947f170f5d63a9274f99bdbca02fd973adcf2a8", - "sha256:a74d56552ddbde46c246b5b89199cb3fd182f9c346c784e1a93e4dc3f5ec9975", - "sha256:a89ce3fd220ff144bd9d54da333ec0de0399b52c9ac3d2ce34b569cf1a5748fb", - "sha256:abd52a09d03adf9c763d706df707c343293d5d106aea53483e0ec8d9e310ad5e", - "sha256:abd8f36c99512755b8456047b7be10372fca271bf1467a1caa88db991e7c421b", - "sha256:af5bd9ccb188f6a5fdda9f1f09d9f4c86cc8a539bd48a0bfdc97723970348418", - "sha256:b02f21c1e2074943312d03d243ac4388319f2456576b2c6023041c4d57cd7019", - "sha256:b06fa97478a5f478fb05e1980980a7cdf2712015493b44d0c87606c1513ed5b1", - "sha256:b0724f05c396b0a4c36a3226c31648385deb6a65d8992644c12a4963c70326ba", - "sha256:b130fe77361d6771ecf5a219d8e0817d61b236b7d8b37cc045172e574ed219e6", - "sha256:b56d5519e470d3f2fe4aa7585f0632b060d532d0696c5bdfb5e8319e1d0f69a2", - "sha256:b67b819628e3b748fd3c2192c15fb951f549d0f47c0449af0764d7647302fda3", - "sha256:ba1711cda2d30634a7e452fc79eabcadaffedf241ff206db2ee93dd2c89a60e7", - "sha256:bbeccb1aa40ab88cd29e6c7d8585582c99548f55f9b2581dfc5ba68c59a85752", - "sha256:bd84395aab8e4d36263cd1b9308cd504f6cf713b7d6d3ce25ea55670baec5416", - "sha256:c99f4309f5145b93eca6e35ac1a988f0dc0a7ccf9ccdcd78d3c0adf57224e62f", - "sha256:ca1cccf838cd28d5a0883b342474c630ac48cac5df0ee6eacc9c7290f76b11c1", - "sha256:cd525e0e52a5ff16653a3fc9e3dd827981917d34996600bbc34c05d048ca35cc", - "sha256:cdb4f085756c96a3af04e6eca7f08b1345e94b53af8921b25c72f096e704e145", - "sha256:ce42618f67741d4697684e501ef02f29e758a123aa2d669e2d964ff734ee00ee", - "sha256:d06730c6aed78cee4126234cf2d071e01b44b915e725a6cb439a879ec9754a3a", - "sha256:d5fe3e099cf07d0fb5a1e23d399e5d4d1ca3e6dfcbe5c8570ccff3e9208274f7", - "sha256:d6bcbfc99f55655c3d93feb7ef3800bd5bbe963a755687cbf1f490a71fb7794b", - "sha256:d787272ed958a05b2c86311d3a4135d3c2aeea4fc655705f074130aa57d71653", - "sha256:e169e957c33576f47e21864cf3fc9ff47c223a4ebca8960079b8bd36cb014fd0", - "sha256:e20076a211cd6f9b44a6be58f7eeafa7ab5720eb796975d0c03f05b47d89eb90", - "sha256:e826aadda3cae59295b95343db8f3d965fb31059da7de01ee8d1c40a60398b29", - "sha256:eef4d64c650f33347c1f9266fa5ae001440b232ad9b98f1f43dfe7a79435c0a6", - "sha256:f2e69b3ed24544b0d3dbe2c5c0ba5153ce50dcebb576fdc4696d52aa22db6034", - "sha256:f87ec75864c37c4c6cb908d282e1969e79763e0d9becdfe9fe5473b7bb1e5f09", - "sha256:fbec11614dba0424ca72f4e8ba3c420dba07b4a7c206c8c8e4e73f2e98f4c559", - "sha256:fd69666217b62fa5d7c6aa88e507493a34dec4fa20c5bd925e4bc12fce586639" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==1.15.0" + "sha256:0d2691979e93d06a95a26257adb7bfd0c93818e89b1406f5a28f36e0d8c1e1fc", + "sha256:14d7dc606219cdd7405133c713f2c218d4252f2a469003f8c46bb92d5d095d81", + "sha256:1a5db485fe2de4403f13fafdc231b0dbae5eca4359232d2efc79025527375b09", + "sha256:1acd723ee2a8826f3d53910255643e33673e1d11db84ce5880675954183ec47e", + "sha256:1ca9b6085e4f866bd584fb135a041bfc32cab916e69f714a7d1d397f8c4891ca", + "sha256:1dd50a2696ff89f57bd8847647a1c363b687d3d796dc30d4dd4a9d1689a706f0", + "sha256:2076fad65c6736184e77d7d4729b63a6d1ae0b70da4868adeec40989858eb3fb", + "sha256:2a88e6010048489cda82b1326889ec075a8c856c2e6a256072b28eaee3ccf487", + "sha256:3ebf019be5c09d400cf7b024aa52b1f3aeebeff51550d007e92c3c1c4afc2a40", + "sha256:418abb18146475c310d7a6dc71143d6f7adec5b004ac9ce08dc7a34e2babdc5c", + "sha256:43aa59eadec7890d9958748db829df269f0368521ba6dc68cc172d5d03ed8060", + "sha256:44a2754372e32ab315734c6c73b24351d06e77ffff6ae27d2ecf14cf3d229202", + "sha256:490b0ee15c1a55be9c1bd8609b8cecd60e325f0575fc98f50058eae366e01f41", + "sha256:49aac49dc4782cb04f58986e81ea0b4768e4ff197b57324dcbd7699c5dfb40b9", + "sha256:5eb404d89131ec9b4f748fa5cfb5346802e5ee8836f57d516576e61f304f3b7b", + "sha256:5f15814a33e42b04e3de432e573aa557f9f0f56458745c2074952f564c50e664", + "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d", + "sha256:66027d667efe95cc4fa945af59f92c5a02c6f5bb6012bff9e60542c74c75c362", + "sha256:66dfbaa7cfa3eb707bbfcd46dab2bc6207b005cbc9caa2199bcbc81d95071a00", + "sha256:685f568fa5e627e93f3b52fda002c7ed2fa1800b50ce51f6ed1d572d8ab3e7fc", + "sha256:6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1", + "sha256:6a42cd0cfa8ffc1915aef79cb4284f6383d8a3e9dcca70c445dcfdd639d51267", + "sha256:6dcfcffe73710be01d90cae08c3e548d90932d37b39ef83969ae135d36ef3956", + "sha256:6f6eac2360f2d543cc875a0e5efd413b6cbd483cb3ad7ebf888884a6e0d2e966", + "sha256:72554a23c78a8e7aa02abbd699d129eead8b147a23c56e08d08dfc29cfdddca1", + "sha256:73870c364c11f03ed072dda68ff7aea6d2a3a5c3fe250d917a429c7432e15228", + "sha256:73aa7d98215d39b8455f103de64391cb79dfcad601701a3aa0dddacf74911d72", + "sha256:75ea7d0ee2a15733684badb16de6794894ed9c55aa5e9903260922f0482e687d", + "sha256:7bd2d7ff69a2cac767fbf7a2b206add2e9a210e57947dd7ce03e25d03d2de292", + "sha256:807cc8543a477ab7422f1120a217054f958a66ef7314f76dd9e77d3f02cdccd0", + "sha256:8e9723528b9f787dc59168369e42ae1c3b0d3fadb2f1a71de14531d321ee05b0", + "sha256:9090c9e676d5236a6948330e83cb89969f433b1943a558968f659ead07cb3b36", + "sha256:9153ed35fc5e4fa3b2fe97bddaa7cbec0ed22412b85bcdaf54aeba92ea37428c", + "sha256:9159485323798c8dc530a224bd3ffcf76659319ccc7bbd52e01e73bd0241a0c5", + "sha256:941988b89b4fd6b41c3f0bfb20e92bd23746579736b7343283297c4c8cbae68f", + "sha256:94265b00870aa407bd0cbcfd536f17ecde43b94fb8d228560a1e9d3041462d73", + "sha256:98b5e1f498a8ca1858a1cdbffb023bfd954da4e3fa2c0cb5853d40014557248b", + "sha256:9b201ae332c3637a42f02d1045e1d0cccfdc41f1f2f801dafbaa7e9b4797bfc2", + "sha256:a0ea261ce52b5952bf669684a251a66df239ec6d441ccb59ec7afa882265d593", + "sha256:a33a747400b94b6d6b8a165e4480264a64a78c8a4c734b62136062e9a248dd39", + "sha256:a452f9ca3e3267cd4d0fcf2edd0d035b1934ac2bd7e0e57ac91ad6b95c0c6389", + "sha256:a86373cf37cd7764f2201b76496aba58a52e76dedfaa698ef9e9688bfd9e41cf", + "sha256:ac83a914ebaf589b69f7d0a1277602ff494e21f4c2f743313414378f8f50a4cf", + "sha256:aefbc4cb0a54f91af643660a0a150ce2c090d3652cf4052a5397fb2de549cd89", + "sha256:b3646eefa23daeba62643a58aac816945cadc0afaf21800a1421eeba5f6cfb9c", + "sha256:b47cfad9e9bbbed2339081f4e346c93ecd7ab504299403320bf85f7f85c7d46c", + "sha256:b935ae30c6e7400022b50f8d359c03ed233d45b725cfdd299462f41ee5ffba6f", + "sha256:bb2dee3874a500de01c93d5c71415fcaef1d858370d405824783e7a8ef5db440", + "sha256:bc57efac2da352a51cc4658878a68d2b1b67dbe9d33c36cb826ca449d80a8465", + "sha256:bf5703fdeb350e36885f2875d853ce13172ae281c56e509f4e6eca049bdfb136", + "sha256:c31f72b1b6624c9d863fc095da460802f43a7c6868c5dda140f51da24fd47d7b", + "sha256:c5cd603b575ebceca7da5a3a251e69561bec509e0b46e4993e1cac402b7247b8", + "sha256:d2efee35b4b0a347e0d99d28e884dfd82797852d62fcd7ebdeee26f3ceb72cf3", + "sha256:d462f28826f4657968ae51d2181a074dfe03c200d6131690b7d65d55b0f360f8", + "sha256:d5e49454f19ef621089e204f862388d29e6e8d8b162efce05208913dde5b9ad6", + "sha256:da4813f751142436b075ed7aa012a8778aa43a99f7b36afe9b742d3ed8bdc95e", + "sha256:db2e408d983b0e61e238cf579c09ef7020560441906ca990fe8412153e3b291f", + "sha256:db98ad84a55eb09b3c32a96c576476777e87c520a34e2519d3e59c44710c002c", + "sha256:dbed418ba5c3dce92619656802cc5355cb679e58d0d89b50f116e4a9d5a9603e", + "sha256:dcdba5c86e368442528f7060039eda390cc4091bfd1dca41e8046af7c910dda8", + "sha256:decbfa2f618fa8ed81c95ee18a387ff973143c656ef800c9f24fb7e9c16054e2", + "sha256:e4fdb9275308292e880dcbeb12546df7f3e0f96c6b41197e0cf37d2826359020", + "sha256:eb1b046be06b0fce7249f1d025cd359b4b80fc1c3e24ad9eca33e0dcdb2e4a35", + "sha256:eb6e651000a19c96f452c85132811d25e9264d836951022d6e81df2fff38337d", + "sha256:ed867c42c268f876097248e05b6117a65bcd1e63b779e916fe2e33cd6fd0d3c3", + "sha256:edfad1d29c73f9b863ebe7082ae9321374ccb10879eeabc84ba3b69f2579d537", + "sha256:f2058f813d4f2b5e3a9eb2eb3faf8f1d99b81c3e51aeda4b168406443e8ba809", + "sha256:f6b2d0c6703c988d334f297aa5df18c45e97b0af3679bb75059e0e0bd8b1069d", + "sha256:f8212564d49c50eb4565e502814f694e240c55551a5f1bc841d4fcaabb0a9b8a", + "sha256:ffa565331890b90056c01db69c0fe634a776f8019c143a5ae265f9c6bc4bd6d4" + ], + "markers": "python_version >= '3.6'", + "version": "==1.16.0" } }, "develop": {} diff --git a/dbrepo-search-service/coverage.txt b/dbrepo-search-service/coverage.txt new file mode 100644 index 0000000000000000000000000000000000000000..9cb4528d61fadb6440e87a9edeae73624a38c217 --- /dev/null +++ b/dbrepo-search-service/coverage.txt @@ -0,0 +1,66 @@ +Name Stmts Miss Cover +-------------------------------------------------------------------------------------------- +/usr/lib/python3/dist-packages/six.py 504 247 51% +/usr/local/lib/python3.9/dist-packages/anyio/__init__.py 67 0 100% +/usr/local/lib/python3.9/dist-packages/anyio/_core/__init__.py 0 0 100% +/usr/local/lib/python3.9/dist-packages/anyio/_core/_eventloop.py 62 38 39% +/usr/local/lib/python3.9/dist-packages/anyio/_core/_exceptions.py 16 3 81% +/usr/local/lib/python3.9/dist-packages/anyio/_core/_fileio.py 306 161 47% +/usr/local/lib/python3.9/dist-packages/anyio/_core/_resources.py 7 3 57% +/usr/local/lib/python3.9/dist-packages/anyio/_core/_signals.py 7 1 86% +/usr/local/lib/python3.9/dist-packages/anyio/_core/_sockets.py 207 160 23% +/usr/local/lib/python3.9/dist-packages/anyio/_core/_streams.py 16 8 50% +/usr/local/lib/python3.9/dist-packages/anyio/_core/_subprocesses.py 38 27 29% +/usr/local/lib/python3.9/dist-packages/anyio/_core/_synchronization.py 241 145 40% +/usr/local/lib/python3.9/dist-packages/anyio/_core/_tasks.py 53 21 60% +/usr/local/lib/python3.9/dist-packages/anyio/_core/_testing.py 29 15 48% +/usr/local/lib/python3.9/dist-packages/anyio/_core/_typedattr.py 34 10 71% +/usr/local/lib/python3.9/dist-packages/anyio/abc/__init__.py 44 0 100% +/usr/local/lib/python3.9/dist-packages/anyio/abc/_eventloop.py 169 43 75% +/usr/local/lib/python3.9/dist-packages/anyio/abc/_resources.py 12 2 83% +/usr/local/lib/python3.9/dist-packages/anyio/abc/_sockets.py 76 25 67% +/usr/local/lib/python3.9/dist-packages/anyio/abc/_streams.py 55 10 82% +/usr/local/lib/python3.9/dist-packages/anyio/abc/_subprocesses.py 29 0 100% +/usr/local/lib/python3.9/dist-packages/anyio/abc/_tasks.py 27 3 89% +/usr/local/lib/python3.9/dist-packages/anyio/abc/_testing.py 18 2 89% +/usr/local/lib/python3.9/dist-packages/anyio/from_thread.py 180 127 29% +/usr/local/lib/python3.9/dist-packages/anyio/lowlevel.py 79 41 48% +/usr/local/lib/python3.9/dist-packages/anyio/pytest_plugin.py 95 65 32% +/usr/local/lib/python3.9/dist-packages/anyio/streams/__init__.py 0 0 100% +/usr/local/lib/python3.9/dist-packages/anyio/streams/memory.py 133 80 40% +/usr/local/lib/python3.9/dist-packages/anyio/streams/stapled.py 64 29 55% +/usr/local/lib/python3.9/dist-packages/anyio/streams/tls.py 139 85 39% +/usr/local/lib/python3.9/dist-packages/anyio/to_thread.py 10 2 80% +/usr/local/lib/python3.9/dist-packages/gunicorn/__init__.py 4 0 100% +/usr/local/lib/python3.9/dist-packages/gunicorn/config.py 1094 275 75% +/usr/local/lib/python3.9/dist-packages/gunicorn/errors.py 8 3 62% +/usr/local/lib/python3.9/dist-packages/gunicorn/http/__init__.py 3 0 100% +/usr/local/lib/python3.9/dist-packages/gunicorn/http/body.py 209 184 12% +/usr/local/lib/python3.9/dist-packages/gunicorn/http/errors.py 70 30 57% +/usr/local/lib/python3.9/dist-packages/gunicorn/http/message.py 239 209 13% +/usr/local/lib/python3.9/dist-packages/gunicorn/http/parser.py 29 19 34% +/usr/local/lib/python3.9/dist-packages/gunicorn/http/unreader.py 56 43 23% +/usr/local/lib/python3.9/dist-packages/gunicorn/http/wsgi.py 247 211 15% +/usr/local/lib/python3.9/dist-packages/gunicorn/reloader.py 78 55 29% +/usr/local/lib/python3.9/dist-packages/gunicorn/util.py 367 294 20% +/usr/local/lib/python3.9/dist-packages/gunicorn/workers/__init__.py 1 0 100% +/usr/local/lib/python3.9/dist-packages/gunicorn/workers/base.py 170 140 18% +/usr/local/lib/python3.9/dist-packages/gunicorn/workers/workertmp.py 33 21 36% +/usr/local/lib/python3.9/dist-packages/simplejson/__init__.py 80 57 29% +/usr/local/lib/python3.9/dist-packages/simplejson/compat.py 29 16 45% +/usr/local/lib/python3.9/dist-packages/simplejson/decoder.py 227 180 21% +/usr/local/lib/python3.9/dist-packages/simplejson/encoder.py 412 358 13% +/usr/local/lib/python3.9/dist-packages/simplejson/errors.py 29 23 21% +/usr/local/lib/python3.9/dist-packages/simplejson/raw_json.py 3 1 67% +/usr/local/lib/python3.9/dist-packages/simplejson/scanner.py 64 53 17% +/usr/local/lib/python3.9/dist-packages/sniffio/__init__.py 3 0 100% +/usr/local/lib/python3.9/dist-packages/sniffio/_impl.py 33 22 33% +/usr/local/lib/python3.9/dist-packages/sniffio/_version.py 1 0 100% +/usr/local/lib/python3.9/dist-packages/socks.py 445 355 20% +/usr/local/lib/python3.9/dist-packages/wrapt/__init__.py 7 0 100% +/usr/local/lib/python3.9/dist-packages/wrapt/arguments.py 16 13 19% +/usr/local/lib/python3.9/dist-packages/wrapt/decorators.py 192 105 45% +/usr/local/lib/python3.9/dist-packages/wrapt/importer.py 126 99 21% +/usr/local/lib/python3.9/dist-packages/wrapt/wrappers.py 508 341 33% +-------------------------------------------------------------------------------------------- +TOTAL 7500 4460 41% diff --git a/dbrepo-search-service/report.xml b/dbrepo-search-service/report.xml new file mode 100644 index 0000000000000000000000000000000000000000..a541716834b5e22f5010d937ffe27110e89866b2 --- /dev/null +++ b/dbrepo-search-service/report.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?><testsuites><testsuite name="pytest" errors="0" failures="1" skipped="0" tests="1" time="10.993" timestamp="2023-11-24T19:16:41.702399" hostname="medusa"><testcase classname="test.test_opensearch_client.DetermineDatatypesTest" name="test_textsearch" time="10.673"><failure message="RuntimeError: Working outside of application context. This typically means that you attempted to use functionality that needed the current application. To solve this, set up an application context with app.app_context(). See the documentation for more information.">self = <test.test_opensearch_client.DetermineDatatypesTest testMethod=test_textsearch> + + def test_textsearch(self): + print("search for entries that contain the word 'measurement data'") +> docIDs = opensearch_client.query_index_by_term_opensearch("", "measurement data", "contains") + +test/test_opensearch_client.py:18: +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ +app/opensearch_client.py:78: in query_index_by_term_opensearch + response = current_app.opensearch_client.search( +../../../.local/lib/python3.9/site-packages/werkzeug/local.py:311: in __get__ + obj = instance._get_current_object() +_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + + def _get_current_object() -> T: + try: + obj = local.get() + except LookupError: +> raise RuntimeError(unbound_message) from None +E RuntimeError: Working outside of application context. +E +E This typically means that you attempted to use functionality that needed +E the current application. To solve this, set up an application context +E with app.app_context(). See the documentation for more information. + +../../../.local/lib/python3.9/site-packages/werkzeug/local.py:508: RuntimeError</failure></testcase></testsuite></testsuites> \ No newline at end of file diff --git a/dbrepo-search-service/test/conftest.py b/dbrepo-search-service/test/conftest.py new file mode 100644 index 0000000000000000000000000000000000000000..a9715f0e435eafa9a59a8f86ee02830ce88e0a2d --- /dev/null +++ b/dbrepo-search-service/test/conftest.py @@ -0,0 +1,48 @@ +import os + +import pytest +import logging + +from testcontainers.opensearch import OpenSearchContainer + +@pytest.fixture(scope="session") +def session(request): + """ + Create one OpenSearch container per test run only (admin:admin) + :param request: / + :return: The OpenSearch container + """ + logging.debug("[fixture] creating opensearch container") + container = OpenSearchContainer() + logging.debug("[fixture] starting opensearch container") + container.start() + # set the environment for the client + os.environ['SEARCH_HOST'] = container.get_container_host_ip() + os.environ['SEARCH_PORT'] = container.get_exposed_port(9200) + client = container.get_client() + + # destructor + def stop_opensearch(): + container.stop() + + request.addfinalizer(stop_opensearch) + return container + + +# @pytest.fixture(scope="function", autouse=True) +# def cleanup(request, session): +# """ +# Clean up after each test by removing the buckets and re-adding them (=so they are empty again) +# :param request: / +# :param session: / +# :return: +# """ +# logging.info("[fixture] truncate buckets") +# for bucket in ["dbrepo-upload", "dbrepo-download"]: +# objects = [] +# for obj in session.get_client().list_objects(bucket): +# objects.append(DeleteObject(obj.object_name)) +# logging.info(f'request to remove objects {objects}') +# errors = session.get_client().remove_objects(bucket, objects) +# for error in errors: +# raise ConnectionError(f'Failed to delete object with key {error.object_name} of bucket {bucket}') diff --git a/dbrepo-search-service/test/test_opensearch_client.py b/dbrepo-search-service/test/test_opensearch_client.py index 6e9a40df9d9f9b2079d22552b04879643519df5f..397f250b29b291595f4670aa03a5f236d7b4081b 100644 --- a/dbrepo-search-service/test/test_opensearch_client.py +++ b/dbrepo-search-service/test/test_opensearch_client.py @@ -8,37 +8,43 @@ run the tests via 'pytest' or 'pipenv run pytest' * enter the port number manually (you prolly have to do that twice if you start it for the first time) * run the tests via 'pytest' or 'pipenv run pytest' """ -import requests -def send_request(path, data): - url = f"http://localhost:4000/api/search{path}" - response = requests.post(url, json=data) - if response.status_code == 200: - return response.json() - else: - raise Exception(response.json()) - - -def test_textsearch(): - print("search for entries that contain the word 'measurement data'") - data = {"search_term": "measurement data"} - result = send_request("", data) - docIDs = [hit["_source"]["docID"] for hit in result["hits"]["hits"]] - assert docIDs == [2] - - -def test_timerange(): - print("search for entries that have been created between January and September of 2023") - data = {"t1":"2023-01-01", - "t2":"2023-09-09"} - result = send_request("", data) - docIDs = [hit["_source"]["docID"] for hit in result["hits"]["hits"]] - assert docIDs == [1, 2] - - -def test_keywords(): - print("Search for entries form the user 'max") - data = {"field": "author", "value": "max"} - result = send_request("", data) - docIDs = [hit["_source"]["docID"] for hit in result["hits"]["hits"]] - assert docIDs == [2] - +import unittest +from requests import post + + +class DetermineDatatypesTest(unittest.TestCase): + + # @Test + def test_textsearch(self): + print("search for entries that contain the word 'measurement data'") + response = post(f"http://localhost:4000/api/search", json={ + "search_term": "measurement data" + }) + if response.status_code != 200: + self.fail("Invalid response code") + docIDs = [hit["_source"]["docID"] for hit in response.json()["hits"]["hits"]] + assert docIDs == [2] + + # @Test + def test_timerange(self): + print("search for entries that have been created between January and September of 2023") + response = post(f"http://localhost:4000/api/search", json={ + "t1": "2023-01-01", + "t2": "2023-09-09" + }) + if response.status_code != 200: + self.fail("Invalid response code") + docIDs = [hit["_source"]["docID"] for hit in response.json()["hits"]["hits"]] + assert docIDs == [1, 2] + + # @Test + def test_keywords(self): + print("Search for entries form the user 'max") + response = post(f"http://localhost:4000/api/search", json={ + "field": "author", + "value": "max" + }) + if response.status_code != 200: + self.fail("Invalid response code") + docIDs = [hit["_source"]["docID"] for hit in response.json()["hits"]["hits"]] + assert docIDs == [2] diff --git a/dbrepo-ui/api/container.service.js b/dbrepo-ui/api/container.service.js index 97d0fc7164efb1c783a52b60614547f17171dfe5..2d6021c89f6bf75e0f15b4a1950faf4d626ab933 100644 --- a/dbrepo-ui/api/container.service.js +++ b/dbrepo-ui/api/container.service.js @@ -11,7 +11,7 @@ class ContainerService { resolve(containers) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to load containers', error) Vue.$toast.error(`[${code}] Failed to load containers: ${message}`) reject(error) @@ -28,7 +28,7 @@ class ContainerService { resolve(container) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to load container', error) Vue.$toast.error(`[${code}] Failed to load container: ${message}`) reject(error) @@ -45,7 +45,7 @@ class ContainerService { resolve(image) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to load image', error) Vue.$toast.error(`[${code}] Failed to load image: ${message}`) reject(error) diff --git a/dbrepo-ui/api/database.service.js b/dbrepo-ui/api/database.service.js index f09e71de207c51f0c7722df6ac6e772b0fd8d2e9..7eb8f36f074b8d2197172665bd92df323f9dfe2c 100644 --- a/dbrepo-ui/api/database.service.js +++ b/dbrepo-ui/api/database.service.js @@ -11,7 +11,7 @@ class DatabaseService { resolve(databases) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to load databases', error) Vue.$toast.error(`[${code}] Failed to load databases: ${message}`) reject(error) @@ -28,7 +28,7 @@ class DatabaseService { resolve(databases) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to load my databases', error) Vue.$toast.error(`[${code}] Failed to load my databases: ${message}`) reject(error) @@ -45,7 +45,7 @@ class DatabaseService { resolve(count) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to count databases', error) Vue.$toast.error(`[${code}] Failed to count databases: ${message}`) reject(error) @@ -61,7 +61,7 @@ class DatabaseService { console.debug('response database', database) resolve(database) }).catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to load database', error) Vue.$toast.error(`[${code}] Failed to load database: ${message}`) reject(error) @@ -77,7 +77,7 @@ class DatabaseService { console.debug('response database', database) resolve(database) }).catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to create database', error) Vue.$toast.error(`[${code}] Failed to create database: ${message}`) reject(error) @@ -90,7 +90,7 @@ class DatabaseService { api.delete(`/api/database/${databaseId}`, { headers: { Accept: 'application/json' } }) .then(() => resolve()) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to delete database', error) Vue.$toast.error(`[${code}] Failed to delete database: ${message}`) reject(error) @@ -106,7 +106,7 @@ class DatabaseService { console.debug('response database', database) resolve(database) }).catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to modify database visibility', error) Vue.$toast.error(`[${code}] Failed to modify database visibility: ${message}`) reject(error) @@ -122,7 +122,7 @@ class DatabaseService { console.debug('response database', database) resolve(database) }).catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to modify database owner', error) Vue.$toast.error(`[${code}] Failed to modify database owner: ${message}`) reject(error) @@ -139,8 +139,8 @@ class DatabaseService { resolve(databases) }) .catch((error) => { - const { code, message, response } = error - const { status } = response + const { status } = error + const { code, message } = error.response.data if (status !== 401 && status !== 403 && status !== 405) { /* ignore no access errors */ console.error('Failed to check database access', error) Vue.$toast.error(`[${code}] Failed to check database access: ${message}`) @@ -159,7 +159,7 @@ class DatabaseService { resolve(database) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to modify database access', error) Vue.$toast.error(`[${code}] Failed to modify database access: ${message}`) reject(error) @@ -172,7 +172,7 @@ class DatabaseService { api.delete(`/api/database/${databaseId}/access/${userId}`, { headers: { Accept: 'application/json' } }) .then(() => resolve()) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to revoke database access', error) Vue.$toast.error(`[${code}] Failed to revoke database access: ${message}`) reject(error) @@ -185,7 +185,7 @@ class DatabaseService { api.post(`/api/database/${databaseId}/access/${userId}`, { type }, { headers: { Accept: 'application/json' } }) .then(() => resolve()) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to give database access', error) Vue.$toast.error(`[${code}] Failed to give database access: ${message}`) reject(error) @@ -202,7 +202,7 @@ class DatabaseService { resolve(licenses) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to load licenses', error) Vue.$toast.error(`[${code}] Failed to load licenses: ${message}`) reject(error) @@ -219,7 +219,7 @@ class DatabaseService { resolve(view) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to find view', error) Vue.$toast.error(`[${code}] Failed to find view: ${message}`) reject(error) @@ -236,7 +236,7 @@ class DatabaseService { resolve(view) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to delete view', error) Vue.$toast.error(`[${code}] Failed to delete view: ${message}`) reject(error) @@ -249,7 +249,7 @@ class DatabaseService { api.delete(`/api/database/${databaseId}/view/${viewId}`, { headers: { Accept: 'application/json' } }) .then(() => resolve()) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to delete view', error) Vue.$toast.error(`[${code}] Failed to delete view: ${message}`) reject(error) diff --git a/dbrepo-ui/api/identifier.service.js b/dbrepo-ui/api/identifier.service.js index 7708df66e4b32548938f74a5de262c2273db9b1a..c377aa03264748308cff834555cf8a5622527660 100644 --- a/dbrepo-ui/api/identifier.service.js +++ b/dbrepo-ui/api/identifier.service.js @@ -12,7 +12,9 @@ class IdentifierService { resolve(identifiers) }) .catch((error) => { + const { code, message } = error.response.data console.error('Failed to load identifiers', error) + Vue.$toast.error(`[${code}] Failed to load identifiers: ${message}`) reject(error) }) }) @@ -39,7 +41,9 @@ class IdentifierService { } }) .catch((error) => { - console.error('Failed to load metadata', error) + const { code, message } = error.response.data + console.error('Failed to load identifier metadata', error) + Vue.$toast.error(`[${code}] Failed to load identifier metadata: ${message}`) reject(error) }) }) @@ -54,7 +58,7 @@ class IdentifierService { resolve(identifier) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to load identifier', error) Vue.$toast.error(`[${code}] Failed to load identifier: ${message}`) reject(error) @@ -71,7 +75,7 @@ class IdentifierService { resolve(identifier) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to create identifier', error) Vue.$toast.error(`[${code}] Failed to create identifier: ${message}`) reject(error) @@ -88,7 +92,7 @@ class IdentifierService { resolve(identifier) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to update identifier', error) Vue.$toast.error(`[${code}] Failed to update identifier: ${message}`) reject(error) @@ -105,7 +109,7 @@ class IdentifierService { resolve(identifier) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to export identifier', error) Vue.$toast.error(`[${code}] Failed to export identifier: ${message}`) reject(error) @@ -118,7 +122,7 @@ class IdentifierService { api.delete(`/api/pid/${pid}`, { headers: { Accept: 'application/json' } }) .then(() => resolve()) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to delete identifier', error) Vue.$toast.error(`[${code}] Failed to delete identifier: ${message}`) reject(error) diff --git a/dbrepo-ui/api/metadata.service.js b/dbrepo-ui/api/metadata.service.js index 9c02ea9247eea2ab242bf5387652d4ed2e50b5bc..c49d1d952da0b0865d6c42c3affd35ed737f1c94 100644 --- a/dbrepo-ui/api/metadata.service.js +++ b/dbrepo-ui/api/metadata.service.js @@ -11,7 +11,7 @@ class MetadataService { resolve(messages) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to load messages', error) Vue.$toast.error(`[${code}] Failed to load messages: ${message}`) reject(error) @@ -28,7 +28,7 @@ class MetadataService { resolve(messages) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to create message', error) Vue.$toast.error(`[${code}] Failed to create message: ${message}`) reject(error) @@ -45,7 +45,7 @@ class MetadataService { resolve(messages) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to find message', error) Vue.$toast.error(`[${code}] Failed to find message: ${message}`) reject(error) @@ -62,7 +62,7 @@ class MetadataService { resolve(messages) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to update message', error) Vue.$toast.error(`[${code}] Failed to update message: ${message}`) reject(error) @@ -75,7 +75,7 @@ class MetadataService { api.delete(`/api/maintenance/message/${id}`, { headers: { Accept: 'application/json' } }) .then(() => resolve()) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to delete message', error) Vue.$toast.error(`[${code}] Failed to delete message: ${message}`) reject(error) @@ -92,7 +92,7 @@ class MetadataService { resolve(messages) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to load active messages', error) Vue.$toast.error(`[${code}] Failed to load active messages: ${message}`) reject(error) diff --git a/dbrepo-ui/api/query.service.js b/dbrepo-ui/api/query.service.js index 857464873a17ffaece8ce68026b7d7d4cfcbaa24..8f8488d6478611c671b62b8bd36fcf36dd92d538 100644 --- a/dbrepo-ui/api/query.service.js +++ b/dbrepo-ui/api/query.service.js @@ -11,7 +11,9 @@ class QueryService { resolve(queries) }) .catch((error) => { + const { code, message } = error.response.data console.error('Failed to load queries', error) + Vue.$toast.error(`[${code}] Failed to load queries: ${message}`) reject(error) }) }) @@ -25,7 +27,9 @@ class QueryService { console.debug('response query', query) resolve(query) }).catch((error) => { + const { code, message } = error.response.data console.error('Failed to load query', error) + Vue.$toast.error(`[${code}] Failed to load query: ${message}`) reject(error) }) }) @@ -40,7 +44,7 @@ class QueryService { resolve(query) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to persist query', error) Vue.$toast.error(`[${code}] Failed to persist query: ${message}`) reject(error) @@ -57,7 +61,7 @@ class QueryService { resolve(table) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to import csv to table', error) Vue.$toast.error(`[${code}] Failed to import csv to table: ${message}`) reject(error) @@ -74,16 +78,14 @@ class QueryService { resolve(tuple) }) .catch((error) => { - const { response } = error - const { status, data } = response - const { message } = data + const { status } = error + const { code, message } = error.response.data if (status === 423) { console.error('Database failed to accept tuple', error) - Vue.$toast.error(message) } else { console.error('Failed to insert tuple', error) - Vue.$toast.error(message) } + Vue.$toast.error(`[${code}] Failed to insert tuple: ${message}`) reject(error) }) }) @@ -98,15 +100,14 @@ class QueryService { resolve(tuple) }) .catch((error) => { - const { code, message, response } = error - const { status } = response + const { status } = error + const { code, message } = error.response.data if (status === 423) { console.error('Database failed to accept tuple', error) - Vue.$toast.error(`Database failed to accept tuple: ${message}`) } else { console.error('Failed to update tuple', error) - Vue.$toast.error(`[${code}] Failed to update tuple: ${message}`) } + Vue.$toast.error(`[${code}] Failed to update tuple: ${message}`) reject(error) }) }) @@ -121,7 +122,7 @@ class QueryService { resolve(subset) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to export query', error) Vue.$toast.error(`[${code}] Failed to export query: ${message}`) reject(error) @@ -138,7 +139,7 @@ class QueryService { resolve(metadata) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to export metadata', error) Vue.$toast.error(`[${code}] Failed to export metadata: ${message}`) reject(error) @@ -155,7 +156,7 @@ class QueryService { resolve(result) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to execute statement', error) Vue.$toast.error(`[${code}] Failed to execute statement: ${message}`) reject(error) @@ -172,7 +173,7 @@ class QueryService { resolve(result) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to re-execute query', error) Vue.$toast.error(`[${code}] Failed to re-execute query: ${message}`) reject(error) @@ -189,7 +190,7 @@ class QueryService { resolve(count) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to re-execute query count', error) Vue.$toast.error(`[${code}] Failed to re-execute query count: ${message}`) reject(error) @@ -206,8 +207,7 @@ class QueryService { resolve(result) }) .catch((error) => { - const { code } = error - const { message } = error.response.data + const { code, message } = error.response.data console.error('Failed to re-execute view', error) Vue.$toast.error(`[${code}] Failed to re-execute view: ${message}`) reject(error) @@ -224,8 +224,7 @@ class QueryService { resolve(count) }) .catch((error) => { - const { code } = error - const { message } = error.response.data + const { code, message } = error.response.data console.error('Failed to re-execute view count', error) Vue.$toast.error(`[${code}] Failed to re-execute view count: ${message}`) reject(error) @@ -242,7 +241,7 @@ class QueryService { resolve(view) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to find view', error) Vue.$toast.error(`[${code}] Failed to find view: ${message}`) reject(error) diff --git a/dbrepo-ui/api/semantic.service.js b/dbrepo-ui/api/semantic.service.js index 01b8b021a3fc86bde81b7ad9a92d383591d35591..88c6b75388c4d51734ca282c968238d556240c83 100644 --- a/dbrepo-ui/api/semantic.service.js +++ b/dbrepo-ui/api/semantic.service.js @@ -11,7 +11,7 @@ class SemanticService { resolve(ontologies) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to load ontologies', error) Vue.$toast.error(`[${code}] Failed to load ontologies: ${message}`) reject(error) @@ -28,7 +28,7 @@ class SemanticService { resolve(concepts) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to load concepts', error) Vue.$toast.error(`[${code}] Failed to load concepts: ${message}`) reject(error) @@ -45,7 +45,7 @@ class SemanticService { resolve(concept) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to update concept', error) Vue.$toast.error(`[${code}] Failed to update concept: ${message}`) reject(error) @@ -62,7 +62,7 @@ class SemanticService { resolve(units) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to load units', error) Vue.$toast.error(`[${code}] Failed to load units: ${message}`) reject(error) @@ -79,7 +79,7 @@ class SemanticService { resolve(unit) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to update unit', error) Vue.$toast.error(`[${code}] Failed to update unit: ${message}`) reject(error) @@ -96,7 +96,7 @@ class SemanticService { resolve(ontology) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to load ontology', error) Vue.$toast.error(`[${code}] Failed to load ontology: ${message}`) reject(error) @@ -113,7 +113,7 @@ class SemanticService { resolve(ontology) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to register ontology', error) Vue.$toast.error(`[${code}] Failed to register ontology: ${message}`) reject(error) @@ -130,7 +130,7 @@ class SemanticService { resolve(ontology) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to update ontology', error) Vue.$toast.error(`[${code}] Failed to update ontology: ${message}`) reject(error) @@ -143,7 +143,7 @@ class SemanticService { api.delete(`/api/semantic/ontology/${id}`, { headers: { Accept: 'application/json' } }) .then(() => resolve()) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to delete ontology', error) Vue.$toast.error(`[${code}] Failed to delete ontology: ${message}`) reject(error) @@ -160,7 +160,7 @@ class SemanticService { resolve(semantics) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to load table column semantics', error) Vue.$toast.error(`[${code}] Failed to load table column semantics: ${message}`) reject(error) diff --git a/dbrepo-ui/api/table.service.js b/dbrepo-ui/api/table.service.js index 6d9f92953948f2748a7e37c25c16cedd00afc803..a438b46aaedf589bb1bc877302157b6b42e4e02b 100644 --- a/dbrepo-ui/api/table.service.js +++ b/dbrepo-ui/api/table.service.js @@ -16,7 +16,7 @@ class TableService { resolve(tables) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to load tables', error) Vue.$toast.error(`[${code}] Failed to load tables: ${message}`) reject(error) @@ -33,7 +33,7 @@ class TableService { resolve(table) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to load table', error) Vue.$toast.error(`[${code}] Failed to load table: ${message}`) reject(error) @@ -50,7 +50,7 @@ class TableService { resolve(column) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to update column', error) Vue.$toast.error(`[${code}] Failed to update column: ${message}`) reject(error) @@ -63,7 +63,7 @@ class TableService { api.post(`/api/database/${databaseId}/table/${tableId}/data/import`, data, { headers: { Accept: 'application/json' } }) .then(() => resolve()) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to import table data', error) Vue.$toast.error(`[${code}] Failed to import table data: ${message}`) reject(error) @@ -80,7 +80,7 @@ class TableService { resolve(data) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to load table data', error) Vue.$toast.error(`[${code}] Failed to load table data: ${message}`) reject(error) @@ -97,7 +97,7 @@ class TableService { resolve(count) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to load table count', error) Vue.$toast.error(`[${code}] Failed to load table count: ${message}`) reject(error) @@ -114,7 +114,7 @@ class TableService { resolve(history) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to load table history', error) Vue.$toast.error(`[${code}] Failed to load table history: ${message}`) reject(error) @@ -135,7 +135,7 @@ class TableService { resolve(data) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to export table data', error) Vue.$toast.error(`[${code}] Failed to export table data: ${message}`) reject(error) @@ -152,7 +152,7 @@ class TableService { resolve(table) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to create table', error) Vue.$toast.error(`[${code}] Failed to create table: ${message}`) reject(error) @@ -165,7 +165,7 @@ class TableService { api.delete(`/api/database/${databaseId}/table/${tableId}/data`, { headers: { Accept: 'application/json' }, data }) .then(() => resolve()) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to delete table tuple', error) Vue.$toast.error(`[${code}] Failed to delete table tuple: ${message}`) reject(error) diff --git a/dbrepo-ui/api/user.service.js b/dbrepo-ui/api/user.service.js index 0fe5a464bd50a40b9af0a945dc86ec52eae2ecb7..0eacd13861c1c9ef74b10cb74c4a91ba554b2aaf 100644 --- a/dbrepo-ui/api/user.service.js +++ b/dbrepo-ui/api/user.service.js @@ -12,7 +12,7 @@ class UserService { resolve(users) }) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to load users', error) Vue.$toast.error(`[${code}] Failed to load users: ${message}`) reject(error) @@ -28,7 +28,7 @@ class UserService { console.debug('response user', response.data, 'mapped user', user) resolve(user) }).catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to load user', error) Vue.$toast.error(`[${code}] Failed to load user: ${message}`) reject(error) @@ -44,7 +44,7 @@ class UserService { console.debug('response user', response.data, 'mapped user', user) resolve(user) }).catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to update user information', error) Vue.$toast.error(`[${code}] Failed to update user information: ${message}`) reject(error) @@ -60,8 +60,8 @@ class UserService { console.debug('response user', user) resolve(user) }).catch((error) => { - const { code, message, response } = error - const { status } = response + const { status } = error + const { code, message } = error.response.data if (status === 417) { Vue.$toast.error('This e-mail address is already taken') } else if (status === 409) { @@ -83,7 +83,7 @@ class UserService { api.put(`/api/user/${id}/password`, { password }, { headers: { Accept: 'application/json' } }) .then(() => resolve()) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to update user password', error) Vue.$toast.error(`[${code}] Failed to update user password: ${message}`) reject(error) @@ -96,7 +96,7 @@ class UserService { api.put(`/api/user/${id}/theme`, { theme_dark: themeDark }, { headers: { Accept: 'application/json' } }) .then(() => resolve()) .catch((error) => { - const { code, message } = error + const { code, message } = error.response.data console.error('Failed to update user theme', error) Vue.$toast.error(`[${code}] Failed to update user theme: ${message}`) reject(error) diff --git a/dbrepo-ui/components/TableSchema.vue b/dbrepo-ui/components/TableSchema.vue index 9f2a0f4103ade28fe58833df9f3aab2a7e698359..d2447f2e277bedb67a13950177532e499915337d 100644 --- a/dbrepo-ui/components/TableSchema.vue +++ b/dbrepo-ui/components/TableSchema.vue @@ -55,6 +55,7 @@ type="number" required :rules="[v => (v !== null && v !== '') || $t('Required')]" + :error-messages="sizeErrorMessages(c)" label="size *" /> </v-col> <v-col cols="1" :hidden="defaultD(c) === false"> @@ -63,6 +64,7 @@ type="number" required :rules="[v => (v !== null && v !== '') || $t('Required')]" + :error-messages="dErrorMessages(c)" label="d *" /> </v-col> <v-col v-if="hasDate(c)" cols="1"> @@ -287,6 +289,24 @@ export default { } return df.has_time }) + }, + sizeErrorMessages (column) { + if (column.size < column.d) { + return ['Size needs to be bigger or equal to d'] + } + if (column.size < 0) { + return ['Size must be positive'] + } + return [] + }, + dErrorMessages (column) { + if (column.size < column.d) { + return ['D needs to be smaller or equal to size'] + } + if (column.d < 0) { + return ['D must be positive'] + } + return [] } } } diff --git a/dbrepo-ui/components/dialogs/EditTuple.vue b/dbrepo-ui/components/dialogs/EditTuple.vue index 781b58014ae8d763b2df40a2ac0523fdc2eb70c8..d3f240100c500b27699675f47646857c8ed6ef71 100644 --- a/dbrepo-ui/components/dialogs/EditTuple.vue +++ b/dbrepo-ui/components/dialogs/EditTuple.vue @@ -194,7 +194,7 @@ export default { }, hint (column) { if (!this.edit && column.auto_generated) { - return 'Value is auto-generated' + return 'Auto-generated by sequence' } if (this.edit && column.is_primary_key) { return 'Required (Primary Key)' @@ -240,13 +240,11 @@ export default { return ['date', 'datetime', 'timestamp', 'time', 'year'].includes(column.column_type) }, rules (column) { - if (column.auto_generated) { + if (column.auto_generated || column.is_null_allowed) { return [] } const rules = [] - if (!column.is_null_allowed) { - rules.push(v => !!v || 'Required') - } + rules.push(v => !!v || 'Required') if (column.column_type === 'char') { rules.push(v => !(!v || v.length !== column.size) || `Must be exactly ${column.size} character${column.size !== 1 ? 's' : ''}`) } @@ -268,9 +266,6 @@ export default { disabled (column) { return (this.edit && column.is_primary_key) || (!this.edit && column.auto_generated) }, - validateTimestamp (val) { - return /^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}$/.test(val) - }, updateTuple () { const constraints = {} this.columns diff --git a/dbrepo-ui/dbrepo.config.json b/dbrepo-ui/dbrepo.config.json index e908ff37abe93a321824fd10cef69cb0af9ae7d5..e87d09debda5c65f98375efe21239f235eef8ad8 100644 --- a/dbrepo-ui/dbrepo.config.json +++ b/dbrepo-ui/dbrepo.config.json @@ -59,22 +59,22 @@ { "text": "OpenSearch Admin", "blank": true, - "href": "http://localhost/admin/dashboard" + "href": "http://localhost/admin/dashboard/" }, { "text": "Storage Admin", "blank": true, - "href": "http://localhost/admin/storage" + "href": "http://localhost/admin/storage/" }, { "text": "RabbitMQ Admin", "blank": true, - "href": "http://localhost/admin/broker" + "href": "http://localhost/admin/broker/" }, { "text": "Keycloak Admin", "blank": true, - "href": "http://localhost/api/auth" + "href": "http://localhost/api/auth/" } ] }, 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 c9221fafcbb1c8724d9bf75b660af346fe5ff33d..1f60efcb8d816ec70ab82bb2325862e15f24a328 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 @@ -24,6 +24,7 @@ item-text="key" item-value="value" required + clearable hint="Character separating the values" label="Separator *" /> </v-col> @@ -35,6 +36,7 @@ :rules="[v => isNonNegativeInteger(v) || $t('Greater or equal to zero')]" type="number" required + clearable hint="Skip n lines from the top. These may include comments or the header of column names." label="Number of lines to skip *" placeholder="e.g. 0" /> @@ -47,6 +49,7 @@ :items="quotes" item-text="key" item-value="value" + clearable hint="Character quoting the values" label="Value quotes" /> </v-col> @@ -57,6 +60,7 @@ v-model="tableImport.null_element" hint="Representation of 'no value present'" placeholder="e.g. NA" + clearable label="NULL Element" /> </v-col> </v-row> @@ -65,6 +69,7 @@ <v-text-field v-model="tableImport.true_element" label="True Element" + clearable hint="Representation of boolean 'true'" placeholder="e.g. 1, true, YES" /> </v-col> @@ -74,6 +79,7 @@ <v-text-field v-model="tableImport.false_element" label="False Element" + clearable hint="Representation of boolean 'false'" placeholder="e.g. 0, false, NO" /> </v-col> @@ -85,6 +91,7 @@ accept=".csv,.tsv" hint="max. 2GB file size" persistent-hint + clearable :show-size="1000" counter label="CSV/TSV File" /> @@ -136,7 +143,7 @@ export default { quote: '"', false_element: null, true_element: null, - null_element: null, + null_element: 'NA', separator: ',', skip_lines: 1 }, diff --git a/dbrepo-ui/pages/database/_database_id/table/import.vue b/dbrepo-ui/pages/database/_database_id/table/import.vue index 5501a4929b3b5718ac21b457e95466bd832f1711..a64bcc54f605e3fea0fbec97d47eee6b41451241 100644 --- a/dbrepo-ui/pages/database/_database_id/table/import.vue +++ b/dbrepo-ui/pages/database/_database_id/table/import.vue @@ -57,6 +57,7 @@ item-text="key" item-value="value" required + clearable hint="Character separating the values" label="Separator *" /> </v-col> @@ -69,6 +70,7 @@ v => isNonNegativeInteger(v) || $t('Greater or equal to zero')]" type="number" required + clearable hint="Skip n lines from the top. These may include comments or the header of column names." label="Number of lines to skip *" placeholder="e.g. 0" /> @@ -81,6 +83,7 @@ :items="quotes" item-text="key" item-value="value" + clearable hint="Character quoting the values" label="Value quotes" /> </v-col> @@ -91,6 +94,7 @@ v-model="tableImport.null_element" hint="Representation of 'no value present'" placeholder="e.g. NA" + clearable label="NULL Element" /> </v-col> </v-row> @@ -99,6 +103,7 @@ <v-text-field v-model="tableImport.true_element" label="True Element" + clearable hint="Representation of boolean 'true'" placeholder="e.g. 1, true, YES" /> </v-col> @@ -108,6 +113,7 @@ <v-text-field v-model="tableImport.false_element" label="False Element" + clearable hint="Representation of boolean 'false'" placeholder="e.g. 0, false, NO" /> </v-col> @@ -256,7 +262,7 @@ export default { quote: '"', false_element: null, true_element: null, - null_element: null, + null_element: 'NA', separator: ',', skip_lines: 1 }, @@ -329,6 +335,7 @@ export default { UploadService.upload(this.fileModel) .then((metadata) => { console.debug('uploaded file', metadata) + this.loading = false resolve(metadata) }) .catch((error) => {