diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/HistoryEventTypeDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/HistoryEventTypeDto.java new file mode 100644 index 0000000000000000000000000000000000000000..8912295addf48290400ffe2073371e2e813cc390 --- /dev/null +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/table/HistoryEventTypeDto.java @@ -0,0 +1,30 @@ +package at.tuwien.api.database.table; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; + +@Getter +@Schema +public enum HistoryEventTypeDto { + + @JsonProperty("read") + READ("read"), + + @JsonProperty("write_own") + WRITE_OWN("write_own"), + + @JsonProperty("write_all") + WRITE_ALL("write_all"); + + private String name; + + HistoryEventTypeDto(String name) { + this.name = name; + } + + @Override + public String toString() { + return this.name; + } +} diff --git a/lib/python/.gitignore b/lib/python/.gitignore index c954a774008e444617bea9ec16df30b7dda3183b..1f4bb524a1c7dfe8ec62be60b755993e13f7dedc 100644 --- a/lib/python/.gitignore +++ b/lib/python/.gitignore @@ -6,6 +6,7 @@ dist/ dbrepo.egg-info/ build/ htmlcov/ +tmp-* # debug debug.py diff --git a/lib/python/Pipfile b/lib/python/Pipfile index 62a93cc02a1d043464485a4506adcf65ca2160a2..662d60f59560b5a6fdc8e200af5845e2f85b171b 100644 --- a/lib/python/Pipfile +++ b/lib/python/Pipfile @@ -21,6 +21,8 @@ pytest = "*" requests-mock = "*" furo = "*" pytest-ordering = "*" +testcontainers = "*" +httpx = "*" [requires] python_version = "3.11" diff --git a/lib/python/Pipfile.lock b/lib/python/Pipfile.lock index 52b7202c3e6e17d9651d8851a66dc925f010a4df..f33975b8cccafdc33f6f6f115e47bdece97939d8 100644 --- a/lib/python/Pipfile.lock +++ b/lib/python/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "01e7f752292f6f3d558a9418f7172696f9bd200d00c7eed2745b74d08ef27eb4" + "sha256": "598d2fb07f9f85270aa16f90dd8ef90b11618a241b5ba050a6dce3b2b250813e" }, "pipfile-spec": 6, "requires": { @@ -18,93 +18,98 @@ "default": { "aiohappyeyeballs": { "hashes": [ - "sha256:5fdd7d87889c63183afc18ce9271f9b0a7d32c2303e394468dd45d514a757745", - "sha256:a980909d50efcd44795c4afeca523296716d50cd756ddca6af8c65b996e27de8" + "sha256:147ec992cf873d74f5062644332c539fcd42956dc69453fe5204195e560517e1", + "sha256:9b05052f9042985d32ecbe4b59a77ae19c006a78f1344d7fdad69d28ded3d0b0" ], - "markers": "python_version >= '3.8'", - "version": "==2.4.4" + "markers": "python_version >= '3.9'", + "version": "==2.4.6" }, "aiohttp": { "hashes": [ - "sha256:0882c2820fd0132240edbb4a51eb8ceb6eef8181db9ad5291ab3332e0d71df5f", - "sha256:0a6d3fbf2232e3a08c41eca81ae4f1dff3d8f1a30bae415ebe0af2d2458b8a33", - "sha256:0b7fb429ab1aafa1f48578eb315ca45bd46e9c37de11fe45c7f5f4138091e2f1", - "sha256:0eb98d90b6690827dcc84c246811feeb4e1eea683c0eac6caed7549be9c84665", - "sha256:0fd82b8e9c383af11d2b26f27a478640b6b83d669440c0a71481f7c865a51da9", - "sha256:10b4ff0ad793d98605958089fabfa350e8e62bd5d40aa65cdc69d6785859f94e", - "sha256:1642eceeaa5ab6c9b6dfeaaa626ae314d808188ab23ae196a34c9d97efb68350", - "sha256:1dac54e8ce2ed83b1f6b1a54005c87dfed139cf3f777fdc8afc76e7841101226", - "sha256:1e69966ea6ef0c14ee53ef7a3d68b564cc408121ea56c0caa2dc918c1b2f553d", - "sha256:1f21bb8d0235fc10c09ce1d11ffbd40fc50d3f08a89e4cf3a0c503dc2562247a", - "sha256:2170816e34e10f2fd120f603e951630f8a112e1be3b60963a1f159f5699059a6", - "sha256:21fef42317cf02e05d3b09c028712e1d73a9606f02467fd803f7c1f39cc59add", - "sha256:249cc6912405917344192b9f9ea5cd5b139d49e0d2f5c7f70bdfaf6b4dbf3a2e", - "sha256:3499c7ffbfd9c6a3d8d6a2b01c26639da7e43d47c7b4f788016226b1e711caa8", - "sha256:3af41686ccec6a0f2bdc66686dc0f403c41ac2089f80e2214a0f82d001052c03", - "sha256:3e23419d832d969f659c208557de4a123e30a10d26e1e14b73431d3c13444c2e", - "sha256:3ea1b59dc06396b0b424740a10a0a63974c725b1c64736ff788a3689d36c02d2", - "sha256:44167fc6a763d534a6908bdb2592269b4bf30a03239bcb1654781adf5e49caf1", - "sha256:479b8c6ebd12aedfe64563b85920525d05d394b85f166b7873c8bde6da612f9c", - "sha256:4af57160800b7a815f3fe0eba9b46bf28aafc195555f1824555fa2cfab6c1538", - "sha256:4b4fa1cb5f270fb3eab079536b764ad740bb749ce69a94d4ec30ceee1b5940d5", - "sha256:4eed954b161e6b9b65f6be446ed448ed3921763cc432053ceb606f89d793927e", - "sha256:541d823548ab69d13d23730a06f97460f4238ad2e5ed966aaf850d7c369782d9", - "sha256:568c1236b2fde93b7720f95a890741854c1200fba4a3471ff48b2934d2d93fd3", - "sha256:5854be2f3e5a729800bac57a8d76af464e160f19676ab6aea74bde18ad19d438", - "sha256:620598717fce1b3bd14dd09947ea53e1ad510317c85dda2c9c65b622edc96b12", - "sha256:6526e5fb4e14f4bbf30411216780c9967c20c5a55f2f51d3abd6de68320cc2f3", - "sha256:6fba278063559acc730abf49845d0e9a9e1ba74f85f0ee6efd5803f08b285853", - "sha256:70d1f9dde0e5dd9e292a6d4d00058737052b01f3532f69c0c65818dac26dc287", - "sha256:731468f555656767cda219ab42e033355fe48c85fbe3ba83a349631541715ba2", - "sha256:81b8fe282183e4a3c7a1b72f5ade1094ed1c6345a8f153506d114af5bf8accd9", - "sha256:84a585799c58b795573c7fa9b84c455adf3e1d72f19a2bf498b54a95ae0d194c", - "sha256:85992ee30a31835fc482468637b3e5bd085fa8fe9392ba0bdcbdc1ef5e9e3c55", - "sha256:8811f3f098a78ffa16e0ea36dffd577eb031aea797cbdba81be039a4169e242c", - "sha256:88a12ad8ccf325a8a5ed80e6d7c3bdc247d66175afedbe104ee2aaca72960d8e", - "sha256:8be8508d110d93061197fd2d6a74f7401f73b6d12f8822bbcd6d74f2b55d71b1", - "sha256:8e2bf8029dbf0810c7bfbc3e594b51c4cc9101fbffb583a3923aea184724203c", - "sha256:929f3ed33743a49ab127c58c3e0a827de0664bfcda566108989a14068f820194", - "sha256:92cde43018a2e17d48bb09c79e4d4cb0e236de5063ce897a5e40ac7cb4878773", - "sha256:92fc484e34b733704ad77210c7957679c5c3877bd1e6b6d74b185e9320cc716e", - "sha256:943a8b052e54dfd6439fd7989f67fc6a7f2138d0a2cf0a7de5f18aa4fe7eb3b1", - "sha256:9d73ee3725b7a737ad86c2eac5c57a4a97793d9f442599bea5ec67ac9f4bdc3d", - "sha256:9f5b3c1ed63c8fa937a920b6c1bec78b74ee09593b3f5b979ab2ae5ef60d7600", - "sha256:9fd46ce0845cfe28f108888b3ab17abff84ff695e01e73657eec3f96d72eef34", - "sha256:a344d5dc18074e3872777b62f5f7d584ae4344cd6006c17ba12103759d407af3", - "sha256:a60804bff28662cbcf340a4d61598891f12eea3a66af48ecfdc975ceec21e3c8", - "sha256:a8f5f7515f3552d899c61202d99dcb17d6e3b0de777900405611cd747cecd1b8", - "sha256:a9b7371665d4f00deb8f32208c7c5e652059b0fda41cf6dbcac6114a041f1cc2", - "sha256:aa54f8ef31d23c506910c21163f22b124facb573bff73930735cf9fe38bf7dff", - "sha256:aba807f9569455cba566882c8938f1a549f205ee43c27b126e5450dc9f83cc62", - "sha256:ae545f31489548c87b0cced5755cfe5a5308d00407000e72c4fa30b19c3220ac", - "sha256:af01e42ad87ae24932138f154105e88da13ce7d202a6de93fafdafb2883a00ef", - "sha256:b540bd67cfb54e6f0865ceccd9979687210d7ed1a1cc8c01f8e67e2f1e883d28", - "sha256:b6212a60e5c482ef90f2d788835387070a88d52cf6241d3916733c9176d39eab", - "sha256:b63de12e44935d5aca7ed7ed98a255a11e5cb47f83a9fded7a5e41c40277d104", - "sha256:ba74ec819177af1ef7f59063c6d35a214a8fde6f987f7661f4f0eecc468a8f76", - "sha256:bb49c7f1e6ebf3821a42d81d494f538107610c3a705987f53068546b0e90303e", - "sha256:bd176afcf8f5d2aed50c3647d4925d0db0579d96f75a31e77cbaf67d8a87742d", - "sha256:bd7227b87a355ce1f4bf83bfae4399b1f5bb42e0259cb9405824bd03d2f4336a", - "sha256:bf8d9bfee991d8acc72d060d53860f356e07a50f0e0d09a8dfedea1c554dd0d5", - "sha256:bfde76a8f430cf5c5584553adf9926534352251d379dcb266ad2b93c54a29745", - "sha256:c341c7d868750e31961d6d8e60ff040fb9d3d3a46d77fd85e1ab8e76c3e9a5c4", - "sha256:c7a06301c2fb096bdb0bd25fe2011531c1453b9f2c163c8031600ec73af1cc99", - "sha256:cb23d8bb86282b342481cad4370ea0853a39e4a32a0042bb52ca6bdde132df43", - "sha256:d119fafe7b634dbfa25a8c597718e69a930e4847f0b88e172744be24515140da", - "sha256:d40f9da8cabbf295d3a9dae1295c69975b86d941bc20f0a087f0477fa0a66231", - "sha256:d6c9af134da4bc9b3bd3e6a70072509f295d10ee60c697826225b60b9959acdd", - "sha256:dd7659baae9ccf94ae5fe8bfaa2c7bc2e94d24611528395ce88d009107e00c6d", - "sha256:de8d38f1c2810fa2a4f1d995a2e9c70bb8737b18da04ac2afbf3971f65781d87", - "sha256:e595c591a48bbc295ebf47cb91aebf9bd32f3ff76749ecf282ea7f9f6bb73886", - "sha256:ec2aa89305006fba9ffb98970db6c8221541be7bee4c1d027421d6f6df7d1ce2", - "sha256:ec82bf1fda6cecce7f7b915f9196601a1bd1a3079796b76d16ae4cce6d0ef89b", - "sha256:ed9ee95614a71e87f1a70bc81603f6c6760128b140bc4030abe6abaa988f1c3d", - "sha256:f047569d655f81cb70ea5be942ee5d4421b6219c3f05d131f64088c73bb0917f", - "sha256:ffa336210cf9cd8ed117011085817d00abe4c08f99968deef0013ea283547204", - "sha256:ffb3dc385f6bb1568aa974fe65da84723210e5d9707e360e9ecb51f59406cd2e" + "sha256:0450ada317a65383b7cce9576096150fdb97396dcfe559109b403c7242faffef", + "sha256:0b5263dcede17b6b0c41ef0c3ccce847d82a7da98709e75cf7efde3e9e3b5cae", + "sha256:0d5176f310a7fe6f65608213cc74f4228e4f4ce9fd10bcb2bb6da8fc66991462", + "sha256:0ed49efcd0dc1611378beadbd97beb5d9ca8fe48579fc04a6ed0844072261b6a", + "sha256:145a73850926018ec1681e734cedcf2716d6a8697d90da11284043b745c286d5", + "sha256:1987770fb4887560363b0e1a9b75aa303e447433c41284d3af2840a2f226d6e0", + "sha256:246067ba0cf5560cf42e775069c5d80a8989d14a7ded21af529a4e10e3e0f0e6", + "sha256:2c311e2f63e42c1bf86361d11e2c4a59f25d9e7aabdbdf53dc38b885c5435cdb", + "sha256:2cee3b117a8d13ab98b38d5b6bdcd040cfb4181068d05ce0c474ec9db5f3c5bb", + "sha256:2de1378f72def7dfb5dbd73d86c19eda0ea7b0a6873910cc37d57e80f10d64e1", + "sha256:30f546358dfa0953db92ba620101fefc81574f87b2346556b90b5f3ef16e55ce", + "sha256:34245498eeb9ae54c687a07ad7f160053911b5745e186afe2d0c0f2898a1ab8a", + "sha256:392432a2dde22b86f70dd4a0e9671a349446c93965f261dbaecfaf28813e5c42", + "sha256:3c0600bcc1adfaaac321422d615939ef300df81e165f6522ad096b73439c0f58", + "sha256:4016e383f91f2814e48ed61e6bda7d24c4d7f2402c75dd28f7e1027ae44ea204", + "sha256:40cd36749a1035c34ba8d8aaf221b91ca3d111532e5ccb5fa8c3703ab1b967ed", + "sha256:413ad794dccb19453e2b97c2375f2ca3cdf34dc50d18cc2693bd5aed7d16f4b9", + "sha256:4a93d28ed4b4b39e6f46fd240896c29b686b75e39cc6992692e3922ff6982b4c", + "sha256:4ee84c2a22a809c4f868153b178fe59e71423e1f3d6a8cd416134bb231fbf6d3", + "sha256:50c5c7b8aa5443304c55c262c5693b108c35a3b61ef961f1e782dd52a2f559c7", + "sha256:525410e0790aab036492eeea913858989c4cb070ff373ec3bc322d700bdf47c1", + "sha256:526c900397f3bbc2db9cb360ce9c35134c908961cdd0ac25b1ae6ffcaa2507ff", + "sha256:54775858c7f2f214476773ce785a19ee81d1294a6bedc5cc17225355aab74802", + "sha256:584096938a001378484aa4ee54e05dc79c7b9dd933e271c744a97b3b6f644957", + "sha256:6130459189e61baac5a88c10019b21e1f0c6d00ebc770e9ce269475650ff7f73", + "sha256:67453e603cea8e85ed566b2700efa1f6916aefbc0c9fcb2e86aaffc08ec38e78", + "sha256:68d54234c8d76d8ef74744f9f9fc6324f1508129e23da8883771cdbb5818cbef", + "sha256:6dfe7f984f28a8ae94ff3a7953cd9678550dbd2a1f9bda5dd9c5ae627744c78e", + "sha256:74bd573dde27e58c760d9ca8615c41a57e719bff315c9adb6f2a4281a28e8798", + "sha256:7603ca26d75b1b86160ce1bbe2787a0b706e592af5b2504e12caa88a217767b0", + "sha256:76719dd521c20a58a6c256d058547b3a9595d1d885b830013366e27011ffe804", + "sha256:7c3623053b85b4296cd3925eeb725e386644fd5bc67250b3bb08b0f144803e7b", + "sha256:7e44eba534381dd2687be50cbd5f2daded21575242ecfdaf86bbeecbc38dae8e", + "sha256:7fe3d65279bfbee8de0fb4f8c17fc4e893eed2dba21b2f680e930cc2b09075c5", + "sha256:8340def6737118f5429a5df4e88f440746b791f8f1c4ce4ad8a595f42c980bd5", + "sha256:84ede78acde96ca57f6cf8ccb8a13fbaf569f6011b9a52f870c662d4dc8cd854", + "sha256:850ff6155371fd802a280f8d369d4e15d69434651b844bde566ce97ee2277420", + "sha256:87a2e00bf17da098d90d4145375f1d985a81605267e7f9377ff94e55c5d769eb", + "sha256:88d385b8e7f3a870146bf5ea31786ef7463e99eb59e31db56e2315535d811f55", + "sha256:8a2fb742ef378284a50766e985804bd6adb5adb5aa781100b09befdbfa757b65", + "sha256:8dc0fba9a74b471c45ca1a3cb6e6913ebfae416678d90529d188886278e7f3f6", + "sha256:8fa1510b96c08aaad49303ab11f8803787c99222288f310a62f493faf883ede1", + "sha256:8fd12d0f989c6099e7b0f30dc6e0d1e05499f3337461f0b2b0dadea6c64b89df", + "sha256:9060addfa4ff753b09392efe41e6af06ea5dd257829199747b9f15bfad819460", + "sha256:930ffa1925393381e1e0a9b82137fa7b34c92a019b521cf9f41263976666a0d6", + "sha256:936d8a4f0f7081327014742cd51d320296b56aa6d324461a13724ab05f4b2933", + "sha256:97fe431f2ed646a3b56142fc81d238abcbaff08548d6912acb0b19a0cadc146b", + "sha256:9bd8695be2c80b665ae3f05cb584093a1e59c35ecb7d794d1edd96e8cc9201d7", + "sha256:9dec0000d2d8621d8015c293e24589d46fa218637d820894cb7356c77eca3259", + "sha256:a478aa11b328983c4444dacb947d4513cb371cd323f3845e53caeda6be5589d5", + "sha256:a481a574af914b6e84624412666cbfbe531a05667ca197804ecc19c97b8ab1b0", + "sha256:a4ac6a0f0f6402854adca4e3259a623f5c82ec3f0c049374133bcb243132baf9", + "sha256:a5e69046f83c0d3cb8f0d5bd9b8838271b1bc898e01562a04398e160953e8eb9", + "sha256:a7442662afebbf7b4c6d28cb7aab9e9ce3a5df055fc4116cc7228192ad6cb484", + "sha256:aa8a8caca81c0a3e765f19c6953416c58e2f4cc1b84829af01dd1c771bb2f91f", + "sha256:ab3247d58b393bda5b1c8f31c9edece7162fc13265334217785518dd770792b8", + "sha256:b10a47e5390c4b30a0d58ee12581003be52eedd506862ab7f97da7a66805befb", + "sha256:b34508f1cd928ce915ed09682d11307ba4b37d0708d1f28e5774c07a7674cac9", + "sha256:b8d3bb96c147b39c02d3db086899679f31958c5d81c494ef0fc9ef5bb1359b3d", + "sha256:b9d45dbb3aaec05cf01525ee1a7ac72de46a8c425cb75c003acd29f76b1ffe94", + "sha256:bf4480a5438f80e0f1539e15a7eb8b5f97a26fe087e9828e2c0ec2be119a9f72", + "sha256:c160a04283c8c6f55b5bf6d4cad59bb9c5b9c9cd08903841b25f1f7109ef1259", + "sha256:c96a43822f1f9f69cc5c3706af33239489a6294be486a0447fb71380070d4d5f", + "sha256:c9fd9dcf9c91affe71654ef77426f5cf8489305e1c66ed4816f5a21874b094b9", + "sha256:cddb31f8474695cd61fc9455c644fc1606c164b93bff2490390d90464b4655df", + "sha256:ce1bb21fc7d753b5f8a5d5a4bae99566386b15e716ebdb410154c16c91494d7f", + "sha256:d1c031a7572f62f66f1257db37ddab4cb98bfaf9b9434a3b4840bf3560f5e788", + "sha256:d589264dbba3b16e8951b6f145d1e6b883094075283dafcab4cdd564a9e353a0", + "sha256:dc065a4285307607df3f3686363e7f8bdd0d8ab35f12226362a847731516e42c", + "sha256:e10c440d142fa8b32cfdb194caf60ceeceb3e49807072e0dc3a8887ea80e8c16", + "sha256:e3552fe98e90fdf5918c04769f338a87fa4f00f3b28830ea9b78b1bdc6140e0d", + "sha256:e392804a38353900c3fd8b7cacbea5132888f7129f8e241915e90b85f00e3250", + "sha256:e4cecdb52aaa9994fbed6b81d4568427b6002f0a91c322697a4bfcc2b2363f5a", + "sha256:e5148ca8955affdfeb864aca158ecae11030e952b25b3ae15d4e2b5ba299bad2", + "sha256:e6b2732ef3bafc759f653a98881b5b9cdef0716d98f013d376ee8dfd7285abf1", + "sha256:ea756b5a7bac046d202a9a3889b9a92219f885481d78cd318db85b15cc0b7bcf", + "sha256:edb69b9589324bdc40961cdf0657815df674f1743a8d5ad9ab56a99e4833cfdd", + "sha256:f0203433121484b32646a5f5ea93ae86f3d9559d7243f07e8c0eab5ff8e3f70e", + "sha256:f6a19bcab7fbd8f8649d6595624856635159a6527861b9cdc3447af288a00c00", + "sha256:f752e80606b132140883bb262a457c475d219d7163d996dc9072434ffb0784c4", + "sha256:f7914ab70d2ee8ab91c13e5402122edbc77821c66d2758abb53aabe87f013287" ], "markers": "python_version >= '3.9'", - "version": "==3.11.11" + "version": "==3.11.12" }, "aiosignal": { "hashes": [ @@ -124,19 +129,19 @@ }, "attrs": { "hashes": [ - "sha256:8f5c07333d543103541ba7be0e2ce16eeee8130cb0b3f9238ab904ce1e85baff", - "sha256:ac96cd038792094f438ad1f6ff80837353805ac950cd2aa0e0625ef19850c308" + "sha256:1c97078a80c814273a76b2a298a932eb681c87415c11dee0a6921de7f1b02c3e", + "sha256:c75a69e28a550a7e93789579c22aa26b0f5b83b75dc4e08fe092980051e1090a" ], "markers": "python_version >= '3.8'", - "version": "==24.3.0" + "version": "==25.1.0" }, "certifi": { "hashes": [ - "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56", - "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db" + "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651", + "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe" ], "markers": "python_version >= '3.6'", - "version": "==2024.12.14" + "version": "==2025.1.31" }, "charset-normalizer": { "hashes": [ @@ -442,64 +447,64 @@ }, "numpy": { "hashes": [ - "sha256:02935e2c3c0c6cbe9c7955a8efa8908dd4221d7755644c59d1bba28b94fd334f", - "sha256:0349b025e15ea9d05c3d63f9657707a4e1d471128a3b1d876c095f328f8ff7f0", - "sha256:09d6a2032faf25e8d0cadde7fd6145118ac55d2740132c1d845f98721b5ebcfd", - "sha256:0bc61b307655d1a7f9f4b043628b9f2b721e80839914ede634e3d485913e1fb2", - "sha256:0eec19f8af947a61e968d5429f0bd92fec46d92b0008d0a6685b40d6adf8a4f4", - "sha256:106397dbbb1896f99e044efc90360d098b3335060375c26aa89c0d8a97c5f648", - "sha256:128c41c085cab8a85dc29e66ed88c05613dccf6bc28b3866cd16050a2f5448be", - "sha256:149d1113ac15005652e8d0d3f6fd599360e1a708a4f98e43c9c77834a28238cb", - "sha256:159ff6ee4c4a36a23fe01b7c3d07bd8c14cc433d9720f977fcd52c13c0098160", - "sha256:22ea3bb552ade325530e72a0c557cdf2dea8914d3a5e1fecf58fa5dbcc6f43cd", - "sha256:23ae9f0c2d889b7b2d88a3791f6c09e2ef827c2446f1c4a3e3e76328ee4afd9a", - "sha256:250c16b277e3b809ac20d1f590716597481061b514223c7badb7a0f9993c7f84", - "sha256:2ec6c689c61df613b783aeb21f945c4cbe6c51c28cb70aae8430577ab39f163e", - "sha256:2ffbb1acd69fdf8e89dd60ef6182ca90a743620957afb7066385a7bbe88dc748", - "sha256:3074634ea4d6df66be04f6728ee1d173cfded75d002c75fac79503a880bf3825", - "sha256:356ca982c188acbfa6af0d694284d8cf20e95b1c3d0aefa8929376fea9146f60", - "sha256:3fbe72d347fbc59f94124125e73fc4976a06927ebc503ec5afbfb35f193cd957", - "sha256:40c7ff5da22cd391944a28c6a9c638a5eef77fcf71d6e3a79e1d9d9e82752715", - "sha256:41184c416143defa34cc8eb9d070b0a5ba4f13a0fa96a709e20584638254b317", - "sha256:451e854cfae0febe723077bd0cf0a4302a5d84ff25f0bfece8f29206c7bed02e", - "sha256:4525b88c11906d5ab1b0ec1f290996c0020dd318af8b49acaa46f198b1ffc283", - "sha256:463247edcee4a5537841d5350bc87fe8e92d7dd0e8c71c995d2c6eecb8208278", - "sha256:4dbd80e453bd34bd003b16bd802fac70ad76bd463f81f0c518d1245b1c55e3d9", - "sha256:57b4012e04cc12b78590a334907e01b3a85efb2107df2b8733ff1ed05fce71de", - "sha256:5a8c863ceacae696aff37d1fd636121f1a512117652e5dfb86031c8d84836369", - "sha256:5acea83b801e98541619af398cc0109ff48016955cc0818f478ee9ef1c5c3dcb", - "sha256:642199e98af1bd2b6aeb8ecf726972d238c9877b0f6e8221ee5ab945ec8a2189", - "sha256:64bd6e1762cd7f0986a740fee4dff927b9ec2c5e4d9a28d056eb17d332158014", - "sha256:6d9fc9d812c81e6168b6d405bf00b8d6739a7f72ef22a9214c4241e0dc70b323", - "sha256:7079129b64cb78bdc8d611d1fd7e8002c0a2565da6a47c4df8062349fee90e3e", - "sha256:7dca87ca328f5ea7dafc907c5ec100d187911f94825f8700caac0b3f4c384b49", - "sha256:860fd59990c37c3ef913c3ae390b3929d005243acca1a86facb0773e2d8d9e50", - "sha256:8e6da5cffbbe571f93588f562ed130ea63ee206d12851b60819512dd3e1ba50d", - "sha256:8ec0636d3f7d68520afc6ac2dc4b8341ddb725039de042faf0e311599f54eb37", - "sha256:9491100aba630910489c1d0158034e1c9a6546f0b1340f716d522dc103788e39", - "sha256:97b974d3ba0fb4612b77ed35d7627490e8e3dff56ab41454d9e8b23448940576", - "sha256:995f9e8181723852ca458e22de5d9b7d3ba4da3f11cc1cb113f093b271d7965a", - "sha256:9dd47ff0cb2a656ad69c38da850df3454da88ee9a6fde0ba79acceee0e79daba", - "sha256:9fad446ad0bc886855ddf5909cbf8cb5d0faa637aaa6277fb4b19ade134ab3c7", - "sha256:a972cec723e0563aa0823ee2ab1df0cb196ed0778f173b381c871a03719d4826", - "sha256:ac9bea18d6d58a995fac1b2cb4488e17eceeac413af014b1dd26170b766d8467", - "sha256:b0531f0b0e07643eb089df4c509d30d72c9ef40defa53e41363eca8a8cc61495", - "sha256:b208cfd4f5fe34e1535c08983a1a6803fdbc7a1e86cf13dd0c61de0b51a0aadc", - "sha256:b3482cb7b3325faa5f6bc179649406058253d91ceda359c104dac0ad320e1391", - "sha256:b6fb9c32a91ec32a689ec6410def76443e3c750e7cfc3fb2206b985ffb2b85f0", - "sha256:b78ea78450fd96a498f50ee096f69c75379af5138f7881a51355ab0e11286c97", - "sha256:bd249bc894af67cbd8bad2c22e7cbcd46cf87ddfca1f1289d1e7e54868cc785c", - "sha256:c7d1fd447e33ee20c1f33f2c8e6634211124a9aabde3c617687d8b739aa69eac", - "sha256:d0bbe7dd86dca64854f4b6ce2ea5c60b51e36dfd597300057cf473d3615f2369", - "sha256:d6d6a0910c3b4368d89dde073e630882cdb266755565155bc33520283b2d9df8", - "sha256:da1eeb460ecce8d5b8608826595c777728cdf28ce7b5a5a8c8ac8d949beadcf2", - "sha256:e0c8854b09bc4de7b041148d8550d3bd712b5c21ff6a8ed308085f190235d7ff", - "sha256:e0d4142eb40ca6f94539e4db929410f2a46052a0fe7a2c1c59f6179c39938d2a", - "sha256:e9e82dcb3f2ebbc8cb5ce1102d5f1c5ed236bf8a11730fb45ba82e2841ec21df", - "sha256:ed6906f61834d687738d25988ae117683705636936cc605be0bb208b23df4d8f" + "sha256:0391ea3622f5c51a2e29708877d56e3d276827ac5447d7f45e9bc4ade8923c52", + "sha256:12c045f43b1d2915eca6b880a7f4a256f59d62df4f044788c8ba67709412128d", + "sha256:136553f123ee2951bfcfbc264acd34a2fc2f29d7cdf610ce7daf672b6fbaa693", + "sha256:1402da8e0f435991983d0a9708b779f95a8c98c6b18a171b9f1be09005e64d9d", + "sha256:16372619ee728ed67a2a606a614f56d3eabc5b86f8b615c79d01957062826ca8", + "sha256:1ad78ce7f18ce4e7df1b2ea4019b5817a2f6a8a16e34ff2775f646adce0a5027", + "sha256:1b416af7d0ed3271cad0f0a0d0bee0911ed7eba23e66f8424d9f3dfcdcae1304", + "sha256:1f45315b2dc58d8a3e7754fe4e38b6fce132dab284a92851e41b2b344f6441c5", + "sha256:2376e317111daa0a6739e50f7ee2a6353f768489102308b0d98fcf4a04f7f3b5", + "sha256:23c9f4edbf4c065fddb10a4f6e8b6a244342d95966a48820c614891e5059bb50", + "sha256:246535e2f7496b7ac85deffe932896a3577be7af8fb7eebe7146444680297e9a", + "sha256:2e8da03bd561504d9b20e7a12340870dfc206c64ea59b4cfee9fceb95070ee94", + "sha256:34c1b7e83f94f3b564b35f480f5652a47007dd91f7c839f404d03279cc8dd021", + "sha256:39261798d208c3095ae4f7bc8eaeb3481ea8c6e03dc48028057d3cbdbdb8937e", + "sha256:3b787adbf04b0db1967798dba8da1af07e387908ed1553a0d6e74c084d1ceafe", + "sha256:3c2ec8a0f51d60f1e9c0c5ab116b7fc104b165ada3f6c58abf881cb2eb16044d", + "sha256:435e7a933b9fda8126130b046975a968cc2d833b505475e588339e09f7672890", + "sha256:4d8335b5f1b6e2bce120d55fb17064b0262ff29b459e8493d1785c18ae2553b8", + "sha256:4d9828d25fb246bedd31e04c9e75714a4087211ac348cb39c8c5f99dbb6683fe", + "sha256:52659ad2534427dffcc36aac76bebdd02b67e3b7a619ac67543bc9bfe6b7cdb1", + "sha256:5266de33d4c3420973cf9ae3b98b54a2a6d53a559310e3236c4b2b06b9c07d4e", + "sha256:5521a06a3148686d9269c53b09f7d399a5725c47bbb5b35747e1cb76326b714b", + "sha256:596140185c7fa113563c67c2e894eabe0daea18cf8e33851738c19f70ce86aeb", + "sha256:5b732c8beef1d7bc2d9e476dbba20aaff6167bf205ad9aa8d30913859e82884b", + "sha256:5ebeb7ef54a7be11044c33a17b2624abe4307a75893c001a4800857956b41094", + "sha256:712a64103d97c404e87d4d7c47fb0c7ff9acccc625ca2002848e0d53288b90ea", + "sha256:7678556eeb0152cbd1522b684dcd215250885993dd00adb93679ec3c0e6e091c", + "sha256:77974aba6c1bc26e3c205c2214f0d5b4305bdc719268b93e768ddb17e3fdd636", + "sha256:783145835458e60fa97afac25d511d00a1eca94d4a8f3ace9fe2043003c678e4", + "sha256:7bfdb06b395385ea9b91bf55c1adf1b297c9fdb531552845ff1d3ea6e40d5aba", + "sha256:7c8dde0ca2f77828815fd1aedfdf52e59071a5bae30dac3b4da2a335c672149a", + "sha256:83807d445817326b4bcdaaaf8e8e9f1753da04341eceec705c001ff342002e5d", + "sha256:87eed225fd415bbae787f93a457af7f5990b92a334e346f72070bf569b9c9c95", + "sha256:8fb62fe3d206d72fe1cfe31c4a1106ad2b136fcc1606093aeab314f02930fdf2", + "sha256:95172a21038c9b423e68be78fd0be6e1b97674cde269b76fe269a5dfa6fadf0b", + "sha256:9f48ba6f6c13e5e49f3d3efb1b51c8193215c42ac82610a04624906a9270be6f", + "sha256:a0c03b6be48aaf92525cccf393265e02773be8fd9551a2f9adbe7db1fa2b60f1", + "sha256:a5ae282abe60a2db0fd407072aff4599c279bcd6e9a2475500fc35b00a57c532", + "sha256:aee2512827ceb6d7f517c8b85aa5d3923afe8fc7a57d028cffcd522f1c6fd082", + "sha256:c8b0451d2ec95010d1db8ca733afc41f659f425b7f608af569711097fd6014e2", + "sha256:c9aa4496fd0e17e3843399f533d62857cef5900facf93e735ef65aa4bbc90ef0", + "sha256:cbc6472e01952d3d1b2772b720428f8b90e2deea8344e854df22b0618e9cce71", + "sha256:cdfe0c22692a30cd830c0755746473ae66c4a8f2e7bd508b35fb3b6a0813d787", + "sha256:cf802eef1f0134afb81fef94020351be4fe1d6681aadf9c5e862af6602af64ef", + "sha256:d42f9c36d06440e34226e8bd65ff065ca0963aeecada587b937011efa02cdc9d", + "sha256:d5b47c440210c5d1d67e1cf434124e0b5c395eee1f5806fdd89b553ed1acd0a3", + "sha256:d9b4a8148c57ecac25a16b0e11798cbe88edf5237b0df99973687dd866f05e1b", + "sha256:daf43a3d1ea699402c5a850e5313680ac355b4adc9770cd5cfc2940e7861f1bf", + "sha256:dbdc15f0c81611925f382dfa97b3bd0bc2c1ce19d4fe50482cb0ddc12ba30020", + "sha256:deaa09cd492e24fd9b15296844c0ad1b3c976da7907e1c1ed3a0ad21dded6f76", + "sha256:e37242f5324ffd9f7ba5acf96d774f9276aa62a966c0bad8dae692deebec7716", + "sha256:ed2cf9ed4e8ebc3b754d398cba12f24359f018b416c380f577bbae112ca52fc9", + "sha256:f2712c5179f40af9ddc8f6727f2bd910ea0eb50206daea75f58ddd9fa3f715bb", + "sha256:f4ca91d61a4bf61b0f2228f24bbfa6a9facd5f8af03759fe2a655c50ae2c6610", + "sha256:f6b3dfc7661f8842babd8ea07e9897fe3d9b69a1d7e5fbb743e4160f9387833b" ], "markers": "python_version == '3.11'", - "version": "==2.2.2" + "version": "==2.2.3" }, "paho-mqtt": { "hashes": [ @@ -655,11 +660,11 @@ }, "pydantic": { "hashes": [ - "sha256:278b38dbbaec562011d659ee05f63346951b3a248a6f3642e1bc68894ea2b4ff", - "sha256:4dd4e322dbe55472cb7ca7e73f4b63574eecccf2835ffa2af9021ce113c83c53" + "sha256:427d664bf0b8a2b34ff5dd0f5a18df00591adcee7198fbd71981054cef37b584", + "sha256:ca5daa827cce33de7a42be142548b0096bf05a7e7b365aebfa5f8eeec7128236" ], "index": "pypi", - "version": "==2.10.5" + "version": "==2.10.6" }, "pydantic-core": { "hashes": [ @@ -777,10 +782,10 @@ }, "pytz": { "hashes": [ - "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a", - "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725" + "sha256:89dd22dca55b46eac6eda23b2d72721bf1bdfef212645d81513ef5d03038de57", + "sha256:c2db42be2a2518b28e65f9207c4d05e6ff547d1efa4086469ef855e4ab70178e" ], - "version": "==2024.2" + "version": "==2025.1" }, "requests": { "hashes": [ @@ -936,13 +941,21 @@ "markers": "python_version >= '3.10'", "version": "==1.0.0" }, + "anyio": { + "hashes": [ + "sha256:1d9fe889df5212298c0c0723fa20479d1b94883a2df44bd3897aa91083316f7a", + "sha256:b5011f270ab5eb0abf13385f851315585cc37ef330dd88e27ec3d34d651fd47a" + ], + "markers": "python_version >= '3.9'", + "version": "==4.8.0" + }, "babel": { "hashes": [ - "sha256:368b5b98b37c06b7daf6696391c3240c938b37767d4584413e8438c5c435fa8b", - "sha256:d1f3554ca26605fe173f3de0c65f750f5a42f924499bf134de6423582298e316" + "sha256:0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d", + "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2" ], "markers": "python_version >= '3.8'", - "version": "==2.16.0" + "version": "==2.17.0" }, "backports.tarfile": { "hashes": [ @@ -954,11 +967,11 @@ }, "beautifulsoup4": { "hashes": [ - "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051", - "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed" + "sha256:1bd32405dacc920b42b83ba01644747ed77456a65760e285fbc47633ceddaf8b", + "sha256:99045d7d3f08f91f0d656bc9b7efbae189426cd913d830294a15eefa0ea4df16" ], - "markers": "python_full_version >= '3.6.0'", - "version": "==4.12.3" + "markers": "python_full_version >= '3.7.0'", + "version": "==4.13.3" }, "build": { "hashes": [ @@ -970,11 +983,11 @@ }, "certifi": { "hashes": [ - "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56", - "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db" + "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651", + "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe" ], "markers": "python_version >= '3.6'", - "version": "==2024.12.14" + "version": "==2025.1.31" }, "cffi": { "hashes": [ @@ -1149,106 +1162,117 @@ }, "coverage": { "hashes": [ - "sha256:05fca8ba6a87aabdd2d30d0b6c838b50510b56cdcfc604d40760dae7153b73d9", - "sha256:0aa9692b4fdd83a4647eeb7db46410ea1322b5ed94cd1715ef09d1d5922ba87f", - "sha256:0c807ca74d5a5e64427c8805de15b9ca140bba13572d6d74e262f46f50b13273", - "sha256:0d7a2bf79378d8fb8afaa994f91bfd8215134f8631d27eba3e0e2c13546ce994", - "sha256:0f460286cb94036455e703c66988851d970fdfd8acc2a1122ab7f4f904e4029e", - "sha256:204a8238afe787323a8b47d8be4df89772d5c1e4651b9ffa808552bdf20e1d50", - "sha256:2396e8116db77789f819d2bc8a7e200232b7a282c66e0ae2d2cd84581a89757e", - "sha256:254f1a3b1eef5f7ed23ef265eaa89c65c8c5b6b257327c149db1ca9d4a35f25e", - "sha256:26bcf5c4df41cad1b19c84af71c22cbc9ea9a547fc973f1f2cc9a290002c8b3c", - "sha256:27c6e64726b307782fa5cbe531e7647aee385a29b2107cd87ba7c0105a5d3853", - "sha256:299e91b274c5c9cdb64cbdf1b3e4a8fe538a7a86acdd08fae52301b28ba297f8", - "sha256:2bcfa46d7709b5a7ffe089075799b902020b62e7ee56ebaed2f4bdac04c508d8", - "sha256:2ccf240eb719789cedbb9fd1338055de2761088202a9a0b73032857e53f612fe", - "sha256:32ee6d8491fcfc82652a37109f69dee9a830e9379166cb73c16d8dc5c2915165", - "sha256:3f7b444c42bbc533aaae6b5a2166fd1a797cdb5eb58ee51a92bee1eb94a1e1cb", - "sha256:457574f4599d2b00f7f637a0700a6422243b3565509457b2dbd3f50703e11f59", - "sha256:489a01f94aa581dbd961f306e37d75d4ba16104bbfa2b0edb21d29b73be83609", - "sha256:4bcc276261505d82f0ad426870c3b12cb177752834a633e737ec5ee79bbdff18", - "sha256:4e0de1e902669dccbf80b0415fb6b43d27edca2fbd48c74da378923b05316098", - "sha256:4e4630c26b6084c9b3cb53b15bd488f30ceb50b73c35c5ad7871b869cb7365fd", - "sha256:4eea95ef275de7abaef630c9b2c002ffbc01918b726a39f5a4353916ec72d2f3", - "sha256:507a20fc863cae1d5720797761b42d2d87a04b3e5aeb682ef3b7332e90598f43", - "sha256:54a5f0f43950a36312155dae55c505a76cd7f2b12d26abeebbe7a0b36dbc868d", - "sha256:55b201b97286cf61f5e76063f9e2a1d8d2972fc2fcfd2c1272530172fd28c359", - "sha256:59af35558ba08b758aec4d56182b222976330ef8d2feacbb93964f576a7e7a90", - "sha256:5c912978f7fbf47ef99cec50c4401340436d200d41d714c7a4766f377c5b7b78", - "sha256:656c82b8a0ead8bba147de9a89bda95064874c91a3ed43a00e687f23cc19d53a", - "sha256:6713ba4b4ebc330f3def51df1d5d38fad60b66720948112f114968feb52d3f99", - "sha256:675cefc4c06e3b4c876b85bfb7c59c5e2218167bbd4da5075cbe3b5790a28988", - "sha256:6f93531882a5f68c28090f901b1d135de61b56331bba82028489bc51bdd818d2", - "sha256:714f942b9c15c3a7a5fe6876ce30af831c2ad4ce902410b7466b662358c852c0", - "sha256:79109c70cc0882e4d2d002fe69a24aa504dec0cc17169b3c7f41a1d341a73694", - "sha256:7bbd8c8f1b115b892e34ba66a097b915d3871db7ce0e6b9901f462ff3a975377", - "sha256:7ed2f37cfce1ce101e6dffdfd1c99e729dd2ffc291d02d3e2d0af8b53d13840d", - "sha256:7fb105327c8f8f0682e29843e2ff96af9dcbe5bab8eeb4b398c6a33a16d80a23", - "sha256:89d76815a26197c858f53c7f6a656686ec392b25991f9e409bcef020cd532312", - "sha256:9a7cfb50515f87f7ed30bc882f68812fd98bc2852957df69f3003d22a2aa0abf", - "sha256:9e1747bab246d6ff2c4f28b4d186b205adced9f7bd9dc362051cc37c4a0c7bd6", - "sha256:9e80eba8801c386f72e0712a0453431259c45c3249f0009aff537a517b52942b", - "sha256:a01ec4af7dfeb96ff0078ad9a48810bb0cc8abcb0115180c6013a6b26237626c", - "sha256:a372c89c939d57abe09e08c0578c1d212e7a678135d53aa16eec4430adc5e690", - "sha256:a3b204c11e2b2d883946fe1d97f89403aa1811df28ce0447439178cc7463448a", - "sha256:a534738b47b0de1995f85f582d983d94031dffb48ab86c95bdf88dc62212142f", - "sha256:a5e37dc41d57ceba70956fa2fc5b63c26dba863c946ace9705f8eca99daecdc4", - "sha256:aa744da1820678b475e4ba3dfd994c321c5b13381d1041fe9c608620e6676e25", - "sha256:ab32947f481f7e8c763fa2c92fd9f44eeb143e7610c4ca9ecd6a36adab4081bd", - "sha256:abb02e2f5a3187b2ac4cd46b8ced85a0858230b577ccb2c62c81482ca7d18852", - "sha256:b330368cb99ef72fcd2dc3ed260adf67b31499584dc8a20225e85bfe6f6cfed0", - "sha256:bc67deb76bc3717f22e765ab3e07ee9c7a5e26b9019ca19a3b063d9f4b874244", - "sha256:c0b1818063dc9e9d838c09e3a473c1422f517889436dd980f5d721899e66f315", - "sha256:c56e097019e72c373bae32d946ecf9858fda841e48d82df7e81c63ac25554078", - "sha256:c7827a5bc7bdb197b9e066cdf650b2887597ad124dd99777332776f7b7c7d0d0", - "sha256:ccc2b70a7ed475c68ceb548bf69cec1e27305c1c2606a5eb7c3afff56a1b3b27", - "sha256:d37a84878285b903c0fe21ac8794c6dab58150e9359f1aaebbeddd6412d53132", - "sha256:e2f0280519e42b0a17550072861e0bc8a80a0870de260f9796157d3fca2733c5", - "sha256:e4ae5ac5e0d1e4edfc9b4b57b4cbecd5bc266a6915c500f358817a8496739247", - "sha256:e67926f51821b8e9deb6426ff3164870976fe414d033ad90ea75e7ed0c2e5022", - "sha256:e78b270eadb5702938c3dbe9367f878249b5ef9a2fcc5360ac7bff694310d17b", - "sha256:ea3c8f04b3e4af80e17bab607c386a830ffc2fb88a5484e1df756478cf70d1d3", - "sha256:ec22b5e7fe7a0fa8509181c4aac1db48f3dd4d3a566131b313d1efc102892c18", - "sha256:f4f620668dbc6f5e909a0946a877310fb3d57aea8198bde792aae369ee1c23b5", - "sha256:fd34e7b3405f0cc7ab03d54a334c17a9e802897580d964bd8c2001f4b9fd488f" + "sha256:00b2086892cf06c7c2d74983c9595dc511acca00665480b3ddff749ec4fb2a95", + "sha256:0533adc29adf6a69c1baa88c3d7dbcaadcffa21afbed3ca7a225a440e4744bf9", + "sha256:06097c7abfa611c91edb9e6920264e5be1d6ceb374efb4986f38b09eed4cb2fe", + "sha256:07e92ae5a289a4bc4c0aae710c0948d3c7892e20fd3588224ebe242039573bf0", + "sha256:0a9d8be07fb0832636a0f72b80d2a652fe665e80e720301fb22b191c3434d924", + "sha256:0e549f54ac5f301e8e04c569dfdb907f7be71b06b88b5063ce9d6953d2d58574", + "sha256:0ef01d70198431719af0b1f5dcbefc557d44a190e749004042927b2a3fed0702", + "sha256:0f16f44025c06792e0fb09571ae454bcc7a3ec75eeb3c36b025eccf501b1a4c3", + "sha256:14d47376a4f445e9743f6c83291e60adb1b127607a3618e3185bbc8091f0467b", + "sha256:1a936309a65cc5ca80fa9f20a442ff9e2d06927ec9a4f54bcba9c14c066323f2", + "sha256:1ceeb90c3eda1f2d8c4c578c14167dbd8c674ecd7d38e45647543f19839dd6ea", + "sha256:1f7ffa05da41754e20512202c866d0ebfc440bba3b0ed15133070e20bf5aeb5f", + "sha256:200e10beb6ddd7c3ded322a4186313d5ca9e63e33d8fab4faa67ef46d3460af3", + "sha256:220fa6c0ad7d9caef57f2c8771918324563ef0d8272c94974717c3909664e674", + "sha256:2251fabcfee0a55a8578a9d29cecfee5f2de02f11530e7d5c5a05859aa85aee9", + "sha256:2458f275944db8129f95d91aee32c828a408481ecde3b30af31d552c2ce284a0", + "sha256:299cf973a7abff87a30609879c10df0b3bfc33d021e1adabc29138a48888841e", + "sha256:2b996819ced9f7dbb812c701485d58f261bef08f9b85304d41219b1496b591ef", + "sha256:3688b99604a24492bcfe1c106278c45586eb819bf66a654d8a9a1433022fb2eb", + "sha256:3a1e465f398c713f1b212400b4e79a09829cd42aebd360362cd89c5bdc44eb87", + "sha256:488c27b3db0ebee97a830e6b5a3ea930c4a6e2c07f27a5e67e1b3532e76b9ef1", + "sha256:48cfc4641d95d34766ad41d9573cc0f22a48aa88d22657a1fe01dca0dbae4de2", + "sha256:4b467a8c56974bf06e543e69ad803c6865249d7a5ccf6980457ed2bc50312703", + "sha256:53c56358d470fa507a2b6e67a68fd002364d23c83741dbc4c2e0680d80ca227e", + "sha256:5d1095bbee1851269f79fd8e0c9b5544e4c00c0c24965e66d8cba2eb5bb535fd", + "sha256:641dfe0ab73deb7069fb972d4d9725bf11c239c309ce694dd50b1473c0f641c3", + "sha256:64cbb1a3027c79ca6310bf101014614f6e6e18c226474606cf725238cf5bc2d4", + "sha256:66fe626fd7aa5982cdebad23e49e78ef7dbb3e3c2a5960a2b53632f1f703ea45", + "sha256:676f92141e3c5492d2a1596d52287d0d963df21bf5e55c8b03075a60e1ddf8aa", + "sha256:69e62c5034291c845fc4df7f8155e8544178b6c774f97a99e2734b05eb5bed31", + "sha256:704c8c8c6ce6569286ae9622e534b4f5b9759b6f2cd643f1c1a61f666d534fe8", + "sha256:78f5243bb6b1060aed6213d5107744c19f9571ec76d54c99cc15938eb69e0e86", + "sha256:79cac3390bfa9836bb795be377395f28410811c9066bc4eefd8015258a7578c6", + "sha256:7ae6eabf519bc7871ce117fb18bf14e0e343eeb96c377667e3e5dd12095e0288", + "sha256:7e39e845c4d764208e7b8f6a21c541ade741e2c41afabdfa1caa28687a3c98cf", + "sha256:8161d9fbc7e9fe2326de89cd0abb9f3599bccc1287db0aba285cb68d204ce929", + "sha256:8bec2ac5da793c2685ce5319ca9bcf4eee683b8a1679051f8e6ec04c4f2fd7dc", + "sha256:959244a17184515f8c52dcb65fb662808767c0bd233c1d8a166e7cf74c9ea985", + "sha256:9b148068e881faa26d878ff63e79650e208e95cf1c22bd3f77c3ca7b1d9821a3", + "sha256:aa6f302a3a0b5f240ee201297fff0bbfe2fa0d415a94aeb257d8b461032389bd", + "sha256:ace9048de91293e467b44bce0f0381345078389814ff6e18dbac8fdbf896360e", + "sha256:ad7525bf0241e5502168ae9c643a2f6c219fa0a283001cee4cf23a9b7da75879", + "sha256:b01a840ecc25dce235ae4c1b6a0daefb2a203dba0e6e980637ee9c2f6ee0df57", + "sha256:b076e625396e787448d27a411aefff867db2bffac8ed04e8f7056b07024eed5a", + "sha256:b172f8e030e8ef247b3104902cc671e20df80163b60a203653150d2fc204d1ad", + "sha256:b1f097878d74fe51e1ddd1be62d8e3682748875b461232cf4b52ddc6e6db0bba", + "sha256:b95574d06aa9d2bd6e5cc35a5bbe35696342c96760b69dc4287dbd5abd4ad51d", + "sha256:bda1c5f347550c359f841d6614fb8ca42ae5cb0b74d39f8a1e204815ebe25750", + "sha256:cec6b9ce3bd2b7853d4a4563801292bfee40b030c05a3d29555fd2a8ee9bd68c", + "sha256:d1a987778b9c71da2fc8948e6f2656da6ef68f59298b7e9786849634c35d2c3c", + "sha256:d74c08e9aaef995f8c4ef6d202dbd219c318450fe2a76da624f2ebb9c8ec5d9f", + "sha256:e18aafdfb3e9ec0d261c942d35bd7c28d031c5855dadb491d2723ba54f4c3015", + "sha256:e216c5c45f89ef8971373fd1c5d8d1164b81f7f5f06bbf23c37e7908d19e8558", + "sha256:e695df2c58ce526eeab11a2e915448d3eb76f75dffe338ea613c1201b33bab2f", + "sha256:e7575ab65ca8399c8c4f9a7d61bbd2d204c8b8e447aab9d355682205c9dd948d", + "sha256:e995b3b76ccedc27fe4f477b349b7d64597e53a43fc2961db9d3fbace085d69d", + "sha256:ea31689f05043d520113e0552f039603c4dd71fa4c287b64cb3606140c66f425", + "sha256:eb5507795caabd9b2ae3f1adc95f67b1104971c22c624bb354232d65c4fc90b3", + "sha256:eb8668cfbc279a536c633137deeb9435d2962caec279c3f8cf8b91fff6ff8953", + "sha256:ecea0c38c9079570163d663c0433a9af4094a60aafdca491c6a3d248c7432827", + "sha256:f25d8b92a4e31ff1bd873654ec367ae811b3a943583e05432ea29264782dc32c", + "sha256:f60a297c3987c6c02ffb29effc70eadcbb412fe76947d394a1091a3615948e2f", + "sha256:f973643ef532d4f9be71dd88cf7588936685fdb576d93a79fe9f65bc337d9d73" ], "index": "pypi", - "version": "==7.6.10" + "version": "==7.6.12" }, "cryptography": { "hashes": [ - "sha256:1923cb251c04be85eec9fda837661c67c1049063305d6be5721643c22dd4e2b7", - "sha256:37d76e6863da3774cd9db5b409a9ecfd2c71c981c38788d3fcfaf177f447b731", - "sha256:3c672a53c0fb4725a29c303be906d3c1fa99c32f58abe008a82705f9ee96f40b", - "sha256:404fdc66ee5f83a1388be54300ae978b2efd538018de18556dde92575e05defc", - "sha256:4ac4c9f37eba52cb6fbeaf5b59c152ea976726b865bd4cf87883a7e7006cc543", - "sha256:60eb32934076fa07e4316b7b2742fa52cbb190b42c2df2863dbc4230a0a9b385", - "sha256:62901fb618f74d7d81bf408c8719e9ec14d863086efe4185afd07c352aee1d2c", - "sha256:660cb7312a08bc38be15b696462fa7cc7cd85c3ed9c576e81f4dc4d8b2b31591", - "sha256:708ee5f1bafe76d041b53a4f95eb28cdeb8d18da17e597d46d7833ee59b97ede", - "sha256:761817a3377ef15ac23cd7834715081791d4ec77f9297ee694ca1ee9c2c7e5eb", - "sha256:831c3c4d0774e488fdc83a1923b49b9957d33287de923d58ebd3cec47a0ae43f", - "sha256:84111ad4ff3f6253820e6d3e58be2cc2a00adb29335d4cacb5ab4d4d34f2a123", - "sha256:8b3e6eae66cf54701ee7d9c83c30ac0a1e3fa17be486033000f2a73a12ab507c", - "sha256:9abcc2e083cbe8dde89124a47e5e53ec38751f0d7dfd36801008f316a127d7ba", - "sha256:9e6fc8a08e116fb7c7dd1f040074c9d7b51d74a8ea40d4df2fc7aa08b76b9e6c", - "sha256:a01956ddfa0a6790d594f5b34fc1bfa6098aca434696a03cfdbe469b8ed79285", - "sha256:abc998e0c0eee3c8a1904221d3f67dcfa76422b23620173e28c11d3e626c21bd", - "sha256:b15492a11f9e1b62ba9d73c210e2416724633167de94607ec6069ef724fad092", - "sha256:be4ce505894d15d5c5037167ffb7f0ae90b7be6f2a98f9a5c3442395501c32fa", - "sha256:c5eb858beed7835e5ad1faba59e865109f3e52b3783b9ac21e7e47dc5554e289", - "sha256:cd4e834f340b4293430701e772ec543b0fbe6c2dea510a5286fe0acabe153a02", - "sha256:d2436114e46b36d00f8b72ff57e598978b37399d2786fd39793c36c6d5cb1c64", - "sha256:eb33480f1bad5b78233b0ad3e1b0be21e8ef1da745d8d2aecbb20671658b9053", - "sha256:eca27345e1214d1b9f9490d200f9db5a874479be914199194e746c893788d417", - "sha256:ed3534eb1090483c96178fcb0f8893719d96d5274dfde98aa6add34614e97c8e", - "sha256:f3f6fdfa89ee2d9d496e2c087cebef9d4fcbb0ad63c40e821b39f74bf48d9c5e", - "sha256:f53c2c87e0fb4b0c00fa9571082a057e37690a8f12233306161c8f4b819960b7", - "sha256:f5e7cb1e5e56ca0933b4873c0220a78b773b24d40d186b6738080b73d3d0a756", - "sha256:f677e1268c4e23420c3acade68fac427fffcb8d19d7df95ed7ad17cdef8404f4" + "sha256:00918d859aa4e57db8299607086f793fa7813ae2ff5a4637e318a25ef82730f7", + "sha256:1e8d181e90a777b63f3f0caa836844a1182f1f265687fac2115fcf245f5fbec3", + "sha256:1f9a92144fa0c877117e9748c74501bea842f93d21ee00b0cf922846d9d0b183", + "sha256:21377472ca4ada2906bc313168c9dc7b1d7ca417b63c1c3011d0c74b7de9ae69", + "sha256:24979e9f2040c953a94bf3c6782e67795a4c260734e5264dceea65c8f4bae64a", + "sha256:2a46a89ad3e6176223b632056f321bc7de36b9f9b93b2cc1cccf935a3849dc62", + "sha256:322eb03ecc62784536bc173f1483e76747aafeb69c8728df48537eb431cd1911", + "sha256:436df4f203482f41aad60ed1813811ac4ab102765ecae7a2bbb1dbb66dcff5a7", + "sha256:4f422e8c6a28cf8b7f883eb790695d6d45b0c385a2583073f3cec434cc705e1a", + "sha256:53f23339864b617a3dfc2b0ac8d5c432625c80014c25caac9082314e9de56f41", + "sha256:5fed5cd6102bb4eb843e3315d2bf25fede494509bddadb81e03a859c1bc17b83", + "sha256:610a83540765a8d8ce0f351ce42e26e53e1f774a6efb71eb1b41eb01d01c3d12", + "sha256:6c8acf6f3d1f47acb2248ec3ea261171a671f3d9428e34ad0357148d492c7864", + "sha256:6f76fdd6fd048576a04c5210d53aa04ca34d2ed63336d4abd306d0cbe298fddf", + "sha256:72198e2b5925155497a5a3e8c216c7fb3e64c16ccee11f0e7da272fa93b35c4c", + "sha256:887143b9ff6bad2b7570da75a7fe8bbf5f65276365ac259a5d2d5147a73775f2", + "sha256:888fcc3fce0c888785a4876ca55f9f43787f4c5c1cc1e2e0da71ad481ff82c5b", + "sha256:8e6a85a93d0642bd774460a86513c5d9d80b5c002ca9693e63f6e540f1815ed0", + "sha256:94f99f2b943b354a5b6307d7e8d19f5c423a794462bde2bf310c770ba052b1c4", + "sha256:9b336599e2cb77b1008cb2ac264b290803ec5e8e89d618a5e978ff5eb6f715d9", + "sha256:a2d8a7045e1ab9b9f803f0d9531ead85f90c5f2859e653b61497228b18452008", + "sha256:b8272f257cf1cbd3f2e120f14c68bff2b6bdfcc157fafdee84a1b795efd72862", + "sha256:bf688f615c29bfe9dfc44312ca470989279f0e94bb9f631f85e3459af8efc009", + "sha256:d9c5b9f698a83c8bd71e0f4d3f9f839ef244798e5ffe96febfa9714717db7af7", + "sha256:dd7c7e2d71d908dc0f8d2027e1604102140d84b155e658c20e8ad1304317691f", + "sha256:df978682c1504fc93b3209de21aeabf2375cb1571d4e61907b3e7a2540e83026", + "sha256:e403f7f766ded778ecdb790da786b418a9f2394f36e8cc8b796cc056ab05f44f", + "sha256:eb3889330f2a4a148abead555399ec9a32b13b7c8ba969b72d8e500eb7ef84cd", + "sha256:f4daefc971c2d1f82f03097dc6f216744a6cd2ac0f04c68fb935ea2ba2a0d420", + "sha256:f51f5705ab27898afda1aaa430f34ad90dc117421057782022edf0600bec5f14", + "sha256:fd0ee90072861e276b0ff08bd627abec29e32a53b2be44e41dbcdf87cbee2b00" ], "markers": "python_version >= '3.7' and python_full_version not in '3.9.0, 3.9.1'", - "version": "==44.0.0" + "version": "==44.0.1" + }, + "docker": { + "hashes": [ + "sha256:ad8c70e6e3f8926cb8a92619b832b4ea5299e2831c14284663184e200546fa6c", + "sha256:c96b93b7f0a746f9e77d325bcfb87422a3d8bd4f03136ae8a85b37f1898d5fc0" + ], + "markers": "python_version >= '3.8'", + "version": "==7.1.0" }, "docutils": { "hashes": [ @@ -1266,6 +1290,30 @@ "index": "pypi", "version": "==2024.8.6" }, + "h11": { + "hashes": [ + "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d", + "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761" + ], + "markers": "python_version >= '3.7'", + "version": "==0.14.0" + }, + "httpcore": { + "hashes": [ + "sha256:8551cb62a169ec7162ac7be8d4817d561f60e08eaa485234898414bb5a8a0b4c", + "sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd" + ], + "markers": "python_version >= '3.8'", + "version": "==1.0.7" + }, + "httpx": { + "hashes": [ + "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", + "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad" + ], + "index": "pypi", + "version": "==0.28.1" + }, "id": { "hashes": [ "sha256:292cb8a49eacbbdbce97244f47a97b4c62540169c976552e497fd57df0734c1d", @@ -1532,6 +1580,14 @@ "index": "pypi", "version": "==0.6" }, + "python-dotenv": { + "hashes": [ + "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca", + "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a" + ], + "markers": "python_version >= '3.8'", + "version": "==1.0.1" + }, "pyyaml": { "hashes": [ "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff", @@ -1655,6 +1711,14 @@ "index": "pypi", "version": "==75.8.0" }, + "sniffio": { + "hashes": [ + "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", + "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc" + ], + "markers": "python_version >= '3.7'", + "version": "==1.3.1" + }, "snowballstemmer": { "hashes": [ "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1", @@ -1734,6 +1798,14 @@ "markers": "python_version >= '3.9'", "version": "==2.0.0" }, + "testcontainers": { + "hashes": [ + "sha256:315fb94b42a383872df530aa45319745278ef0cc18b9cfcdc231a75d14afa5a0", + "sha256:37fe9a222549ddb788463935965b16f91809e9a8d654f437d6a59eac9b77f76f" + ], + "index": "pypi", + "version": "==4.9.1" + }, "twine": { "hashes": [ "sha256:a47f973caf122930bf0fbbf17f80b83bc1602c9ce393c7845f289a3001dc5384", @@ -1742,6 +1814,14 @@ "index": "pypi", "version": "==6.1.0" }, + "typing-extensions": { + "hashes": [ + "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", + "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8" + ], + "markers": "python_version >= '3.8'", + "version": "==4.12.2" + }, "urllib3": { "hashes": [ "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df", @@ -1750,6 +1830,91 @@ "markers": "python_version >= '3.9'", "version": "==2.3.0" }, + "wrapt": { + "hashes": [ + "sha256:08e7ce672e35efa54c5024936e559469436f8b8096253404faeb54d2a878416f", + "sha256:0a6e821770cf99cc586d33833b2ff32faebdbe886bd6322395606cf55153246c", + "sha256:0b929ac182f5ace000d459c59c2c9c33047e20e935f8e39371fa6e3b85d56f4a", + "sha256:129a150f5c445165ff941fc02ee27df65940fcb8a22a61828b1853c98763a64b", + "sha256:13e6afb7fe71fe7485a4550a8844cc9ffbe263c0f1a1eea569bc7091d4898555", + "sha256:1473400e5b2733e58b396a04eb7f35f541e1fb976d0c0724d0223dd607e0f74c", + "sha256:18983c537e04d11cf027fbb60a1e8dfd5190e2b60cc27bc0808e653e7b218d1b", + "sha256:1a7ed2d9d039bd41e889f6fb9364554052ca21ce823580f6a07c4ec245c1f5d6", + "sha256:1e1fe0e6ab7775fd842bc39e86f6dcfc4507ab0ffe206093e76d61cde37225c8", + "sha256:1fb5699e4464afe5c7e65fa51d4f99e0b2eadcc176e4aa33600a3df7801d6662", + "sha256:2696993ee1eebd20b8e4ee4356483c4cb696066ddc24bd70bcbb80fa56ff9061", + "sha256:35621ae4c00e056adb0009f8e86e28eb4a41a4bfa8f9bfa9fca7d343fe94f998", + "sha256:36ccae62f64235cf8ddb682073a60519426fdd4725524ae38874adf72b5f2aeb", + "sha256:3cedbfa9c940fdad3e6e941db7138e26ce8aad38ab5fe9dcfadfed9db7a54e62", + "sha256:3d57c572081fed831ad2d26fd430d565b76aa277ed1d30ff4d40670b1c0dd984", + "sha256:3fc7cb4c1c744f8c05cd5f9438a3caa6ab94ce8344e952d7c45a8ed59dd88392", + "sha256:4011d137b9955791f9084749cba9a367c68d50ab8d11d64c50ba1688c9b457f2", + "sha256:40d615e4fe22f4ad3528448c193b218e077656ca9ccb22ce2cb20db730f8d306", + "sha256:410a92fefd2e0e10d26210e1dfb4a876ddaf8439ef60d6434f21ef8d87efc5b7", + "sha256:41388e9d4d1522446fe79d3213196bd9e3b301a336965b9e27ca2788ebd122f3", + "sha256:468090021f391fe0056ad3e807e3d9034e0fd01adcd3bdfba977b6fdf4213ea9", + "sha256:49703ce2ddc220df165bd2962f8e03b84c89fee2d65e1c24a7defff6f988f4d6", + "sha256:4a721d3c943dae44f8e243b380cb645a709ba5bd35d3ad27bc2ed947e9c68192", + "sha256:4afd5814270fdf6380616b321fd31435a462019d834f83c8611a0ce7484c7317", + "sha256:4c82b8785d98cdd9fed4cac84d765d234ed3251bd6afe34cb7ac523cb93e8b4f", + "sha256:4db983e7bca53819efdbd64590ee96c9213894272c776966ca6306b73e4affda", + "sha256:582530701bff1dec6779efa00c516496968edd851fba224fbd86e46cc6b73563", + "sha256:58455b79ec2661c3600e65c0a716955adc2410f7383755d537584b0de41b1d8a", + "sha256:58705da316756681ad3c9c73fd15499aa4d8c69f9fd38dc8a35e06c12468582f", + "sha256:5bb1d0dbf99411f3d871deb6faa9aabb9d4e744d67dcaaa05399af89d847a91d", + "sha256:5c803c401ea1c1c18de70a06a6f79fcc9c5acfc79133e9869e730ad7f8ad8ef9", + "sha256:5cbabee4f083b6b4cd282f5b817a867cf0b1028c54d445b7ec7cfe6505057cf8", + "sha256:612dff5db80beef9e649c6d803a8d50c409082f1fedc9dbcdfde2983b2025b82", + "sha256:62c2caa1585c82b3f7a7ab56afef7b3602021d6da34fbc1cf234ff139fed3cd9", + "sha256:69606d7bb691b50a4240ce6b22ebb319c1cfb164e5f6569835058196e0f3a845", + "sha256:6d9187b01bebc3875bac9b087948a2bccefe464a7d8f627cf6e48b1bbae30f82", + "sha256:6ed6ffac43aecfe6d86ec5b74b06a5be33d5bb9243d055141e8cabb12aa08125", + "sha256:703919b1633412ab54bcf920ab388735832fdcb9f9a00ae49387f0fe67dad504", + "sha256:766d8bbefcb9e00c3ac3b000d9acc51f1b399513f44d77dfe0eb026ad7c9a19b", + "sha256:80dd7db6a7cb57ffbc279c4394246414ec99537ae81ffd702443335a61dbf3a7", + "sha256:8112e52c5822fc4253f3901b676c55ddf288614dc7011634e2719718eaa187dc", + "sha256:8c8b293cd65ad716d13d8dd3624e42e5a19cc2a2f1acc74b30c2c13f15cb61a6", + "sha256:8fdbdb757d5390f7c675e558fd3186d590973244fab0c5fe63d373ade3e99d40", + "sha256:91bd7d1773e64019f9288b7a5101f3ae50d3d8e6b1de7edee9c2ccc1d32f0c0a", + "sha256:95c658736ec15602da0ed73f312d410117723914a5c91a14ee4cdd72f1d790b3", + "sha256:99039fa9e6306880572915728d7f6c24a86ec57b0a83f6b2491e1d8ab0235b9a", + "sha256:9a2bce789a5ea90e51a02dfcc39e31b7f1e662bc3317979aa7e5538e3a034f72", + "sha256:9a7d15bbd2bc99e92e39f49a04653062ee6085c0e18b3b7512a4f2fe91f2d681", + "sha256:9abc77a4ce4c6f2a3168ff34b1da9b0f311a8f1cfd694ec96b0603dff1c79438", + "sha256:9e8659775f1adf02eb1e6f109751268e493c73716ca5761f8acb695e52a756ae", + "sha256:9fee687dce376205d9a494e9c121e27183b2a3df18037f89d69bd7b35bcf59e2", + "sha256:a5aaeff38654462bc4b09023918b7f21790efb807f54c000a39d41d69cf552cb", + "sha256:a604bf7a053f8362d27eb9fefd2097f82600b856d5abe996d623babd067b1ab5", + "sha256:abbb9e76177c35d4e8568e58650aa6926040d6a9f6f03435b7a522bf1c487f9a", + "sha256:acc130bc0375999da18e3d19e5a86403667ac0c4042a094fefb7eec8ebac7cf3", + "sha256:b18f2d1533a71f069c7f82d524a52599053d4c7166e9dd374ae2136b7f40f7c8", + "sha256:b4e42a40a5e164cbfdb7b386c966a588b1047558a990981ace551ed7e12ca9c2", + "sha256:b5e251054542ae57ac7f3fba5d10bfff615b6c2fb09abeb37d2f1463f841ae22", + "sha256:b60fb58b90c6d63779cb0c0c54eeb38941bae3ecf7a73c764c52c88c2dcb9d72", + "sha256:b870b5df5b71d8c3359d21be8f0d6c485fa0ebdb6477dda51a1ea54a9b558061", + "sha256:ba0f0eb61ef00ea10e00eb53a9129501f52385c44853dbd6c4ad3f403603083f", + "sha256:bb87745b2e6dc56361bfde481d5a378dc314b252a98d7dd19a651a3fa58f24a9", + "sha256:bb90fb8bda722a1b9d48ac1e6c38f923ea757b3baf8ebd0c82e09c5c1a0e7a04", + "sha256:bc570b5f14a79734437cb7b0500376b6b791153314986074486e0b0fa8d71d98", + "sha256:c86563182421896d73858e08e1db93afdd2b947a70064b813d515d66549e15f9", + "sha256:c958bcfd59bacc2d0249dcfe575e71da54f9dcf4a8bdf89c4cb9a68a1170d73f", + "sha256:d18a4865f46b8579d44e4fe1e2bcbc6472ad83d98e22a26c963d46e4c125ef0b", + "sha256:d5e2439eecc762cd85e7bd37161d4714aa03a33c5ba884e26c81559817ca0925", + "sha256:e3890b508a23299083e065f435a492b5435eba6e304a7114d2f919d400888cc6", + "sha256:e496a8ce2c256da1eb98bd15803a79bee00fc351f5dfb9ea82594a3f058309e0", + "sha256:e8b2816ebef96d83657b56306152a93909a83f23994f4b30ad4573b00bd11bb9", + "sha256:eaf675418ed6b3b31c7a989fd007fa7c3be66ce14e5c3b27336383604c9da85c", + "sha256:ec89ed91f2fa8e3f52ae53cd3cf640d6feff92ba90d62236a81e4e563ac0e991", + "sha256:ecc840861360ba9d176d413a5489b9a0aff6d6303d7e733e2c4623cfa26904a6", + "sha256:f09b286faeff3c750a879d336fb6d8713206fc97af3adc14def0cdd349df6000", + "sha256:f393cda562f79828f38a819f4788641ac7c4085f30f1ce1a68672baa686482bb", + "sha256:f917c1180fdb8623c2b75a99192f4025e412597c50b2ac870f156de8fb101119", + "sha256:fc78a84e2dfbc27afe4b2bd7c80c8db9bca75cc5b85df52bfe634596a1da846b", + "sha256:ff04ef6eec3eee8a5efef2401495967a916feaa353643defcc03fc74fe213b58" + ], + "markers": "python_version >= '3.8'", + "version": "==1.17.2" + }, "zipp": { "hashes": [ "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4", diff --git a/lib/python/data.csv b/lib/python/data.csv deleted file mode 100644 index f776784b3750ee8d183efec700935bb0cbea570e..0000000000000000000000000000000000000000 Binary files a/lib/python/data.csv and /dev/null differ diff --git a/lib/python/dbrepo/RestClient.py b/lib/python/dbrepo/RestClient.py index 54d377079d4e8f32304f05652c8d9da01857bdd0..368e3886be85abe57daab86692f9b4a2672aad77 100644 --- a/lib/python/dbrepo/RestClient.py +++ b/lib/python/dbrepo/RestClient.py @@ -1,7 +1,6 @@ import logging import os import sys -import time import requests from pandas import DataFrame @@ -9,7 +8,7 @@ from pydantic import TypeAdapter from dbrepo.UploadClient import UploadClient from dbrepo.api.dto import * -from dbrepo.api.exceptions import ResponseCodeError, UsernameExistsError, EmailExistsError, NotExistsError, \ +from dbrepo.api.exceptions import ResponseCodeError, NotExistsError, \ ForbiddenError, MalformedError, NameExistsError, QueryStoreError, ExternalSystemError, \ AuthenticationError, FormatNotAvailable, RequestError, ServiceError, ServiceConnectionError @@ -78,71 +77,6 @@ class RestClient: return requests.request(method=method, url=url, auth=auth, verify=self.secure, json=payload, headers=headers, params=params, stream=stream) - def get_jwt_auth(self, username: str = None, password: str = None) -> JwtAuth: - """ - Obtains a JWT auth object from the auth service containing e.g. the access token and refresh token. - - :param username: The username used to authenticate with the auth service. Optional. Default: username from the `RestClient` constructor. - :param password: The password used to authenticate with the auth service. Optional. Default: password from the `RestClient` constructor. - - :returns: JWT auth object from the auth service, if successful. - - :raises MalformedError: If the payload was rejected by the service. - :raises ForbiddenError: If something went wrong with the authorization. - :raises AuthenticationError: If something went wrong with the authentication. - :raises ServiceConnectionError: If something went wrong with connection to the auth service. - :raises ServiceError: If something went wrong with obtaining the information in the auth service. - :raises ResponseCodeError: If something went wrong with the authentication. - """ - if username is None: - username = self.username - if password is None: - password = self.password - url = f'{self.endpoint}/api/user/token' - response = requests.post(url=url, json=dict({"username": username, "password": password})) - if response.status_code == 202: - body = response.json() - return JwtAuth.model_validate(body) - if response.status_code == 400: - raise MalformedError(f'Failed to get JWT: {response.text}') - if response.status_code == 403: - raise ForbiddenError(f'Failed to get JWT: not allowed') - if response.status_code == 428: - raise AuthenticationError(f'Failed to get JWT: account not fully setup (requires password change?)') - if response.status_code == 502: - raise ServiceConnectionError(f'Failed to get JWT: failed to establish connection with auth service') - if response.status_code == 503: - raise ServiceError(f'Failed to get JWT: failed to get user in auth service') - raise ResponseCodeError(f'Failed to get JWT: response code: {response.status_code} is not ' - f'202 (ACCEPTED): {response.text}') - - def refresh_jwt_auth(self, refresh_token: str) -> JwtAuth: - """ - Refreshes a JWT auth object from the auth service containing e.g. the access token and refresh token. - - :param refresh_token: The refresh token. - - :returns: JWT auth object from the auth service, if successful. - - :raises MalformedError: If the payload was rejected by the service. - :raises ForbiddenError: If something went wrong with the authorization. - :raises ServiceConnectionError: If something went wrong with the connection to the auth service. - :raises ResponseCodeError: If something went wrong with the authentication. - """ - url = f'{self.endpoint}/api/user/token' - response = requests.put(url=url, json=dict({"refresh_token": refresh_token})) - if response.status_code == 202: - body = response.json() - return JwtAuth.model_validate(body) - if response.status_code == 400: - raise MalformedError(f'Failed to refresh JWT: {response.text}') - if response.status_code == 403: - raise ForbiddenError(f'Failed to refresh JWT: not allowed') - if response.status_code == 502: - raise ServiceConnectionError(f'Failed to refresh JWT: failed to establish connection with auth service') - raise ResponseCodeError(f'Failed to refresh JWT: response code: {response.status_code} is not ' - f'202 (ACCEPTED): {response.text}') - def whoami(self) -> str | None: """ Print the username. @@ -209,48 +143,6 @@ class RestClient: raise ResponseCodeError(f'Failed to find user: response code: {response.status_code} is not ' f'200 (OK): {response.text}') - def create_user(self, username: str, password: str, email: str) -> UserBrief: - """ - Creates a new user. - - :param username: The username of the new user. Must be unique. - :param password: The password of the new user. - :param email: The email of the new user. Must be unique. - - :returns: The user, if successful. - - :raises MalformedError: If the payload was rejected by the service. - :raises ForbiddenError: If the internal authentication to the auth service is invalid. - :raises UsernameExistsError: The username exists already. - :raises ForbiddenError: If something went wrong with the authorization. - :raises NotExistsError: If the created user was not found in the auth service. - :raises EmailExistsError: The email exists already. - :raises ServiceConnectionError: If something went wrong with connection to the auth service. - :raises ServiceError: If something went wrong with obtaining the information in the auth service. - """ - url = f'/api/user' - response = self._wrapper(method="post", url=url, - payload=CreateUser(username=username, password=password, email=email)) - if response.status_code == 201: - body = response.json() - return UserBrief.model_validate(body) - if response.status_code == 400: - raise MalformedError(f'Failed to create user: {response.text}') - if response.status_code == 403: - raise ForbiddenError(f'Failed to create user: internal authentication to the auth service is invalid') - if response.status_code == 404: - raise NotExistsError(f'Failed to create user: created user not found in auth service') - if response.status_code == 409: - raise UsernameExistsError(f'Failed to create user: user with username exists') - if response.status_code == 417: - raise EmailExistsError(f'Failed to create user: user with e-mail exists') - if response.status_code == 502: - raise ServiceConnectionError(f'Failed to create user: failed to establish connection with auth service') - if response.status_code == 503: - raise ServiceError(f'Failed to create user: failed to create in auth service') - raise ResponseCodeError(f'Failed to create user: response code: {response.status_code} is not ' - f'201 (CREATED): {response.text}') - def update_user(self, user_id: str, theme: str, language: str, firstname: str = None, lastname: str = None, affiliation: str = None, orcid: str = None) -> UserBrief: """ @@ -287,38 +179,6 @@ class RestClient: raise ResponseCodeError(f'Failed to update user: response code: {response.status_code} is not ' f'202 (ACCEPTED): {response.text}') - def update_user_password(self, user_id: str, password: str) -> None: - """ - Updates the password of a user with given user id. - - :param user_id: The user id of the user that should be updated. - :param password: The updated user password. - - :raises MalformedError: If the payload was rejected by the service. - :raises ForbiddenError: If something went wrong with the authorization. - :raises NotExistsError: If the user does not exist. - :raises ServiceConnectionError: If something went wrong with connection to the auth service. - :raises ServiceError: If something went wrong with obtaining the information in the auth service. - :raises ResponseCodeError: If something went wrong with the update. - """ - url = f'/api/user/{user_id}/password' - response = self._wrapper(method="put", url=url, force_auth=True, payload=UpdateUserPassword(password=password)) - if response.status_code == 202: - return None - if response.status_code == 400: - raise MalformedError(f'Failed to update user password: {response.text}') - if response.status_code == 403: - raise ForbiddenError(f'Failed to update user password: not allowed') - if response.status_code == 404: - raise NotExistsError(f'Failed to update user password: not found') - if response.status_code == 502: - raise ServiceConnectionError( - f'Failed to update user password: failed to establish connection with auth service') - if response.status_code == 503: - raise ServiceError(f'Failed to update user password: failed to update in auth service') - raise ResponseCodeError(f'Failed to update user theme: response code: {response.status_code} is not ' - f'202 (ACCEPTED): {response.text}') - def get_containers(self) -> List[ContainerBrief]: """ Get all containers. @@ -393,8 +253,6 @@ class RestClient: :returns: The database, if successful. :raises NotExistsError: If the container does not exist. - :raises ServiceConnectionError: If something went wrong with connection to the broker service. - :raises ServiceError: If something went wrong with obtaining the information in the broker service. :raises ResponseCodeError: If something went wrong with the retrieval. """ url = f'/api/database/{database_id}' @@ -402,12 +260,10 @@ class RestClient: if response.status_code == 200: body = response.json() return Database.model_validate(body) + if response.status_code == 403: + raise ForbiddenError(f'Failed to find database: not allowed') if response.status_code == 404: raise NotExistsError(f'Failed to find database: not found') - if response.status_code == 502: - raise ServiceConnectionError(f'Failed to find database: failed to establish connection with broker service') - if response.status_code == 503: - raise ServiceError(f'Failed to find database: failed to obtain queue metadata from broker service') raise ResponseCodeError(f'Failed to find database: response code: {response.status_code} is not ' f'200 (OK): {response.text}') @@ -453,9 +309,8 @@ class RestClient: raise ResponseCodeError(f'Failed to create database: response code: {response.status_code} is not ' f'201 (CREATED): {response.text}') - def create_container(self, name: str, host: str, image_id: int, sidecar_host: str, sidecar_port: int, - privileged_username: str, privileged_password: str, port: int = None, ui_host: str = None, - ui_port: int = None) -> Container: + def create_container(self, name: str, host: str, image_id: int, privileged_username: str, privileged_password: str, + port: int = None, ui_host: str = None, ui_port: int = None) -> Container: """ Register a container instance executing a given container image. Note that this does not create a container, but only saves it in the metadata database to be used within DBRepo. The container still needs to be created @@ -464,8 +319,6 @@ class RestClient: :param name: The container name. :param host: The container hostname. :param image_id: The container image id. - :param sidecar_host: The container sidecar hostname. - :param sidecar_port: The container sidecar port. :param privileged_username: The container privileged user username. :param privileged_password: The container privileged user password. :param port: The container port bound to the host. Optional. @@ -483,7 +336,6 @@ class RestClient: url = f'/api/container' response = self._wrapper(method="post", url=url, force_auth=True, payload=CreateContainer(name=name, host=host, image_id=image_id, - sidecar_host=sidecar_host, sidecar_port=sidecar_port, privileged_username=privileged_username, privileged_password=privileged_password, port=port, ui_host=ui_host, ui_port=ui_port)) @@ -574,7 +426,7 @@ class RestClient: raise ResponseCodeError( f'Failed to update database visibility: response code: {response.status_code} is not 202 (ACCEPTED)') - def update_database_schema(self, database_id: int) -> Database: + def update_database_schema(self, database_id: int) -> DatabaseBrief: """ Updates the database table and view metadata of a database with given database id. @@ -597,7 +449,7 @@ class RestClient: response = self._wrapper(method="put", url=url, force_auth=True) if response.status_code == 200: body = response.json() - return Database.model_validate(body) + return DatabaseBrief.model_validate(body) if response.status_code == 400: raise MalformedError(f'Failed to update database schema: {response.text}') if response.status_code == 403: @@ -693,8 +545,6 @@ class RestClient: :raises ForbiddenError: If something went wrong with the authorization. :raises NotExistsError: If the table does not exist. - :raises ServiceConnectionError: If something went wrong with connection to the metadata service. - :raises ServiceError: If something went wrong with obtaining the information in the metadata service. :raises ResponseCodeError: If something went wrong with the retrieval. """ url = f'/api/database/{database_id}/table/{table_id}' @@ -706,10 +556,6 @@ class RestClient: raise ForbiddenError(f'Failed to find table: not allowed') if response.status_code == 404: raise NotExistsError(f'Failed to find table: not found') - if response.status_code == 502: - raise ServiceConnectionError(f'Failed to find table: failed to establish connection to broker service') - if response.status_code == 503: - raise ServiceError(f'Failed to find table: failed to obtain queue information from broker service') raise ResponseCodeError(f'Failed to find table: response code: {response.status_code} is not ' f'200 (OK): {response.text}') @@ -770,29 +616,7 @@ class RestClient: raise ResponseCodeError(f'Failed to delete container: response code: {response.status_code} is not ' f'202 (ACCEPTED): {response.text}') - def get_table_metadata(self, database_id: int) -> Database: - """ - Generate metadata of all system-versioned tables in a database with given id. - - :param database_id: The database id. - - :raises ForbiddenError: If something went wrong with the authorization. - :raises NotExistsError: If the table does not exist. - :raises ResponseCodeError: If something went wrong with the retrieval. - """ - url = f'/api/database/{database_id}/metadata/table' - response = self._wrapper(method="put", url=url, force_auth=True) - if response.status_code == 200: - body = response.json() - return Database.model_validate(body) - if response.status_code == 403: - raise ForbiddenError(f'Failed to get tables metadata: not allowed') - if response.status_code == 404: - raise NotExistsError(f'Failed to get tables metadata: not found') - raise ResponseCodeError(f'Failed to get tables metadata: response code: {response.status_code} is not ' - f'200 (OK): {response.text}') - - def get_table_history(self, database_id: int, table_id: int, size: int = 100) -> Database: + def get_table_history(self, database_id: int, table_id: int, size: int = 100) -> [History]: """ Get the table history of insert/delete operations. @@ -807,10 +631,10 @@ class RestClient: :raises ResponseCodeError: If something went wrong with the retrieval. """ url = f'/api/database/{database_id}/table/{table_id}/history?size={size}' - response = self._wrapper(method="get", url=url, force_auth=True) + response = self._wrapper(method="get", url=url) if response.status_code == 200: body = response.json() - return Database.model_validate(body) + return TypeAdapter(List[History]).validate_python(body) if response.status_code == 400: raise MalformedError(f'Failed to get table history: {response.text}') if response.status_code == 403: @@ -868,13 +692,14 @@ class RestClient: raise ResponseCodeError(f'Failed to find view: response code: {response.status_code} is not ' f'200 (OK): {response.text}') - def update_view(self, database_id: int, view_id: int, is_public: bool) -> ViewBrief: + def update_view(self, database_id: int, view_id: int, is_public: bool, is_schema_public: bool) -> ViewBrief: """ Get a view of a database with given database id and view id. :param database_id: The database id. :param view_id: The view id. - :param is_public: If set to `True`, the view is publicly visible. + :param is_public: If set to `True`, the view data is publicly visible. + :param is_schema_public: If set to `True`, the view schema is publicly visible. :returns: The view, if successful. @@ -883,7 +708,8 @@ class RestClient: :raises ResponseCodeError: If something went wrong with the retrieval. """ url = f'/api/database/{database_id}/view/{view_id}' - response = self._wrapper(method="put", url=url, payload=UpdateView(is_public=is_public)) + response = self._wrapper(method="put", url=url, force_auth=True, payload=UpdateView(is_public=is_public, + is_schema_public=is_schema_public)) if response.status_code == 202: body = response.json() return ViewBrief.model_validate(body) @@ -1010,28 +836,6 @@ class RestClient: raise ResponseCodeError(f'Failed to get view data: response code: {response.status_code} is not ' f'200 (OK):{response.text}') - def get_views_metadata(self, database_id: int) -> Database: - """ - Generate metadata of all views in a database with given id. - - :param database_id: The database id. - - :raises ForbiddenError: If something went wrong with the authorization. - :raises NotExistsError: If the container does not exist. - :raises ResponseCodeError: If something went wrong with the retrieval. - """ - url = f'/api/database/{database_id}/metadata/view' - response = self._wrapper(method="put", url=url, force_auth=True) - if response.status_code == 200: - body = response.json() - return Database.model_validate(body) - if response.status_code == 403: - raise ForbiddenError(f'Failed to get views metadata: not allowed') - if response.status_code == 404: - raise NotExistsError(f'Failed to get views metadata: not found') - raise ResponseCodeError(f'Failed to get views metadata: response code: {response.status_code} is not ' - f'200 (OK): {response.text}') - def get_table_data(self, database_id: int, table_id: int, page: int = 0, size: int = 10, timestamp: datetime.datetime = None) -> DataFrame: """ @@ -1103,9 +907,7 @@ class RestClient: raise ResponseCodeError(f'Failed to insert table data: response code: {response.status_code} is not ' f'201 (CREATED): {response.text}') - def import_table_data(self, database_id: int, table_id: int, file_name_or_data_frame: str | DataFrame, - separator: str = ",", quote: str = "\"", header: bool = False, - line_encoding: str = "\n") -> None: + def import_table_data(self, database_id: int, table_id: int, dataframe: DataFrame) -> None: """ Import a csv dataset from a file into a table in a database with given database id and table id. ATTENTION: the import is column-ordering sensitive! The csv dataset must have the same columns in the same order as the @@ -1113,11 +915,7 @@ class RestClient: :param database_id: The database id. :param table_id: The table id. - :param file_name_or_data_frame: The path of the file that is imported on the storage service or pandas dataframe. - :param separator: The csv column separator. Optional. - :param quote: The column data quotation character. Optional. - :param header: If `True`, the first line contains column names, otherwise the first line is data. Optional. Default: `False`. - :param line_encoding: The encoding of the line termination. Optional. Default: CR (Windows). + :param dataframe: The pandas dataframe. :raises MalformedError: If the payload is rejected by the service (e.g. LOB could not be imported). :raises ForbiddenError: If something went wrong with the authorization. @@ -1125,18 +923,12 @@ class RestClient: :raises ServiceError: If something went wrong with obtaining the information in the metadata service. :raises ResponseCodeError: If something went wrong with the insert. """ - client = UploadClient(endpoint=f"{self.endpoint}/api/upload/files") - if type(file_name_or_data_frame) is DataFrame: - file_path: str = f"./tmp-{time.time()}" - df: DataFrame = file_name_or_data_frame - df.to_csv(path_or_buf=file_path, index=False, header=False) - else: - file_path: str = file_name_or_data_frame - filename = client.upload(file_path=file_path) + client = UploadClient() + filename = client.upload(dataframe) url = f'/api/database/{database_id}/table/{table_id}/data/import' response = self._wrapper(method="post", url=url, force_auth=True, - payload=Import(location=filename, separator=separator, quote=quote, - header=header, line_termination=line_encoding)) + payload=Import(location=filename, separator=',', quote='"', header=True, + line_termination='\n')) if response.status_code == 202: return if response.status_code == 400: @@ -1151,17 +943,13 @@ class RestClient: raise ResponseCodeError(f'Failed to import table data: response code: {response.status_code} is not ' f'202 (ACCEPTED): {response.text}') - def analyse_datatypes(self, file_path: str, separator: str, enum: bool = None, - enum_tol: int = None, upload: bool = True) -> DatatypeAnalysis: + def analyse_datatypes(self, dataframe: DataFrame, enum: bool = None, enum_tol: int = None) -> DatatypeAnalysis: """ Import a csv dataset from a file and analyse it for the possible enums, line encoding and column data types. - :param file_path: The path of the file that is imported on the storage service. - :param separator: The csv column separator. + :param dataframe: The dataframe. :param enum: If set to true, enumerations should be guessed, otherwise no guessing. Optional. :param enum_tol: The tolerance for guessing enumerations (ignored if enum=False). Optional. - :param upload: If set to true, the file from file_path will be uploaded, otherwise no upload will be performed \ - and the file_path will be treated as S3 filename and analysed instead. Optional. Default: true. :returns: The determined data types, if successful. @@ -1169,14 +957,11 @@ class RestClient: :raises NotExistsError: If the file was not found by the Analyse Service. :raises ResponseCodeError: If something went wrong with the analysis. """ - if upload: - client = UploadClient(endpoint=f"{self.endpoint}/api/upload/files") - filename = client.upload(file_path=file_path) - else: - filename = file_path + client = UploadClient() + filename = client.upload(dataframe) params = [ ('filename', filename), - ('separator', separator), + ('separator', ','), ('enum', enum), ('enum_tol', enum_tol) ] @@ -1192,14 +977,11 @@ class RestClient: raise ResponseCodeError(f'Failed to analyse data types: response code: {response.status_code} is not ' f'202 (ACCEPTED): {response.text}') - def analyse_keys(self, file_path: str, separator: str, upload: bool = True) -> KeyAnalysis: + def analyse_keys(self, dataframe: DataFrame) -> KeyAnalysis: """ Import a csv dataset from a file and analyse it for the possible primary key. - :param file_path: The path of the file that is imported on the storage service. - :param separator: The csv column separator. - :param upload: If set to true, the file from file_path will be uploaded, otherwise no upload will be performed \ - and the file_path will be treated as S3 filename and analysed instead. Optional. Default: `True`. + :param dataframe: The dataframe. :returns: The determined ranking of the primary key candidates, if successful. @@ -1207,14 +989,11 @@ class RestClient: :raises NotExistsError: If the file was not found by the Analyse Service. :raises ResponseCodeError: If something went wrong with the analysis. """ - if upload: - client = UploadClient(endpoint=f"{self.endpoint}/api/upload/files") - filename = client.upload(file_path=file_path) - else: - filename = file_path + client = UploadClient() + filename = client.upload(dataframe=dataframe) params = [ ('filename', filename), - ('separator', separator), + ('separator', ','), ] url = f'/api/analyse/keys' response = self._wrapper(method="get", url=url, params=params) @@ -1253,7 +1032,7 @@ class RestClient: if response.status_code == 404: raise NotExistsError(f'Failed to analyse table statistics: separator error') if response.status_code == 502: - raise NotExistsError( + raise ServiceConnectionError( f'Failed to analyse table statistics: data service failed to establish connection to metadata service') if response.status_code == 503: raise ServiceError(f'Failed to analyse table statistics: failed to save statistic in search service') diff --git a/lib/python/dbrepo/UploadClient.py b/lib/python/dbrepo/UploadClient.py index b0bc90fc00aa1810d4e80873b9e8583ff1a4e5be..cb4dc3a3c534cd9534d28863c7a504233b7551ab 100644 --- a/lib/python/dbrepo/UploadClient.py +++ b/lib/python/dbrepo/UploadClient.py @@ -2,8 +2,13 @@ import logging import os import re import sys +from io import BytesIO + +from pandas import DataFrame from tusclient import client +from dbrepo.api.exceptions import UploadError + logging.basicConfig(format='%(asctime)s %(name)-12s %(levelname)-6s %(message)s', level=logging.INFO, stream=sys.stdout) @@ -21,19 +26,15 @@ class UploadClient: def __init__(self, endpoint: str = 'http://gateway-service/api/upload/files') -> None: self.endpoint = os.environ.get('REST_UPLOAD_ENDPOINT', endpoint) - def upload(self, file_path: str) -> str: - """ - Imports a file through the Upload Service into the Storage Service. - - :param file_path: The file path on the local machine. - - :returns: Filename on the Storage Service, if successful. - """ - logging.debug(f"upload file to endpoint: {self.endpoint}") + def upload(self, dataframe: DataFrame) -> str: + logging.debug(f"upload to endpoint: {self.endpoint}") tus_client = client.TusClient(url=self.endpoint) - uploader = tus_client.uploader(file_path=file_path) + buffer: BytesIO = BytesIO(dataframe.to_csv(index=False, header=False).encode('utf-8')) + uploader = tus_client.uploader(file_stream=buffer) uploader.upload() - m = re.search('\\/([a-f0-9]+)\\+', uploader.url) + m = re.search('\\/([a-f0-9]+)$', uploader.url) filename = m.group(0)[1:-1] - logging.info(f'Uploaded file {file_path} to storage service with key: {filename}') + if filename is None: + raise UploadError('Failed to upload file: no filename') + logging.info(f'Uploaded to storage service with key: {filename}') return filename diff --git a/lib/python/dbrepo/api/dto.py b/lib/python/dbrepo/api/dto.py index 582f40c9b16a8fe60fd7b2f4a14901958a7a7b3b..27801a948238953ffdd66eb35c049e44d4a2bd5d 100644 --- a/lib/python/dbrepo/api/dto.py +++ b/lib/python/dbrepo/api/dto.py @@ -12,18 +12,6 @@ Timestamp = Annotated[ ] -class JwtAuth(BaseModel): - access_token: str - refresh_token: str - id_token: str - expires_in: int - refresh_expires_in: int - not_before_policy: int - scope: str - session_state: str - token_type: str - - class Image(BaseModel): id: int registry: str @@ -52,6 +40,7 @@ class CreateDatabase(BaseModel): class UpdateView(BaseModel): is_public: bool + is_schema_public: bool class CreateContainer(BaseModel): @@ -134,6 +123,7 @@ class TableBrief(BaseModel): class UserAttributes(BaseModel): theme: str + language: str orcid: Optional[str] = None affiliation: Optional[str] = None @@ -690,6 +680,12 @@ class CreateView(BaseModel): is_schema_public: bool +class History(BaseModel): + event: HistoryEventType + total: int + timestamp: Timestamp + + class ViewBrief(BaseModel): id: int database_id: int @@ -759,6 +755,14 @@ class TitleType(str, Enum): OTHER = "Other" +class HistoryEventType(str, Enum): + """ + Enumeration of history event types. + """ + INSERT = "insert" + DELETE = "delete" + + class RelatedIdentifierType(str, Enum): """ Enumeration of related identifier types. diff --git a/lib/python/tests/conftest.py b/lib/python/tests/conftest.py new file mode 100644 index 0000000000000000000000000000000000000000..d1a41ca922c870f37fb12f2b9a68de5d1ff68286 --- /dev/null +++ b/lib/python/tests/conftest.py @@ -0,0 +1,31 @@ +import logging +import os + +import pytest +from testcontainers.generic import ServerContainer + +logging.basicConfig(level=logging.DEBUG) + + +@pytest.fixture(scope="session", autouse=True) +def session(request): + """ + Create one TUSd container per test run only + :param request: / + :return: The TUSd container + """ + logging.debug("[fixture] creating container") + container = ServerContainer(port=8080, image='tusproject/tusd:v2.4.0').with_command("-base-path=/api/upload/files/") + logging.debug("[fixture] starting container") + container.start() + # set the environment for the client + endpoint = f"http://{container.get_container_host_ip()}:{container.get_exposed_port(8080)}/api/upload/files" + logging.debug(f'set upload endpoint {endpoint}') + os.environ['REST_UPLOAD_ENDPOINT'] = endpoint + + # destructor + def stop_tusd(): + container.stop() + + request.addfinalizer(stop_tusd) + return container diff --git a/lib/python/tests/test_integration_table.py b/lib/python/tests/test_integration_table.py new file mode 100644 index 0000000000000000000000000000000000000000..f8ca294779e8d93559514e533820ccd3e773566d --- /dev/null +++ b/lib/python/tests/test_integration_table.py @@ -0,0 +1,83 @@ +import os +import unittest + +import requests_mock +from pandas import DataFrame + +from dbrepo.RestClient import RestClient +from dbrepo.api.exceptions import ForbiddenError, MalformedError, NotExistsError, ServiceError, ResponseCodeError + + +class TableUnitTest(unittest.TestCase): + + def test_import_table_data_succeeds(self): + with requests_mock.Mocker() as mock: + mock.register_uri('POST', os.environ.get('REST_UPLOAD_ENDPOINT'), real_http=True) + # mock + mock.post('/api/database/1/table/9/data/import', status_code=202) + # test + RestClient(username="a", password="b").import_table_data(database_id=1, table_id=9, + dataframe=DataFrame()) + + def test_import_table_data_400_fails(self): + with requests_mock.Mocker() as mock: + mock.register_uri('POST', os.environ.get('REST_UPLOAD_ENDPOINT'), real_http=True) + # mock + mock.post('/api/database/1/table/9/data/import', status_code=400) + try: + RestClient(username="a", password="b").import_table_data(database_id=1, table_id=9, + dataframe=DataFrame()) + except MalformedError: + pass + + def test_import_table_data_403_fails(self): + with requests_mock.Mocker() as mock: + mock.register_uri('POST', os.environ.get('REST_UPLOAD_ENDPOINT'), real_http=True) + # mock + mock.post('/api/database/1/table/9/data/import', status_code=403) + # test + try: + RestClient(username="a", password="b").import_table_data(database_id=1, table_id=9, + dataframe=DataFrame()) + except ForbiddenError: + pass + + def test_import_table_data_404_fails(self): + with requests_mock.Mocker() as mock: + mock.register_uri('POST', os.environ.get('REST_UPLOAD_ENDPOINT'), real_http=True) + # mock + mock.post('/api/database/1/table/9/data/import', status_code=404) + # test + try: + RestClient(username="a", password="b").import_table_data(database_id=1, table_id=9, + dataframe=DataFrame()) + except NotExistsError: + pass + + def test_import_table_data_503_fails(self): + with requests_mock.Mocker() as mock: + mock.register_uri('POST', os.environ.get('REST_UPLOAD_ENDPOINT'), real_http=True) + # mock + mock.post('/api/database/1/table/9/data/import', status_code=503) + # test + try: + RestClient(username="a", password="b").import_table_data(database_id=1, table_id=9, + dataframe=DataFrame()) + except ServiceError: + pass + + def test_import_table_data_unknown_fails(self): + with requests_mock.Mocker() as mock: + mock.register_uri('POST', os.environ.get('REST_UPLOAD_ENDPOINT'), real_http=True) + # mock + mock.post('/api/database/1/table/9/data/import', status_code=200) + # test + try: + RestClient(username="a", password="b").import_table_data(database_id=1, table_id=9, + dataframe=DataFrame()) + except ResponseCodeError: + pass + + +if __name__ == "__main__": + unittest.main() diff --git a/lib/python/tests/test_unit_analyse.py b/lib/python/tests/test_unit_analyse.py index 5ca2b8301cbb04f9e896436909c97b4873842612..fbbb342f790a19b9e930db8df899ae1d044ab590 100644 --- a/lib/python/tests/test_unit_analyse.py +++ b/lib/python/tests/test_unit_analyse.py @@ -1,9 +1,13 @@ +import os +import re import unittest + import requests_mock +from pandas import DataFrame from dbrepo.RestClient import RestClient - -from dbrepo.api.dto import KeyAnalysis +from dbrepo.api.dto import KeyAnalysis, DatatypeAnalysis, ColumnType +from dbrepo.api.exceptions import NotExistsError, MalformedError, ResponseCodeError class AnalyseUnitTest(unittest.TestCase): @@ -12,12 +16,89 @@ class AnalyseUnitTest(unittest.TestCase): with requests_mock.Mocker() as mock: exp = KeyAnalysis(keys={'id': 0, 'firstname': 1, 'lastname': 2}) # mock - mock.get('/api/analyse/keys', json=exp.model_dump(), status_code=202) + mock.register_uri('POST', os.environ.get('REST_UPLOAD_ENDPOINT'), real_http=True) + mock.register_uri('GET', '/api/analyse/keys', json=exp.model_dump(), status_code=202) # test - response = RestClient().analyse_keys(file_path='f705a7bd0cb2d5e37ab2b425036810a2', separator=',', - upload=False) + response = RestClient().analyse_keys(dataframe=DataFrame()) self.assertEqual(exp, response) + def test_analyse_keys_400_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.register_uri('POST', os.environ.get('REST_UPLOAD_ENDPOINT'), real_http=True) + mock.register_uri('GET', '/api/analyse/keys', status_code=400) + # test + try: + RestClient().analyse_keys(dataframe=DataFrame()) + except MalformedError: + pass + + def test_analyse_keys_404_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.register_uri('POST', os.environ.get('REST_UPLOAD_ENDPOINT'), real_http=True) + mock.register_uri('GET', '/api/analyse/keys', status_code=404) + # test + try: + RestClient().analyse_keys(dataframe=DataFrame()) + except NotExistsError: + pass + + def test_analyse_keys_unknown_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.register_uri('POST', os.environ.get('REST_UPLOAD_ENDPOINT'), real_http=True) + mock.register_uri('GET', '/api/analyse/keys', status_code=200) + # test + try: + RestClient().analyse_keys(dataframe=DataFrame()) + except ResponseCodeError: + pass + + def test_analyse_datatypes_succeeds(self): + with requests_mock.Mocker() as mock: + exp = DatatypeAnalysis(separator=',', + columns={'id': ColumnType.SERIAL}) + # mock + mock.register_uri('POST', os.environ.get('REST_UPLOAD_ENDPOINT'), real_http=True) + mock.register_uri('GET', re.compile('/api/analyse/datatypes.*'), json=exp.model_dump(), status_code=202) + # test + response = RestClient().analyse_datatypes(dataframe=DataFrame()) + self.assertEqual(exp, response) + + def test_analyse_datatypes_400_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.register_uri('POST', os.environ.get('REST_UPLOAD_ENDPOINT'), real_http=True) + mock.register_uri('GET', re.compile('/api/analyse/datatypes.*'), status_code=400) + # test + try: + RestClient().analyse_datatypes(dataframe=DataFrame()) + except MalformedError: + pass + + def test_analyse_datatypes_404_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.register_uri('POST', os.environ.get('REST_UPLOAD_ENDPOINT'), real_http=True) + mock.register_uri('GET', re.compile('/api/analyse/datatypes.*'), status_code=404) + # test + try: + RestClient().analyse_datatypes(dataframe=DataFrame()) + except NotExistsError: + pass + + def test_analyse_datatypes_unknown_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.register_uri('POST', os.environ.get('REST_UPLOAD_ENDPOINT'), real_http=True) + mock.register_uri('GET', re.compile('/api/analyse/datatypes.*'), status_code=200) + # test + try: + RestClient().analyse_datatypes(dataframe=DataFrame()) + except ResponseCodeError: + pass + if __name__ == "__main__": unittest.main() diff --git a/lib/python/tests/test_unit_container.py b/lib/python/tests/test_unit_container.py index b7a35e6a4a714f38d3cb06841541b182a0c769e2..2edc65bf1983aa836eae6475918a990ae19611a6 100644 --- a/lib/python/tests/test_unit_container.py +++ b/lib/python/tests/test_unit_container.py @@ -1,15 +1,123 @@ import unittest import requests_mock -import datetime from dbrepo.RestClient import RestClient from dbrepo.api.dto import Container, Image, ContainerBrief, ImageBrief, DataType -from dbrepo.api.exceptions import ResponseCodeError, NotExistsError +from dbrepo.api.exceptions import ResponseCodeError, NotExistsError, AuthenticationError, MalformedError, \ + ForbiddenError, NameExistsError class ContainerUnitTest(unittest.TestCase): + def test_create_container_succeeds(self): + with requests_mock.Mocker() as mock: + exp = Container(id=1, + name="MariaDB 10.11.3", + internal_name="mariadb_10_11_3", + host="data-db", + port=12345, + image=Image(id=1, + registry="docker.io", + name="mariadb", + version="10.11.3", + default_port=3306, + dialect="org.hibernate.dialect.MariaDBDialect", + driver_class="org.mariadb.jdbc.Driver", + jdbc_method="mariadb", + data_types=[ + DataType(display_name="SERIAL", value="serial", + documentation="https://mariadb.com/kb/en/bigint/", + is_quoted=False, is_buildable=True)])) + # mock + mock.post('/api/container', json=exp.model_dump(), status_code=201) + # test + response = RestClient(username="foo", password="bar").create_container(name='MariaDB 10.11.3', + host='data-db2', image_id=1, + privileged_username='root', + privileged_password='dbrepo', + port=3306) + self.assertEqual(exp, response) + + def test_create_container_anonymous_fails(self): + # test + try: + response = RestClient().create_container(name='MariaDB 10.11.3', host='data-db2', image_id=1, + privileged_username='root', privileged_password='dbrepo', + port=3306) + except AuthenticationError: + pass + + def test_create_container_400_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.post('/api/container', status_code=400) + # test + try: + response = RestClient(username="foo", password="bar").create_container(name='MariaDB 10.11.3', + host='data-db2', image_id=1, + privileged_username='root', + privileged_password='dbrepo', + port=3306) + except MalformedError: + pass + + def test_create_container_403_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.post('/api/container', status_code=403) + # test + try: + response = RestClient(username="foo", password="bar").create_container(name='MariaDB 10.11.3', + host='data-db2', image_id=1, + privileged_username='root', + privileged_password='dbrepo', + port=3306) + except ForbiddenError: + pass + + def test_create_container_404_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.post('/api/container', status_code=404) + # test + try: + response = RestClient(username="foo", password="bar").create_container(name='MariaDB 10.11.3', + host='data-db2', image_id=1, + privileged_username='root', + privileged_password='dbrepo', + port=3306) + except NotExistsError: + pass + + def test_create_container_409_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.post('/api/container', status_code=409) + # test + try: + response = RestClient(username="foo", password="bar").create_container(name='MariaDB 10.11.3', + host='data-db2', image_id=1, + privileged_username='root', + privileged_password='dbrepo', + port=3306) + except NameExistsError: + pass + + def test_create_container_unknown_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.post('/api/container', status_code=200) + # test + try: + response = RestClient(username="foo", password="bar").create_container(name='MariaDB 10.11.3', + host='data-db2', image_id=1, + privileged_username='root', + privileged_password='dbrepo', + port=3306) + except ResponseCodeError: + pass + def test_get_containers_empty_succeeds(self): with requests_mock.Mocker() as mock: # mock @@ -52,11 +160,8 @@ class ContainerUnitTest(unittest.TestCase): exp = Container(id=1, name="MariaDB 10.11.3", internal_name="mariadb_10_11_3", - running=True, host="data-db", port=12345, - sidecar_host="data-db-sidecar", - sidecar_port=3305, image=Image(id=1, registry="docker.io", name="mariadb", @@ -68,15 +173,14 @@ class ContainerUnitTest(unittest.TestCase): data_types=[ DataType(display_name="SERIAL", value="serial", documentation="https://mariadb.com/kb/en/bigint/", - is_quoted=False, is_buildable=True)]), - hash="f829dd8a884182d0da846f365dee1221fd16610a14c81b8f9f295ff162749e50") + is_quoted=False, is_buildable=True)])) # mock mock.get('/api/container/1', json=exp.model_dump()) # test response = RestClient().get_container(container_id=1) self.assertEqual(exp, response) - def test_get_container_not_found_fails(self): + def test_get_container_404_fails(self): with requests_mock.Mocker() as mock: # mock mock.get('/api/container/1', status_code=404) @@ -86,6 +190,70 @@ class ContainerUnitTest(unittest.TestCase): except NotExistsError: pass + def test_get_container_unknown_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.get('/api/container/1', status_code=401) + # test + try: + response = RestClient().get_container(container_id=1) + except ResponseCodeError: + pass + + def test_delete_container_succeeds(self): + with requests_mock.Mocker() as mock: + # mock + mock.delete('/api/container/1', status_code=202) + # test + RestClient(username='foo', password='bar').delete_container(container_id=1) + + def test_delete_container_400_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.delete('/api/container/1', status_code=400) + # test + try: + RestClient(username='foo', password='bar').delete_container(container_id=1) + except MalformedError: + pass + + def test_delete_container_403_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.delete('/api/container/1', status_code=403) + # test + try: + RestClient(username='foo', password='bar').delete_container(container_id=1) + except ForbiddenError: + pass + + def test_delete_container_404_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.delete('/api/container/1', status_code=404) + # test + try: + RestClient(username='foo', password='bar').delete_container(container_id=1) + except NotExistsError: + pass + + def test_delete_container_unknown_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.delete('/api/container/1', status_code=200) + # test + try: + RestClient(username='foo', password='bar').delete_container(container_id=1) + except ResponseCodeError: + pass + + def test_delete_container_anonymous_fails(self): + # test + try: + RestClient().delete_container(container_id=1) + except AuthenticationError: + pass + if __name__ == "__main__": unittest.main() diff --git a/lib/python/tests/test_unit_database.py b/lib/python/tests/test_unit_database.py index 203c296b9c86d9bca0fce4f3b433af298f6cd251..0eb4675433d509c22ee77512af392b014c6e85aa 100644 --- a/lib/python/tests/test_unit_database.py +++ b/lib/python/tests/test_unit_database.py @@ -1,12 +1,12 @@ import unittest import requests_mock -from pydantic_core import ValidationError from dbrepo.RestClient import RestClient from dbrepo.api.dto import Database, DatabaseAccess, AccessType, DatabaseBrief, UserBrief, \ ContainerBrief, ImageBrief -from dbrepo.api.exceptions import ResponseCodeError, NotExistsError, ForbiddenError, MalformedError, AuthenticationError +from dbrepo.api.exceptions import ResponseCodeError, NotExistsError, ForbiddenError, MalformedError, \ + AuthenticationError, QueryStoreError, ServiceConnectionError, ServiceError class DatabaseUnitTest(unittest.TestCase): @@ -28,18 +28,7 @@ class DatabaseUnitTest(unittest.TestCase): contact=UserBrief(id='8638c043-5145-4be8-a3e4-4b79991b0a16', username='mweise'), internal_name='test_abcd', is_public=True, - is_schema_public=True, - container=ContainerBrief( - id=1, - name='MariaDB Galera 11.1.3', - internal_name='mariadb', - image=ImageBrief( - id=1, - name='mariadb', - version='11.2.2', - jdbc_method='mariadb' - ) - ) + is_schema_public=True ) ] with requests_mock.Mocker() as mock: @@ -49,6 +38,34 @@ class DatabaseUnitTest(unittest.TestCase): response = RestClient().get_databases() self.assertEqual(exp, response) + def test_get_databases_unknown_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.get('/api/database', status_code=401) + # test + try: + RestClient().get_databases() + except ResponseCodeError: + pass + + def test_get_databases_count_succeeds(self): + with requests_mock.Mocker() as mock: + # mock + mock.head('/api/database', headers={'X-Count': '100'}) + # test + response = RestClient().get_databases_count() + self.assertEqual(100, response) + + def test_get_databases_count_unknown_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.head('/api/database', status_code=401) + # test + try: + RestClient().get_databases_count() + except ResponseCodeError: + pass + def test_get_database_succeeds(self): exp = Database( id=1, @@ -78,7 +95,17 @@ class DatabaseUnitTest(unittest.TestCase): response = RestClient().get_database(1) self.assertEqual(exp, response) - def test_get_database_not_found_fails(self): + def test_get_database_403_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.get('/api/database/1', status_code=403) + # test + try: + response = RestClient().get_database(1) + except ForbiddenError as e: + pass + + def test_get_database_404_fails(self): with requests_mock.Mocker() as mock: # mock mock.get('/api/database/1', status_code=404) @@ -88,16 +115,10 @@ class DatabaseUnitTest(unittest.TestCase): except NotExistsError as e: pass - def test_get_database_invalid_dto_fails(self): - try: - exp = Database() - except ValidationError as e: - pass - - def test_get_database_unauthorized_fails(self): + def test_get_database_unknown_fails(self): with requests_mock.Mocker() as mock: # mock - mock.get('/api/database/1', status_code=401) + mock.get('/api/database/1', status_code=202) # test try: response = RestClient().get_database(1) @@ -131,32 +152,88 @@ class DatabaseUnitTest(unittest.TestCase): mock.post('/api/database', json=exp.model_dump(), status_code=201) # test client = RestClient(username="a", password="b") - response = client.create_database(name='test', container_id=1, is_public=True) + response = RestClient(username="a", password="b").create_database(name='test', container_id=1, + is_public=True) self.assertEqual(response.name, 'test') - def test_create_database_not_allowed_fails(self): + def test_create_database_400_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.post('/api/database', status_code=400) + # test + try: + response = RestClient(username="a", password="b").create_database(name='test', container_id=1, + is_public=True) + except MalformedError as e: + pass + + def test_create_database_403_fails(self): with requests_mock.Mocker() as mock: # mock mock.post('/api/database', status_code=403) # test try: - client = RestClient(username="a", password="b") - response = client.create_database(name='test', container_id=1, is_public=True) + response = RestClient(username="a", password="b").create_database(name='test', container_id=1, + is_public=True) except ForbiddenError as e: pass - def test_create_database_not_found_fails(self): + def test_create_database_404_fails(self): with requests_mock.Mocker() as mock: # mock mock.post('/api/database', status_code=404) # test try: - client = RestClient(username="a", password="b") - response = client.create_database(name='test', container_id=1, is_public=True) + response = RestClient(username="a", password="b").create_database(name='test', container_id=1, + is_public=True) except NotExistsError as e: pass - def test_create_database_not_auth_fails(self): + def test_create_database_409_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.post('/api/database', status_code=409) + # test + try: + response = RestClient(username="a", password="b").create_database(name='test', container_id=1, + is_public=True) + except QueryStoreError as e: + pass + + def test_create_database_502_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.post('/api/database', status_code=502) + # test + try: + response = RestClient(username="a", password="b").create_database(name='test', container_id=1, + is_public=True) + except ServiceConnectionError as e: + pass + + def test_create_database_503_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.post('/api/database', status_code=503) + # test + try: + response = RestClient(username="a", password="b").create_database(name='test', container_id=1, + is_public=True) + except ServiceError as e: + pass + + def test_create_database_unknown_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.post('/api/database', status_code=202) + # test + try: + response = RestClient(username="a", password="b").create_database(name='test', container_id=1, + is_public=True) + except ResponseCodeError as e: + pass + + def test_create_database_anonymous_fails(self): with requests_mock.Mocker() as mock: # mock mock.post('/api/database', status_code=404) @@ -193,41 +270,89 @@ class DatabaseUnitTest(unittest.TestCase): mock.put('/api/database/1/visibility', json=exp.model_dump(), status_code=202) # test client = RestClient(username="a", password="b") - response = client.update_database_visibility(database_id=1, is_public=True, is_schema_public=True) + response = RestClient(username="a", password="b").update_database_visibility(database_id=1, is_public=True, + is_schema_public=True) self.assertEqual(response.is_public, True) - def test_update_database_visibility_not_allowed_fails(self): + def test_update_database_visibility_400_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.put('/api/database/1/visibility', status_code=400) + # test + try: + response = RestClient(username="a", password="b").update_database_visibility(database_id=1, + is_public=True, + is_schema_public=True) + except MalformedError: + pass + + def test_update_database_visibility_403_fails(self): with requests_mock.Mocker() as mock: # mock mock.put('/api/database/1/visibility', status_code=403) # test try: - client = RestClient(username="a", password="b") - response = client.update_database_visibility(database_id=1, is_public=True, is_schema_public=True) + response = RestClient(username="a", password="b").update_database_visibility(database_id=1, + is_public=True, + is_schema_public=True) except ForbiddenError: pass - def test_update_database_visibility_not_found_fails(self): + def test_update_database_visibility_404_fails(self): with requests_mock.Mocker() as mock: # mock mock.put('/api/database/1/visibility', status_code=404) # test try: - client = RestClient(username="a", password="b") - response = client.update_database_visibility(database_id=1, is_public=True, is_schema_public=True) + response = RestClient(username="a", password="b").update_database_visibility(database_id=1, + is_public=True, + is_schema_public=True) except NotExistsError: pass - def test_update_database_visibility_not_auth_fails(self): + def test_update_database_visibility_502_fails(self): with requests_mock.Mocker() as mock: # mock - mock.put('/api/database/1', status_code=404) + mock.put('/api/database/1/visibility', status_code=502) # test try: - response = RestClient().update_database_visibility(database_id=1, is_public=True, is_schema_public=True) - except AuthenticationError: + response = RestClient(username="a", password="b").update_database_visibility(database_id=1, + is_public=True, + is_schema_public=True) + except ServiceConnectionError: + pass + + def test_update_database_visibility_503_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.put('/api/database/1/visibility', status_code=503) + # test + try: + response = RestClient(username="a", password="b").update_database_visibility(database_id=1, + is_public=True, + is_schema_public=True) + except ServiceError: + pass + + def test_update_database_visibility_unknown_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.put('/api/database/1/visibility', status_code=200) + # test + try: + response = RestClient(username="a", password="b").update_database_visibility(database_id=1, + is_public=True, + is_schema_public=True) + except ResponseCodeError: pass + def test_update_database_visibility_anonymous_fails(self): + # test + try: + response = RestClient().update_database_visibility(database_id=1, is_public=True, is_schema_public=True) + except AuthenticationError: + pass + def test_update_database_owner_succeeds(self): exp = Database( id=1, @@ -255,34 +380,77 @@ class DatabaseUnitTest(unittest.TestCase): mock.put('/api/database/1/owner', json=exp.model_dump(), status_code=202) # test client = RestClient(username="a", password="b") - response = client.update_database_owner(database_id=1, user_id='abdbf897-e599-4e5a-a3f0-7529884ea011') + response = RestClient(username="a", password="b").update_database_owner(database_id=1, + user_id='abdbf897-e599-4e5a-a3f0-7529884ea011') self.assertEqual(response.owner.id, 'abdbf897-e599-4e5a-a3f0-7529884ea011') - def test_update_database_owner_not_allowed_fails(self): + def test_update_database_owner_400_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.put('/api/database/1/owner', status_code=400) + # test + try: + response = RestClient(username="a", password="b").update_database_owner(database_id=1, + user_id='abdbf897-e599-4e5a-a3f0-7529884ea011') + except MalformedError: + pass + + def test_update_database_owner_403_fails(self): with requests_mock.Mocker() as mock: # mock mock.put('/api/database/1/owner', status_code=403) # test try: - client = RestClient(username="a", password="b") - response = client.update_database_owner(database_id=1, - user_id='abdbf897-e599-4e5a-a3f0-7529884ea011') + response = RestClient(username="a", password="b").update_database_owner(database_id=1, + user_id='abdbf897-e599-4e5a-a3f0-7529884ea011') except ForbiddenError: pass - def test_update_database_owner_not_found_fails(self): + def test_update_database_owner_404_fails(self): with requests_mock.Mocker() as mock: # mock mock.put('/api/database/1/owner', status_code=404) # test try: - client = RestClient(username="a", password="b") - response = client.update_database_owner(database_id=1, - user_id='abdbf897-e599-4e5a-a3f0-7529884ea011') + response = RestClient(username="a", password="b").update_database_owner(database_id=1, + user_id='abdbf897-e599-4e5a-a3f0-7529884ea011') except NotExistsError: pass - def test_update_database_owner_not_auth_fails(self): + def test_update_database_owner_502_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.put('/api/database/1/owner', status_code=502) + # test + try: + response = RestClient(username="a", password="b").update_database_owner(database_id=1, + user_id='abdbf897-e599-4e5a-a3f0-7529884ea011') + except ServiceConnectionError: + pass + + def test_update_database_owner_503_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.put('/api/database/1/owner', status_code=503) + # test + try: + response = RestClient(username="a", password="b").update_database_owner(database_id=1, + user_id='abdbf897-e599-4e5a-a3f0-7529884ea011') + except ServiceError: + pass + + def test_update_database_owner_unknown_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.put('/api/database/1/owner', status_code=200) + # test + try: + response = RestClient(username="a", password="b").update_database_owner(database_id=1, + user_id='abdbf897-e599-4e5a-a3f0-7529884ea011') + except ResponseCodeError: + pass + + def test_update_database_owner_anonymous_fails(self): with requests_mock.Mocker() as mock: # mock mock.put('/api/database/1/owner', status_code=404) @@ -293,6 +461,157 @@ class DatabaseUnitTest(unittest.TestCase): except AuthenticationError: pass + def test_update_database_schema_succeeds(self): + exp = DatabaseBrief( + id=1, + name='test', + owner_id='8638c043-5145-4be8-a3e4-4b79991b0a16', + contact=UserBrief(id='8638c043-5145-4be8-a3e4-4b79991b0a16', username='mweise'), + internal_name='test_abcd', + is_public=True, + is_schema_public=True + ) + with requests_mock.Mocker() as mock: + # mock + mock.put('/api/database/1/metadata/table', json=exp.model_dump()) + mock.put('/api/database/1/metadata/view', json=exp.model_dump()) + # test + response = RestClient(username='foo', password='bar').update_database_schema(database_id=1) + self.assertEqual(exp, response) + + def test_update_database_schema_400_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.put('/api/database/1/metadata/table', status_code=400) + # test + try: + RestClient(username='foo', password='bar').update_database_schema(database_id=1) + except MalformedError: + pass + + def test_update_database_schema_view_400_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.put('/api/database/1/metadata/table', json={}, status_code=200) + mock.put('/api/database/1/metadata/view', status_code=400) + # test + try: + RestClient(username='foo', password='bar').update_database_schema(database_id=1) + except MalformedError: + pass + + def test_update_database_schema_403_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.put('/api/database/1/metadata/table', status_code=403) + # test + try: + RestClient(username='foo', password='bar').update_database_schema(database_id=1) + except ForbiddenError: + pass + + def test_update_database_schema_view_403_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.put('/api/database/1/metadata/table', json={}, status_code=200) + mock.put('/api/database/1/metadata/view', status_code=403) + # test + try: + RestClient(username='foo', password='bar').update_database_schema(database_id=1) + except ForbiddenError: + pass + + def test_update_database_schema_404_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.put('/api/database/1/metadata/table', status_code=404) + # test + try: + RestClient(username='foo', password='bar').update_database_schema(database_id=1) + except NotExistsError: + pass + + def test_update_database_schema_view_404_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.put('/api/database/1/metadata/table', json={}, status_code=200) + mock.put('/api/database/1/metadata/view', status_code=404) + # test + try: + RestClient(username='foo', password='bar').update_database_schema(database_id=1) + except NotExistsError: + pass + + def test_update_database_schema_502_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.put('/api/database/1/metadata/table', status_code=502) + # test + try: + RestClient(username='foo', password='bar').update_database_schema(database_id=1) + except ServiceConnectionError: + pass + + def test_update_database_schema_view_502_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.put('/api/database/1/metadata/table', json={}, status_code=200) + mock.put('/api/database/1/metadata/view', status_code=502) + # test + try: + RestClient(username='foo', password='bar').update_database_schema(database_id=1) + except ServiceConnectionError: + pass + + def test_update_database_schema_503_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.put('/api/database/1/metadata/table', status_code=503) + # test + try: + RestClient(username='foo', password='bar').update_database_schema(database_id=1) + except ServiceError: + pass + + def test_update_database_schema_view_503_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.put('/api/database/1/metadata/table', json={}, status_code=200) + mock.put('/api/database/1/metadata/view', status_code=503) + # test + try: + RestClient(username='foo', password='bar').update_database_schema(database_id=1) + except ServiceError: + pass + + def test_update_database_schema_unknown_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.put('/api/database/1/metadata/table', status_code=202) + # test + try: + RestClient(username='foo', password='bar').update_database_schema(database_id=1) + except ResponseCodeError: + pass + + def test_update_database_schema_view_unknown_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.put('/api/database/1/metadata/table', json={}, status_code=200) + mock.put('/api/database/1/metadata/view', status_code=202) + # test + try: + RestClient(username='foo', password='bar').update_database_schema(database_id=1) + except ResponseCodeError: + pass + + def test_update_database_schema_anonymous_fails(self): + # test + try: + RestClient().update_database_schema(database_id=1) + except AuthenticationError: + pass + def test_get_database_access_succeeds(self): exp = DatabaseAccess(type=AccessType.READ, user=UserBrief(id='abdbf897-e599-4e5a-a3f0-7529884ea011', username='other')) @@ -303,7 +622,7 @@ class DatabaseUnitTest(unittest.TestCase): response = RestClient().get_database_access(database_id=1) self.assertEqual(response, AccessType.READ) - def test_get_database_access_not_allowed_fails(self): + def test_get_database_access_403_fails(self): with requests_mock.Mocker() as mock: # mock mock.get('/api/database/1/access', status_code=403) @@ -313,7 +632,7 @@ class DatabaseUnitTest(unittest.TestCase): except ForbiddenError: pass - def test_get_database_access_not_found_fails(self): + def test_get_database_access_404_fails(self): with requests_mock.Mocker() as mock: # mock mock.get('/api/database/1/access', status_code=404) @@ -331,24 +650,24 @@ class DatabaseUnitTest(unittest.TestCase): mock.post('/api/database/1/access/abdbf897-e599-4e5a-a3f0-7529884ea011', json=exp.model_dump(), status_code=202) # test - client = RestClient(username="a", password="b") - response = client.create_database_access(database_id=1, type=AccessType.READ, - user_id='abdbf897-e599-4e5a-a3f0-7529884ea011') + response = RestClient(username="a", password="b").create_database_access(database_id=1, + type=AccessType.READ, + user_id='abdbf897-e599-4e5a-a3f0-7529884ea011') self.assertEqual(response, exp.type) - def test_create_database_access_malformed_fails(self): + def test_create_database_access_400_fails(self): with requests_mock.Mocker() as mock: # mock mock.post('/api/database/1/access/abdbf897-e599-4e5a-a3f0-7529884ea011', status_code=400) # test try: - client = RestClient(username="a", password="b") - response = client.create_database_access(database_id=1, type=AccessType.READ, - user_id='abdbf897-e599-4e5a-a3f0-7529884ea011') + response = RestClient(username="a", password="b").create_database_access(database_id=1, + type=AccessType.READ, + user_id='abdbf897-e599-4e5a-a3f0-7529884ea011') except MalformedError: pass - def test_create_database_access_not_auth_fails(self): + def test_create_database_access_anonymous_fails(self): with requests_mock.Mocker() as mock: # mock mock.post('/api/database/1/access/abdbf897-e599-4e5a-a3f0-7529884ea011', status_code=400) @@ -359,27 +678,27 @@ class DatabaseUnitTest(unittest.TestCase): except AuthenticationError: pass - def test_create_database_access_not_allowed_fails(self): + def test_create_database_access_403_fails(self): with requests_mock.Mocker() as mock: # mock mock.post('/api/database/1/access/abdbf897-e599-4e5a-a3f0-7529884ea011', status_code=403) # test try: - client = RestClient(username="a", password="b") - response = client.create_database_access(database_id=1, type=AccessType.READ, - user_id='abdbf897-e599-4e5a-a3f0-7529884ea011') + response = RestClient(username="a", password="b").create_database_access(database_id=1, + type=AccessType.READ, + user_id='abdbf897-e599-4e5a-a3f0-7529884ea011') except ForbiddenError: pass - def test_create_database_access_not_found_fails(self): + def test_create_database_access_404_fails(self): with requests_mock.Mocker() as mock: # mock mock.post('/api/database/1/access/abdbf897-e599-4e5a-a3f0-7529884ea011', status_code=404) # test try: - client = RestClient(username="a", password="b") - response = client.create_database_access(database_id=1, type=AccessType.READ, - user_id='abdbf897-e599-4e5a-a3f0-7529884ea011') + response = RestClient(username="a", password="b").create_database_access(database_id=1, + type=AccessType.READ, + user_id='abdbf897-e599-4e5a-a3f0-7529884ea011') except NotExistsError: pass @@ -391,48 +710,48 @@ class DatabaseUnitTest(unittest.TestCase): mock.put('/api/database/1/access/abdbf897-e599-4e5a-a3f0-7529884ea011', json=exp.model_dump(), status_code=202) # test - client = RestClient(username="a", password="b") - response = client.update_database_access(database_id=1, type=AccessType.READ, - user_id='abdbf897-e599-4e5a-a3f0-7529884ea011') + response = RestClient(username="a", password="b").update_database_access(database_id=1, + type=AccessType.READ, + user_id='abdbf897-e599-4e5a-a3f0-7529884ea011') self.assertEqual(response, exp.type) - def test_update_database_access_malformed_fails(self): + def test_update_database_access_400_fails(self): with requests_mock.Mocker() as mock: # mock mock.put('/api/database/1/access/abdbf897-e599-4e5a-a3f0-7529884ea011', status_code=400) # test try: - client = RestClient(username="a", password="b") - response = client.update_database_access(database_id=1, type=AccessType.READ, - user_id='abdbf897-e599-4e5a-a3f0-7529884ea011') + response = RestClient(username="a", password="b").update_database_access(database_id=1, + type=AccessType.READ, + user_id='abdbf897-e599-4e5a-a3f0-7529884ea011') except MalformedError: pass - def test_update_database_access_not_allowed_fails(self): + def test_update_database_access_403_fails(self): with requests_mock.Mocker() as mock: # mock mock.put('/api/database/1/access/abdbf897-e599-4e5a-a3f0-7529884ea011', status_code=403) # test try: - client = RestClient(username="a", password="b") - response = client.update_database_access(database_id=1, type=AccessType.READ, - user_id='abdbf897-e599-4e5a-a3f0-7529884ea011') + response = RestClient(username="a", password="b").update_database_access(database_id=1, + type=AccessType.READ, + user_id='abdbf897-e599-4e5a-a3f0-7529884ea011') except ForbiddenError: pass - def test_update_database_access_not_found_fails(self): + def test_update_database_access_404_fails(self): with requests_mock.Mocker() as mock: # mock mock.put('/api/database/1/access/abdbf897-e599-4e5a-a3f0-7529884ea011', status_code=404) # test try: - client = RestClient(username="a", password="b") - response = client.update_database_access(database_id=1, type=AccessType.READ, - user_id='abdbf897-e599-4e5a-a3f0-7529884ea011') + response = RestClient(username="a", password="b").update_database_access(database_id=1, + type=AccessType.READ, + user_id='abdbf897-e599-4e5a-a3f0-7529884ea011') except NotExistsError: pass - def test_update_database_access_not_auth_fails(self): + def test_update_database_access_anonymous_fails(self): with requests_mock.Mocker() as mock: # mock mock.put('/api/database/1/access/abdbf897-e599-4e5a-a3f0-7529884ea011', status_code=404) @@ -449,42 +768,43 @@ class DatabaseUnitTest(unittest.TestCase): mock.delete('/api/database/1/access/abdbf897-e599-4e5a-a3f0-7529884ea011', status_code=202) # test client = RestClient(username="a", password="b") - client.delete_database_access(database_id=1, user_id='abdbf897-e599-4e5a-a3f0-7529884ea011') + RestClient(username="a", password="b").delete_database_access(database_id=1, + user_id='abdbf897-e599-4e5a-a3f0-7529884ea011') - def test_delete_database_access_malformed_fails(self): + def test_delete_database_access_400_fails(self): with requests_mock.Mocker() as mock: # mock mock.delete('/api/database/1/access/abdbf897-e599-4e5a-a3f0-7529884ea011', status_code=400) # test try: - client = RestClient(username="a", password="b") - client.delete_database_access(database_id=1, user_id='abdbf897-e599-4e5a-a3f0-7529884ea011') + RestClient(username="a", password="b").delete_database_access(database_id=1, + user_id='abdbf897-e599-4e5a-a3f0-7529884ea011') except MalformedError: pass - def test_delete_database_access_not_allowed_fails(self): + def test_delete_database_access_403_fails(self): with requests_mock.Mocker() as mock: # mock mock.delete('/api/database/1/access/abdbf897-e599-4e5a-a3f0-7529884ea011', status_code=403) # test try: - client = RestClient(username="a", password="b") - client.delete_database_access(database_id=1, user_id='abdbf897-e599-4e5a-a3f0-7529884ea011') + RestClient(username="a", password="b").delete_database_access(database_id=1, + user_id='abdbf897-e599-4e5a-a3f0-7529884ea011') except ForbiddenError: pass - def test_delete_database_access_not_found_fails(self): + def test_delete_database_access_404_fails(self): with requests_mock.Mocker() as mock: # mock mock.delete('/api/database/1/access/abdbf897-e599-4e5a-a3f0-7529884ea011', status_code=404) # test try: - client = RestClient(username="a", password="b") - client.delete_database_access(database_id=1, user_id='abdbf897-e599-4e5a-a3f0-7529884ea011') + RestClient(username="a", password="b").delete_database_access(database_id=1, + user_id='abdbf897-e599-4e5a-a3f0-7529884ea011') except NotExistsError: pass - def test_delete_database_access_not_auth_fails(self): + def test_delete_database_access_anonymous_fails(self): with requests_mock.Mocker() as mock: # mock mock.delete('/api/database/1/access/abdbf897-e599-4e5a-a3f0-7529884ea011', status_code=404) diff --git a/lib/python/tests/test_unit_identifier.py b/lib/python/tests/test_unit_identifier.py index 8726d05ef23108c2a0a8fb9e8c4cd65052e19b1e..137f26aab22ca87c04aca8bb2d58d3293b67351b 100644 --- a/lib/python/tests/test_unit_identifier.py +++ b/lib/python/tests/test_unit_identifier.py @@ -47,7 +47,7 @@ class IdentifierUnitTest(unittest.TestCase): creators=[CreateIdentifierCreator(creator_name='Carberry, Josiah')]) self.assertEqual(exp, response) - def test_create_identifier_malformed_fails(self): + def test_create_identifier_400_fails(self): with requests_mock.Mocker() as mock: # mock mock.post('/api/identifier', status_code=400) @@ -63,7 +63,7 @@ class IdentifierUnitTest(unittest.TestCase): except MalformedError: pass - def test_create_identifier_not_allowed_fails(self): + def test_create_identifier_403_fails(self): with requests_mock.Mocker() as mock: # mock mock.post('/api/identifier', status_code=403) @@ -79,7 +79,7 @@ class IdentifierUnitTest(unittest.TestCase): except ForbiddenError: pass - def test_create_identifier_not_found_fails(self): + def test_create_identifier_404_fails(self): with requests_mock.Mocker() as mock: # mock mock.post('/api/identifier', status_code=404) @@ -95,7 +95,7 @@ class IdentifierUnitTest(unittest.TestCase): except NotExistsError: pass - def test_create_identifier_not_auth_fails(self): + def test_create_identifier_anonymous_fails(self): with requests_mock.Mocker() as mock: # mock mock.post('/api/identifier', status_code=503) diff --git a/lib/python/tests/test_unit_query.py b/lib/python/tests/test_unit_query.py index 6dff04867e33062f09ea151b8651f4362ce06bf8..31b4182a4b0fec18e22ac70cf3192e6ca8596793 100644 --- a/lib/python/tests/test_unit_query.py +++ b/lib/python/tests/test_unit_query.py @@ -25,7 +25,7 @@ class QueryUnitTest(unittest.TestCase): query="SELECT id, username FROM some_table WHERE id IN (1,2)") self.assertTrue(DataFrame.equals(df, response)) - def test_create_subset_malformed_fails(self): + def test_create_subset_400_fails(self): with requests_mock.Mocker() as mock: # mock mock.post('/api/database/1/subset', status_code=400) @@ -37,7 +37,7 @@ class QueryUnitTest(unittest.TestCase): except MalformedError: pass - def test_create_subset_not_allowed_fails(self): + def test_create_subset_403_fails(self): with requests_mock.Mocker() as mock: # mock mock.post('/api/database/1/subset', status_code=403) @@ -49,7 +49,7 @@ class QueryUnitTest(unittest.TestCase): except ForbiddenError: pass - def test_create_subset_not_found_fails(self): + def test_create_subset_404_fails(self): with requests_mock.Mocker() as mock: # mock mock.post('/api/database/1/subset', status_code=404) @@ -61,7 +61,7 @@ class QueryUnitTest(unittest.TestCase): except NotExistsError: pass - def test_create_subset_not_auth_succeeds(self): + def test_create_subset_anonymous_succeeds(self): with requests_mock.Mocker() as mock: exp = [{'id': 1, 'username': 'foo'}, {'id': 2, 'username': 'bar'}] df = DataFrame.from_records(json.dumps(exp)) @@ -94,7 +94,7 @@ class QueryUnitTest(unittest.TestCase): response = RestClient().get_subset(database_id=1, subset_id=6) self.assertEqual(exp, response) - def test_find_query_not_allowed_fails(self): + def test_find_query_403_fails(self): with requests_mock.Mocker() as mock: # mock mock.get('/api/database/1/subset/6', status_code=403) @@ -104,7 +104,7 @@ class QueryUnitTest(unittest.TestCase): except ForbiddenError: pass - def test_find_query_not_found_fails(self): + def test_find_query_404_fails(self): with requests_mock.Mocker() as mock: # mock mock.get('/api/database/1/subset/6', status_code=404) @@ -143,7 +143,7 @@ class QueryUnitTest(unittest.TestCase): response = RestClient().get_queries(database_id=1) self.assertEqual(exp, response) - def test_get_queries_not_allowed_fails(self): + def test_get_queries_403_fails(self): with requests_mock.Mocker() as mock: # mock mock.get('/api/database/1/subset', status_code=403) @@ -153,7 +153,7 @@ class QueryUnitTest(unittest.TestCase): except ForbiddenError: pass - def test_get_queries_not_found_fails(self): + def test_get_queries_404_fails(self): with requests_mock.Mocker() as mock: # mock mock.get('/api/database/1/subset', status_code=404) @@ -184,7 +184,7 @@ class QueryUnitTest(unittest.TestCase): self.assertEqual(df.shape, response.shape) self.assertTrue(DataFrame.equals(df, response)) - def test_get_subset_data_not_allowed_fails(self): + def test_get_subset_data_403_fails(self): with requests_mock.Mocker() as mock: # mock mock.get('/api/database/1/subset/6/data', status_code=403) @@ -194,7 +194,7 @@ class QueryUnitTest(unittest.TestCase): except ForbiddenError: pass - def test_get_subset_data_not_found_fails(self): + def test_get_subset_data_404_fails(self): with requests_mock.Mocker() as mock: # mock mock.get('/api/database/1/subset/6/data', status_code=404) @@ -213,7 +213,7 @@ class QueryUnitTest(unittest.TestCase): response = RestClient().get_subset_data_count(database_id=1, subset_id=6) self.assertEqual(exp, response) - def test_get_subset_data_count_not_allowed_fails(self): + def test_get_subset_data_count_403_fails(self): with requests_mock.Mocker() as mock: # mock mock.head('/api/database/1/subset/6/data', status_code=403) @@ -223,7 +223,7 @@ class QueryUnitTest(unittest.TestCase): except ForbiddenError: pass - def test_get_subset_data_count_not_found_fails(self): + def test_get_subset_data_count_404_fails(self): with requests_mock.Mocker() as mock: # mock mock.head('/api/database/1/subset/6/data', status_code=404) diff --git a/lib/python/tests/test_unit_rest_client.py b/lib/python/tests/test_unit_rest_client.py index 61a9432ce41cb36a0ea2f4295c7d6136e3a7a696..0c52cfd33ec211aa130d9771e97fc2e718253adf 100644 --- a/lib/python/tests/test_unit_rest_client.py +++ b/lib/python/tests/test_unit_rest_client.py @@ -1,99 +1,26 @@ import os import unittest -import requests_mock - from dbrepo.RestClient import RestClient -from dbrepo.api.dto import JwtAuth -from dbrepo.api.exceptions import MalformedError, ServiceConnectionError, ServiceError, ForbiddenError, \ - AuthenticationError, ResponseCodeError class RestClientUnitTest(unittest.TestCase): def test_constructor_succeeds(self): - with requests_mock.Mocker() as mock: - # test - os.environ['REST_API_SECURE'] = 'True' - response = RestClient() - self.assertTrue(response.secure) - - def test_get_jwt_auth_empty_succeeds(self): - with requests_mock.Mocker() as mock: - exp = JwtAuth(access_token='ey123', - refresh_token='ey456', - id_token='ey789', - expires_in=3600, - scope='scope', - token_type='Bearer', - not_before_policy=0, - session_state='session_state', - refresh_expires_in=7200) - # mock - mock.post('/api/user/token', json=exp.model_dump(), status_code=202) - # test - response = RestClient().get_jwt_auth() - self.assertEqual(exp, response) - - def test_get_jwt_auth_400_fails(self): - with requests_mock.Mocker() as mock: - # mock - mock.post('/api/user/token', status_code=400) - # test - try: - response = RestClient().get_jwt_auth() - except MalformedError: - pass - - def test_get_jwt_auth_403_fails(self): - with requests_mock.Mocker() as mock: - # mock - mock.post('/api/user/token', status_code=403) - # test - try: - response = RestClient().get_jwt_auth() - except ForbiddenError: - pass - - def test_get_jwt_auth_428_fails(self): - with requests_mock.Mocker() as mock: - # mock - mock.post('/api/user/token', status_code=428) - # test - try: - response = RestClient().get_jwt_auth() - except AuthenticationError: - pass - - def test_get_jwt_auth_502_fails(self): - with requests_mock.Mocker() as mock: - # mock - mock.post('/api/user/token', status_code=502) - # test - try: - response = RestClient().get_jwt_auth() - except ServiceConnectionError: - pass - - def test_get_jwt_auth_503_fails(self): - with requests_mock.Mocker() as mock: - # mock - mock.post('/api/user/token', status_code=503) - # test - try: - response = RestClient().get_jwt_auth() - except ServiceError: - pass - - def test_get_jwt_auth_unknown_fails(self): - with requests_mock.Mocker() as mock: - # mock - mock.post('/api/user/token', status_code=418) - # test - try: - response = RestClient().get_jwt_auth() - except ResponseCodeError: - pass + # test + os.environ['REST_API_SECURE'] = 'True' + response = RestClient() + self.assertTrue(response.secure) + + def test_whoami_anonymous_succeeds(self): + # test + response = RestClient().whoami() + self.assertIsNone(response) + + def test_whoami_succeeds(self): + # test + response = RestClient(username="foobar").whoami() + self.assertEqual("foobar", response) if __name__ == "__main__": diff --git a/lib/python/tests/test_unit_table.py b/lib/python/tests/test_unit_table.py index eccf8c2a59ee45c3341c4116678301e438f0089b..998a1bc4f0ae96b8517eac4c90b5a7c50c008444 100644 --- a/lib/python/tests/test_unit_table.py +++ b/lib/python/tests/test_unit_table.py @@ -1,3 +1,4 @@ +import datetime import json import unittest @@ -6,9 +7,9 @@ from pandas import DataFrame from dbrepo.RestClient import RestClient from dbrepo.api.dto import Table, CreateTableConstraints, Column, Constraints, ColumnType, ConceptBrief, UnitBrief, \ - TableStatistics, ColumnStatistic, PrimaryKey, ColumnBrief, TableBrief, UserBrief + TableStatistics, ColumnStatistic, PrimaryKey, ColumnBrief, TableBrief, UserBrief, History, HistoryEventType from dbrepo.api.exceptions import MalformedError, ForbiddenError, NotExistsError, NameExistsError, \ - AuthenticationError, ExternalSystemError + AuthenticationError, ExternalSystemError, ServiceError, ServiceConnectionError, ResponseCodeError class TableUnitTest(unittest.TestCase): @@ -33,7 +34,7 @@ class TableUnitTest(unittest.TestCase): constraints=CreateTableConstraints()) self.assertEqual(exp, response) - def test_create_table_malformed_fails(self): + def test_create_table_400_fails(self): with requests_mock.Mocker() as mock: # mock mock.post('/api/database/1/table', status_code=400) @@ -46,20 +47,20 @@ class TableUnitTest(unittest.TestCase): except MalformedError: pass - def test_create_table_not_allowed_fails(self): + def test_create_table_403_fails(self): with requests_mock.Mocker() as mock: # mock mock.post('/api/database/1/table', status_code=403) # test try: - client = RestClient(username="a", password="b") - response = client.create_table(database_id=1, name="Test", description="Test Table", columns=[], - is_public=True, is_schema_public=True, - constraints=CreateTableConstraints()) + RestClient(username="a", password="b").create_table(database_id=1, name="Test", + description="Test Table", columns=[], + is_public=True, is_schema_public=True, + constraints=CreateTableConstraints()) except ForbiddenError: pass - def test_create_table_not_allowed_fails(self): + def test_create_table_404_fails(self): with requests_mock.Mocker() as mock: # mock mock.post('/api/database/1/table', status_code=404) @@ -72,7 +73,7 @@ class TableUnitTest(unittest.TestCase): except NotExistsError: pass - def test_create_table_name_exists_fails(self): + def test_create_table_409_fails(self): with requests_mock.Mocker() as mock: # mock mock.post('/api/database/1/table', status_code=409) @@ -85,7 +86,46 @@ class TableUnitTest(unittest.TestCase): except NameExistsError: pass - def test_create_table_not_auth_fails(self): + def test_create_table_502_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.post('/api/database/1/table', status_code=502) + # test + try: + client = RestClient(username="a", password="b") + response = client.create_table(database_id=1, name="Test", description="Test Table", columns=[], + is_public=True, is_schema_public=True, + constraints=CreateTableConstraints()) + except ServiceConnectionError: + pass + + def test_create_table_503_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.post('/api/database/1/table', status_code=503) + # test + try: + client = RestClient(username="a", password="b") + response = client.create_table(database_id=1, name="Test", description="Test Table", columns=[], + is_public=True, is_schema_public=True, + constraints=CreateTableConstraints()) + except ServiceError: + pass + + def test_create_table_unknown_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.post('/api/database/1/table', status_code=200) + # test + try: + client = RestClient(username="a", password="b") + response = client.create_table(database_id=1, name="Test", description="Test Table", columns=[], + is_public=True, is_schema_public=True, + constraints=CreateTableConstraints()) + except ResponseCodeError: + pass + + def test_create_table_anonymous_fails(self): with requests_mock.Mocker() as mock: # mock mock.post('/api/database/1/table', status_code=409) @@ -122,6 +162,36 @@ class TableUnitTest(unittest.TestCase): response = RestClient().get_tables(database_id=1) self.assertEqual(exp, response) + def test_get_tables_403_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.get('/api/database/1/table', status_code=403) + # test + try: + RestClient().get_tables(database_id=1) + except ForbiddenError: + pass + + def test_get_tables_404_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.get('/api/database/1/table', status_code=404) + # test + try: + RestClient().get_tables(database_id=1) + except NotExistsError: + pass + + def test_get_tables_unknown_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.get('/api/database/1/table', status_code=202) + # test + try: + RestClient().get_tables(database_id=1) + except ResponseCodeError: + pass + def test_get_table_succeeds(self): with requests_mock.Mocker() as mock: exp = Table(id=2, @@ -159,10 +229,7 @@ class TableUnitTest(unittest.TestCase): database_id=1, table_id=2, internal_name="id", - auto_generated=True, - is_primary_key=True, type=ColumnType.BIGINT, - is_public=True, is_null_allowed=False)]) # mock mock.get('/api/database/1/table/2', json=exp.model_dump()) @@ -170,7 +237,7 @@ class TableUnitTest(unittest.TestCase): response = RestClient().get_table(database_id=1, table_id=2) self.assertEqual(exp, response) - def test_get_table_not_allowed_fails(self): + def test_get_table_403_fails(self): with requests_mock.Mocker() as mock: # mock mock.get('/api/database/1/table/2', status_code=403) @@ -180,7 +247,7 @@ class TableUnitTest(unittest.TestCase): except ForbiddenError: pass - def test_get_table_not_found_fails(self): + def test_get_table_404_fails(self): with requests_mock.Mocker() as mock: # mock mock.get('/api/database/1/table/2', status_code=404) @@ -190,6 +257,16 @@ class TableUnitTest(unittest.TestCase): except NotExistsError: pass + def test_get_table_unknown_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.get('/api/database/1/table/2', status_code=202) + # test + try: + response = RestClient().get_table(database_id=1, table_id=2) + except ResponseCodeError: + pass + def test_delete_table_succeeds(self): with requests_mock.Mocker() as mock: # mock @@ -198,7 +275,18 @@ class TableUnitTest(unittest.TestCase): client = RestClient(username="a", password="b") client.delete_table(database_id=1, table_id=2) - def test_delete_table_not_allowed_fails(self): + def test_delete_table_400_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.delete('/api/database/1/table/2', status_code=400) + # test + try: + client = RestClient(username="a", password="b") + client.delete_table(database_id=1, table_id=2) + except MalformedError: + pass + + def test_delete_table_403_fails(self): with requests_mock.Mocker() as mock: # mock mock.delete('/api/database/1/table/2', status_code=403) @@ -209,7 +297,7 @@ class TableUnitTest(unittest.TestCase): except ForbiddenError: pass - def test_delete_table_not_found_fails(self): + def test_delete_table_404_fails(self): with requests_mock.Mocker() as mock: # mock mock.delete('/api/database/1/table/2', status_code=404) @@ -220,7 +308,40 @@ class TableUnitTest(unittest.TestCase): except NotExistsError: pass - def test_delete_table_not_auth_fails(self): + def test_delete_table_502_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.delete('/api/database/1/table/2', status_code=502) + # test + try: + client = RestClient(username="a", password="b") + client.delete_table(database_id=1, table_id=2) + except ServiceConnectionError: + pass + + def test_delete_table_503_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.delete('/api/database/1/table/2', status_code=503) + # test + try: + client = RestClient(username="a", password="b") + client.delete_table(database_id=1, table_id=2) + except ServiceError: + pass + + def test_delete_table_unknown_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.delete('/api/database/1/table/2', status_code=200) + # test + try: + client = RestClient(username="a", password="b") + client.delete_table(database_id=1, table_id=2) + except ResponseCodeError: + pass + + def test_delete_table_anonymous_fails(self): with requests_mock.Mocker() as mock: # mock mock.delete('/api/database/1/table/2', status_code=404) @@ -230,6 +351,67 @@ class TableUnitTest(unittest.TestCase): except AuthenticationError: pass + def test_get_table_history_succeeds(self): + with requests_mock.Mocker() as mock: + exp = [History(event=HistoryEventType.INSERT, + total=2, + timestamp=datetime.datetime(2024, 1, 1, 0, 0, 0, 0, datetime.timezone.utc))] + # mock + mock.get('/api/database/1/table/9/history?size=100', json=[exp[0].model_dump()]) + # test + response = RestClient().get_table_history(database_id=1, table_id=9) + self.assertEqual(exp, response) + + def test_get_table_history_400_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.get('/api/database/1/table/9/history?size=100', status_code=400) + # test + try: + RestClient().get_table_history(database_id=1, table_id=9) + except MalformedError: + pass + + def test_get_table_history_403_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.get('/api/database/1/table/9/history?size=100', status_code=403) + # test + try: + RestClient().get_table_history(database_id=1, table_id=9) + except ForbiddenError: + pass + + def test_get_table_history_404_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.get('/api/database/1/table/9/history?size=100', status_code=404) + # test + try: + RestClient().get_table_history(database_id=1, table_id=9) + except NotExistsError: + pass + + def test_get_table_history_503_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.get('/api/database/1/table/9/history?size=100', status_code=503) + # test + try: + RestClient().get_table_history(database_id=1, table_id=9) + except ServiceError: + pass + + def test_get_table_history_unknown_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.get('/api/database/1/table/9/history?size=100', status_code=202) + # test + try: + RestClient().get_table_history(database_id=1, table_id=9) + except ResponseCodeError: + pass + def test_get_table_data_succeeds(self): with requests_mock.Mocker() as mock: exp = [{'id': 1, 'username': 'foo'}, {'id': 2, 'username': 'bar'}] @@ -237,7 +419,9 @@ class TableUnitTest(unittest.TestCase): # mock mock.get('/api/database/1/table/9/data', json=json.dumps(exp)) # test - response = RestClient().get_table_data(database_id=1, table_id=9) + response = RestClient().get_table_data(database_id=1, table_id=9, + timestamp=datetime.datetime(2024, 1, 1, 0, 0, 0, 0, + datetime.timezone.utc)) self.assertTrue(DataFrame.equals(df, response)) def test_get_table_data_dataframe_succeeds(self): @@ -251,7 +435,7 @@ class TableUnitTest(unittest.TestCase): self.assertEqual(df.shape, response.shape) self.assertTrue(DataFrame.equals(df, response)) - def test_get_table_data_malformed_fails(self): + def test_get_table_data_400_fails(self): with requests_mock.Mocker() as mock: # mock mock.get('/api/database/1/table/9/data', status_code=400) @@ -261,7 +445,7 @@ class TableUnitTest(unittest.TestCase): except MalformedError: pass - def test_get_table_data_not_allowed_fails(self): + def test_get_table_data_403_fails(self): with requests_mock.Mocker() as mock: # mock mock.get('/api/database/1/table/9/data', status_code=403) @@ -271,7 +455,7 @@ class TableUnitTest(unittest.TestCase): except ForbiddenError: pass - def test_get_table_data_not_found_fails(self): + def test_get_table_data_404_fails(self): with requests_mock.Mocker() as mock: # mock mock.get('/api/database/1/table/9/data', status_code=404) @@ -281,6 +465,26 @@ class TableUnitTest(unittest.TestCase): except NotExistsError: pass + def test_get_table_data_503_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.get('/api/database/1/table/9/data', status_code=503) + # test + try: + response = RestClient().get_table_data(database_id=1, table_id=9) + except ServiceError: + pass + + def test_get_table_data_unknown_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.get('/api/database/1/table/9/data', status_code=202) + # test + try: + response = RestClient().get_table_data(database_id=1, table_id=9) + except ResponseCodeError: + pass + def test_get_table_data_count_succeeds(self): with requests_mock.Mocker() as mock: exp = 2 @@ -290,7 +494,7 @@ class TableUnitTest(unittest.TestCase): response = RestClient().get_table_data_count(database_id=1, table_id=9) self.assertEqual(exp, response) - def test_get_table_data_count_malformed_fails(self): + def test_get_table_data_count_400_fails(self): with requests_mock.Mocker() as mock: # mock mock.head('/api/database/1/table/9/data', status_code=400) @@ -300,7 +504,7 @@ class TableUnitTest(unittest.TestCase): except MalformedError: pass - def test_get_table_data_count_not_allowed_fails(self): + def test_get_table_data_count_403_fails(self): with requests_mock.Mocker() as mock: # mock mock.head('/api/database/1/table/9/data', status_code=403) @@ -310,7 +514,7 @@ class TableUnitTest(unittest.TestCase): except ForbiddenError: pass - def test_get_table_data_count_not_found_fails(self): + def test_get_table_data_count_404_fails(self): with requests_mock.Mocker() as mock: # mock mock.head('/api/database/1/table/9/data', status_code=404) @@ -339,7 +543,7 @@ class TableUnitTest(unittest.TestCase): client.create_table_data(database_id=1, table_id=9, data={'name': 'Josiah', 'age': 45, 'gender': 'male'}) - def test_create_table_data_malformed_fails(self): + def test_create_table_data_400_fails(self): with requests_mock.Mocker() as mock: # mock mock.post('/api/database/1/table/9/data', status_code=400) @@ -351,7 +555,7 @@ class TableUnitTest(unittest.TestCase): except MalformedError: pass - def test_create_table_data_not_allowed_fails(self): + def test_create_table_data_403_fails(self): with requests_mock.Mocker() as mock: # mock mock.post('/api/database/1/table/9/data', status_code=403) @@ -363,7 +567,7 @@ class TableUnitTest(unittest.TestCase): except ForbiddenError: pass - def test_create_table_data_not_found_fails(self): + def test_create_table_data_404_fails(self): with requests_mock.Mocker() as mock: # mock mock.post('/api/database/1/table/9/data', status_code=404) @@ -375,6 +579,41 @@ class TableUnitTest(unittest.TestCase): except NotExistsError: pass + def test_create_table_data_503_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.post('/api/database/1/table/9/data', status_code=503) + # test + try: + client = RestClient(username="a", password="b") + client.create_table_data(database_id=1, table_id=9, + data={'name': 'Josiah', 'age': 45, 'gender': 'male'}) + except ServiceError: + pass + + def test_create_table_data_unknown_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.post('/api/database/1/table/9/data', status_code=200) + # test + try: + client = RestClient(username="a", password="b") + client.create_table_data(database_id=1, table_id=9, + data={'name': 'Josiah', 'age': 45, 'gender': 'male'}) + except ResponseCodeError: + pass + + def test_create_table_data_anonymous_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.post('/api/database/1/table/9/data', status_code=503) + # test + try: + RestClient().create_table_data(database_id=1, table_id=9, + data={'name': 'Josiah', 'age': 45, 'gender': 'male'}) + except AuthenticationError: + pass + def test_update_table_data_succeeds(self): with requests_mock.Mocker() as mock: # mock @@ -385,7 +624,7 @@ class TableUnitTest(unittest.TestCase): data={'name': 'Josiah', 'age': 45, 'gender': 'male'}, keys={'id': 1}) - def test_update_table_data_malformed_fails(self): + def test_update_table_data_400_fails(self): with requests_mock.Mocker() as mock: # mock mock.put('/api/database/1/table/9/data', status_code=400) @@ -398,7 +637,7 @@ class TableUnitTest(unittest.TestCase): except MalformedError: pass - def test_update_table_data_not_allowed_fails(self): + def test_update_table_data_403_fails(self): with requests_mock.Mocker() as mock: # mock mock.put('/api/database/1/table/9/data', status_code=403) @@ -411,7 +650,7 @@ class TableUnitTest(unittest.TestCase): except ForbiddenError: pass - def test_update_table_data_not_found_fails(self): + def test_update_table_data_404_fails(self): with requests_mock.Mocker() as mock: # mock mock.put('/api/database/1/table/9/data', status_code=404) @@ -424,6 +663,32 @@ class TableUnitTest(unittest.TestCase): except NotExistsError: pass + def test_update_table_data_503_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.put('/api/database/1/table/9/data', status_code=503) + # test + try: + client = RestClient(username="a", password="b") + client.update_table_data(database_id=1, table_id=9, + data={'name': 'Josiah', 'age': 45, 'gender': 'male'}, + keys={'id': 1}) + except ServiceError: + pass + + def test_update_table_data_unknown_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.put('/api/database/1/table/9/data', status_code=200) + # test + try: + client = RestClient(username="a", password="b") + client.update_table_data(database_id=1, table_id=9, + data={'name': 'Josiah', 'age': 45, 'gender': 'male'}, + keys={'id': 1}) + except ResponseCodeError: + pass + def test_delete_table_data_succeeds(self): with requests_mock.Mocker() as mock: # mock @@ -432,7 +697,7 @@ class TableUnitTest(unittest.TestCase): client = RestClient(username="a", password="b") client.delete_table_data(database_id=1, table_id=9, keys={'id': 1}) - def test_delete_table_data_malformed_fails(self): + def test_delete_table_data_400_fails(self): with requests_mock.Mocker() as mock: # mock mock.delete('/api/database/1/table/9/data', status_code=400) @@ -443,7 +708,7 @@ class TableUnitTest(unittest.TestCase): except MalformedError: pass - def test_delete_table_data_not_allowed_fails(self): + def test_delete_table_data_403_fails(self): with requests_mock.Mocker() as mock: # mock mock.delete('/api/database/1/table/9/data', status_code=403) @@ -454,7 +719,7 @@ class TableUnitTest(unittest.TestCase): except ForbiddenError: pass - def test_delete_table_data_not_found_fails(self): + def test_delete_table_data_404_fails(self): with requests_mock.Mocker() as mock: # mock mock.delete('/api/database/1/table/9/data', status_code=404) @@ -465,7 +730,29 @@ class TableUnitTest(unittest.TestCase): except NotExistsError: pass - def test_delete_table_data_not_auth_fails(self): + def test_delete_table_data_503_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.delete('/api/database/1/table/9/data', status_code=503) + # test + try: + client = RestClient(username="a", password="b") + client.delete_table_data(database_id=1, table_id=9, keys={'id': 1}) + except ServiceError: + pass + + def test_delete_table_data_unknown_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.delete('/api/database/1/table/9/data', status_code=200) + # test + try: + client = RestClient(username="a", password="b") + client.delete_table_data(database_id=1, table_id=9, keys={'id': 1}) + except ResponseCodeError: + pass + + def test_delete_table_data_anonymous_fails(self): with requests_mock.Mocker() as mock: # mock mock.delete('/api/database/1/table/9/data', status_code=404) @@ -503,7 +790,7 @@ class TableUnitTest(unittest.TestCase): concept_uri="http://dbpedia.org/page/Category:Precipitation") self.assertEqual(exp, response) - def test_update_table_column_malformed_fails(self): + def test_update_table_column_400_fails(self): with requests_mock.Mocker() as mock: # mock mock.put('/api/database/1/table/2/column/1', status_code=400) @@ -516,7 +803,7 @@ class TableUnitTest(unittest.TestCase): except MalformedError: pass - def test_update_table_column_not_allowed_fails(self): + def test_update_table_column_403_fails(self): with requests_mock.Mocker() as mock: # mock mock.put('/api/database/1/table/2/column/1', status_code=403) @@ -529,7 +816,7 @@ class TableUnitTest(unittest.TestCase): except ForbiddenError: pass - def test_update_table_column_not_found_fails(self): + def test_update_table_column_404_fails(self): with requests_mock.Mocker() as mock: # mock mock.put('/api/database/1/table/2/column/1', status_code=404) @@ -542,7 +829,7 @@ class TableUnitTest(unittest.TestCase): except NotExistsError: pass - def test_update_table_column_not_auth_fails(self): + def test_update_table_column_anonymous_fails(self): with requests_mock.Mocker() as mock: # mock mock.put('/api/database/1/table/2/column/1', status_code=404) @@ -564,7 +851,7 @@ class TableUnitTest(unittest.TestCase): response = RestClient().analyse_table_statistics(database_id=1, table_id=2) self.assertEqual(exp, response) - def test_analyse_table_statistics_malformed_fails(self): + def test_analyse_table_statistics_400_fails(self): with requests_mock.Mocker() as mock: # mock mock.get('/api/analyse/database/1/table/2/statistics', status_code=400) @@ -574,7 +861,7 @@ class TableUnitTest(unittest.TestCase): except MalformedError: pass - def test_analyse_table_statistics_not_found_fails(self): + def test_analyse_table_statistics_404_fails(self): with requests_mock.Mocker() as mock: # mock mock.get('/api/analyse/database/1/table/2/statistics', status_code=404) @@ -584,6 +871,36 @@ class TableUnitTest(unittest.TestCase): except NotExistsError: pass + def test_analyse_table_statistics_502_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.get('/api/analyse/database/1/table/2/statistics', status_code=502) + # test + try: + RestClient().analyse_table_statistics(database_id=1, table_id=2) + except ServiceConnectionError: + pass + + def test_analyse_table_statistics_503_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.get('/api/analyse/database/1/table/2/statistics', status_code=503) + # test + try: + RestClient().analyse_table_statistics(database_id=1, table_id=2) + except ServiceError: + pass + + def test_analyse_table_statistics_unknown_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.get('/api/analyse/database/1/table/2/statistics', status_code=200) + # test + try: + RestClient().analyse_table_statistics(database_id=1, table_id=2) + except ResponseCodeError: + pass + if __name__ == "__main__": unittest.main() diff --git a/lib/python/tests/test_unit_unit.py b/lib/python/tests/test_unit_unit.py new file mode 100644 index 0000000000000000000000000000000000000000..efea8b69bb335cf4618216e7822dd5a7d2a1e467 --- /dev/null +++ b/lib/python/tests/test_unit_unit.py @@ -0,0 +1,35 @@ +import unittest + +import requests_mock + +from dbrepo.RestClient import RestClient +from dbrepo.api.dto import UnitBrief +from dbrepo.api.exceptions import ResponseCodeError + + +class UserUnitTest(unittest.TestCase): + + def test_get_units_succeeds(self): + with requests_mock.Mocker() as mock: + exp = [UnitBrief(id=1, + uri='http://www.ontology-of-units-of-measure.org/resource/om-2/CelsiusTemperature', + name='Celsius Temperature')] + # mock + mock.get('/api/unit', json=[exp[0].model_dump()]) + # test + response = RestClient().get_units() + self.assertEqual(exp, response) + + def test_get_units_unknown_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.get('/api/unit', status_code=202) + # test + try: + RestClient().get_units() + except ResponseCodeError: + pass + + +if __name__ == "__main__": + unittest.main() diff --git a/lib/python/tests/test_unit_user.py b/lib/python/tests/test_unit_user.py index 0e846b1b42e6ba935a2f204d09aa0920dce69626..213635634f4248ec32997b02966f79fd45c12663 100644 --- a/lib/python/tests/test_unit_user.py +++ b/lib/python/tests/test_unit_user.py @@ -3,9 +3,9 @@ import unittest import requests_mock from dbrepo.RestClient import RestClient -from dbrepo.api.dto import User, UserAttributes, UserBrief -from dbrepo.api.exceptions import ResponseCodeError, UsernameExistsError, EmailExistsError, NotExistsError, \ - ForbiddenError, AuthenticationError, MalformedError, ServiceError +from dbrepo.api.dto import UserAttributes, UserBrief, User, UnitBrief +from dbrepo.api.exceptions import ResponseCodeError, NotExistsError, \ + ForbiddenError, AuthenticationError, ServiceError, MalformedError class UserUnitTest(unittest.TestCase): @@ -22,101 +22,45 @@ class UserUnitTest(unittest.TestCase): def test_get_users_empty_succeeds(self): with requests_mock.Mocker() as mock: # mock - mock.get('http://localhost/api/user', json=[]) + mock.get('/api/user', json=[]) # test response = RestClient().get_users() self.assertEqual([], response) - def test_get_user_succeeds(self): + def test_get_users_succeeds(self): with requests_mock.Mocker() as mock: - exp = [ - User(id='8638c043-5145-4be8-a3e4-4b79991b0a16', username='mweise', - attributes=UserAttributes(theme='dark')) - ] + exp = [UserBrief(id='8638c043-5145-4be8-a3e4-4b79991b0a16', username='mweise')] # mock - mock.get('http://localhost/api/user', json=[exp[0].model_dump()]) + mock.get('/api/user', json=[exp[0].model_dump()]) # test response = RestClient().get_users() self.assertEqual(exp, response) - def test_get_user_fails(self): + def test_get_users_unknown_fails(self): with requests_mock.Mocker() as mock: # mock - mock.get('http://localhost/api/user', status_code=404) + mock.get('/api/user', status_code=404) # test try: response = RestClient().get_users() except ResponseCodeError as e: pass - def test_create_user_succeeds(self): - with requests_mock.Mocker() as mock: - exp = UserBrief(id='8638c043-5145-4be8-a3e4-4b79991b0a16', username='mweise') - # mock - mock.post('http://localhost/api/user', json=exp.model_dump(), status_code=201) - # test - response = RestClient().create_user(username='mweise', password='s3cr3t', email='mweise@example.com') - self.assertEqual(exp, response) - - def test_create_user_bad_request_fails(self): - with requests_mock.Mocker() as mock: - exp = UserBrief(id='8638c043-5145-4be8-a3e4-4b79991b0a16', username='mweise') - # mock - mock.post('http://localhost/api/user', json=exp.model_dump(), status_code=400) - # test - try: - response = RestClient().create_user(username='mweise', password='s3cr3t', email='mweise@example.com') - except MalformedError as e: - pass - - def test_create_user_username_exists_fails(self): - with requests_mock.Mocker() as mock: - exp = UserBrief(id='8638c043-5145-4be8-a3e4-4b79991b0a16', username='mweise') - # mock - mock.post('http://localhost/api/user', json=exp.model_dump(), status_code=409) - # test - try: - response = RestClient().create_user(username='mweise', password='s3cr3t', email='mweise@example.com') - except UsernameExistsError as e: - pass - - def test_create_user_default_role_not_exists_fails(self): - with requests_mock.Mocker() as mock: - exp = UserBrief(id='8638c043-5145-4be8-a3e4-4b79991b0a16', username='mweise') - # mock - mock.post('http://localhost/api/user', json=exp.model_dump(), status_code=404) - # test - try: - response = RestClient().create_user(username='mweise', password='s3cr3t', email='mweise@example.com') - except NotExistsError as e: - pass - - def test_create_user_emails_exists_fails(self): - with requests_mock.Mocker() as mock: - exp = UserBrief(id='8638c043-5145-4be8-a3e4-4b79991b0a16', username='mweise') - # mock - mock.post('http://localhost/api/user', json=exp.model_dump(), status_code=417) - # test - try: - response = RestClient().create_user(username='mweise', password='s3cr3t', email='mweise@example.com') - except EmailExistsError as e: - pass - def test_get_user_succeeds(self): with requests_mock.Mocker() as mock: exp = User(id='8638c043-5145-4be8-a3e4-4b79991b0a16', username='mweise', - attributes=UserAttributes(theme='dark')) + attributes=UserAttributes(theme='dark', language='en')) # mock - mock.get('http://localhost/api/user/8638c043-5145-4be8-a3e4-4b79991b0a16', + mock.get('/api/user/8638c043-5145-4be8-a3e4-4b79991b0a16', json=exp.model_dump()) # test response = RestClient().get_user(user_id='8638c043-5145-4be8-a3e4-4b79991b0a16') self.assertEqual(exp, response) - def test_get_user_not_found_fails(self): + def test_get_user_404_fails(self): with requests_mock.Mocker() as mock: # mock - mock.get('http://localhost/api/user/8638c043-5145-4be8-a3e4-4b79991b0a16', status_code=404) + mock.get('/api/user/8638c043-5145-4be8-a3e4-4b79991b0a16', status_code=404) # test try: response = RestClient().get_user(user_id='8638c043-5145-4be8-a3e4-4b79991b0a16') @@ -125,10 +69,9 @@ class UserUnitTest(unittest.TestCase): def test_update_user_succeeds(self): with requests_mock.Mocker() as mock: - exp = UserBrief(id='8638c043-5145-4be8-a3e4-4b79991b0a16', username='mweise', given_name='Martin', - attributes=UserAttributes(theme='dark')) + exp = UserBrief(id='8638c043-5145-4be8-a3e4-4b79991b0a16', username='mweise', given_name='Martin') # mock - mock.put('http://localhost/api/user/8638c043-5145-4be8-a3e4-4b79991b0a16', status_code=202, + mock.put('/api/user/8638c043-5145-4be8-a3e4-4b79991b0a16', status_code=202, json=exp.model_dump()) # test client = RestClient(username="a", password="b") @@ -136,95 +79,88 @@ class UserUnitTest(unittest.TestCase): language='en', theme='light') self.assertEqual(exp, response) - def test_update_user_not_allowed_fails(self): + def test_get_user_403_fails(self): with requests_mock.Mocker() as mock: # mock - mock.put('http://localhost/api/user/8638c043-5145-4be8-a3e4-4b79991b0a16', status_code=403) + mock.get(f'/api/user/f27921d4-b05f-4e21-a122-4953a6a779a2', status_code=403) # test try: - client = RestClient(username="a", password="b") - response = client.update_user(user_id='8638c043-5145-4be8-a3e4-4b79991b0a16', firstname='Martin', - language='en', theme='light') - except ForbiddenError as e: + RestClient().get_user('f27921d4-b05f-4e21-a122-4953a6a779a2') + except ForbiddenError: pass - def test_update_user_not_found_fails(self): + def test_get_user_404_fails(self): with requests_mock.Mocker() as mock: # mock - mock.put('http://localhost/api/user/8638c043-5145-4be8-a3e4-4b79991b0a16', status_code=404) + mock.get(f'/api/user/f27921d4-b05f-4e21-a122-4953a6a779a2', status_code=404) # test try: - client = RestClient(username="a", password="b") - response = client.update_user(user_id='8638c043-5145-4be8-a3e4-4b79991b0a16', firstname='Martin', - language='en', theme='light') - except NotExistsError as e: + RestClient().get_user('f27921d4-b05f-4e21-a122-4953a6a779a2') + except NotExistsError: pass - def test_update_user_not_auth_fails(self): + def test_get_user_unknown_fails(self): with requests_mock.Mocker() as mock: # mock - mock.put('http://localhost/api/user/8638c043-5145-4be8-a3e4-4b79991b0a16', status_code=405) + mock.get(f'/api/user/f27921d4-b05f-4e21-a122-4953a6a779a2', status_code=409) # test try: - response = RestClient().update_user(user_id='8638c043-5145-4be8-a3e4-4b79991b0a16', firstname='Martin', - language='en', theme='light') - except AuthenticationError as e: + RestClient().get_user('f27921d4-b05f-4e21-a122-4953a6a779a2') + except ResponseCodeError: pass - def test_update_user_password_succeeds(self): + def test_update_user_anonymous_fails(self): with requests_mock.Mocker() as mock: # mock - mock.put('http://localhost/api/user/8638c043-5145-4be8-a3e4-4b79991b0a16/password', status_code=202) + mock.put('/api/user/f27921d4-b05f-4e21-a122-4953a6a779a2', status_code=202) # test - client = RestClient(username="a", password="b") - response = client.update_user_password(user_id='8638c043-5145-4be8-a3e4-4b79991b0a16', - password='s3cr3t1n0rm4t10n') + try: + RestClient().update_user(user_id='f27921d4-b05f-4e21-a122-4953a6a779a2', theme='dark', language='en') + except AuthenticationError: + pass - def test_update_user_password_not_allowed_fails(self): + def test_update_user_400_fails(self): with requests_mock.Mocker() as mock: # mock - mock.put('http://localhost/api/user/8638c043-5145-4be8-a3e4-4b79991b0a16/password', status_code=403) + mock.put('/api/user/f27921d4-b05f-4e21-a122-4953a6a779a2', status_code=400) # test try: - client = RestClient(username="a", password="b") - response = client.update_user_password(user_id='8638c043-5145-4be8-a3e4-4b79991b0a16', - password='s3cr3t1n0rm4t10n') - except ForbiddenError as e: + RestClient(username='foo', password='bar').update_user(user_id='f27921d4-b05f-4e21-a122-4953a6a779a2', + theme='dark', language='en') + except MalformedError: pass - def test_update_user_password_not_found_fails(self): + def test_update_user_403_fails(self): with requests_mock.Mocker() as mock: # mock - mock.put('http://localhost/api/user/8638c043-5145-4be8-a3e4-4b79991b0a16/password', status_code=404) + mock.put('/api/user/f27921d4-b05f-4e21-a122-4953a6a779a2', status_code=403) # test try: - client = RestClient(username="a", password="b") - response = client.update_user_password(user_id='8638c043-5145-4be8-a3e4-4b79991b0a16', - password='s3cr3t1n0rm4t10n') - except NotExistsError as e: + RestClient(username='foo', password='bar').update_user(user_id='f27921d4-b05f-4e21-a122-4953a6a779a2', + theme='dark', language='en') + except ForbiddenError: pass - def test_update_user_password_keycloak_fails(self): + def test_update_user_404_fails(self): with requests_mock.Mocker() as mock: # mock - mock.put('http://localhost/api/user/8638c043-5145-4be8-a3e4-4b79991b0a16/password', status_code=503) + mock.put('/api/user/f27921d4-b05f-4e21-a122-4953a6a779a2', status_code=404) # test try: - client = RestClient(username="a", password="b") - response = client.update_user_password(user_id='8638c043-5145-4be8-a3e4-4b79991b0a16', - password='s3cr3t1n0rm4t10n') - except ServiceError as e: + RestClient(username='foo', password='bar').update_user(user_id='f27921d4-b05f-4e21-a122-4953a6a779a2', + theme='dark', language='en') + except NotExistsError: pass - def test_update_user_password_not_auth_fails(self): + def test_update_user_unknown_fails(self): with requests_mock.Mocker() as mock: # mock - mock.put('http://localhost/api/user/8638c043-5145-4be8-a3e4-4b79991b0a16/password', status_code=503) + mock.put('/api/user/f27921d4-b05f-4e21-a122-4953a6a779a2', status_code=200) # test try: - response = RestClient().update_user_password(user_id='8638c043-5145-4be8-a3e4-4b79991b0a16', - password='s3cr3t1n0rm4t10n') - except AuthenticationError as e: + RestClient(username='foo', password='bar').update_user(user_id='f27921d4-b05f-4e21-a122-4953a6a779a2', + theme='dark', language='en') + except ResponseCodeError: pass diff --git a/lib/python/tests/test_unit_view.py b/lib/python/tests/test_unit_view.py index 29fd6aac2f7942fa978c492a5b95ea31e724e0b9..842c2d7388b205590336ce38d722c437adb2ec40 100644 --- a/lib/python/tests/test_unit_view.py +++ b/lib/python/tests/test_unit_view.py @@ -6,7 +6,8 @@ from pandas import DataFrame from dbrepo.RestClient import RestClient from dbrepo.api.dto import View, ViewColumn, ColumnType, UserBrief, ViewBrief -from dbrepo.api.exceptions import ForbiddenError, NotExistsError, MalformedError, AuthenticationError +from dbrepo.api.exceptions import ForbiddenError, NotExistsError, MalformedError, AuthenticationError, \ + ResponseCodeError, ExternalSystemError, ServiceError, ServiceConnectionError class ViewUnitTest(unittest.TestCase): @@ -22,22 +23,22 @@ class ViewUnitTest(unittest.TestCase): def test_get_views_succeeds(self): with requests_mock.Mocker() as mock: exp = [ViewBrief(id=1, - name="Data", - internal_name="data", - database_id=1, - initial_view=False, - query="SELECT id FROM mytable WHERE deg > 0", - query_hash="94c74728b11a690e51d64719868824735f0817b7", - owned_by='8638c043-5145-4be8-a3e4-4b79991b0a16', - is_public=True, - is_schema_public=True)] + name="Data", + internal_name="data", + database_id=1, + initial_view=False, + query="SELECT id FROM mytable WHERE deg > 0", + query_hash="94c74728b11a690e51d64719868824735f0817b7", + owned_by='8638c043-5145-4be8-a3e4-4b79991b0a16', + is_public=True, + is_schema_public=True)] # mock mock.get('/api/database/1/view', json=[exp[0].model_dump()]) # test response = RestClient().get_views(database_id=1) self.assertEqual(exp, response) - def test_get_views_not_found_fails(self): + def test_get_views_404_fails(self): with requests_mock.Mocker() as mock: # mock mock.get('/api/database/1/view', status_code=404) @@ -47,6 +48,16 @@ class ViewUnitTest(unittest.TestCase): except NotExistsError: pass + def test_get_views_unknown_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.get('/api/database/1/view', status_code=202) + # test + try: + response = RestClient().get_views(database_id=1) + except ResponseCodeError: + pass + def test_get_view_succeeds(self): with requests_mock.Mocker() as mock: exp = View(id=3, @@ -73,7 +84,7 @@ class ViewUnitTest(unittest.TestCase): response = RestClient().get_view(database_id=1, view_id=3) self.assertEqual(exp, response) - def test_get_view_not_allowed_fails(self): + def test_get_view_403_fails(self): with requests_mock.Mocker() as mock: # mock mock.get('/api/database/1/view/3', status_code=403) @@ -83,7 +94,7 @@ class ViewUnitTest(unittest.TestCase): except ForbiddenError: pass - def test_get_views_not_found_fails(self): + def test_get_view_404_fails(self): with requests_mock.Mocker() as mock: # mock mock.get('/api/database/1/view/3', status_code=404) @@ -93,6 +104,78 @@ class ViewUnitTest(unittest.TestCase): except NotExistsError: pass + def test_get_view_unknown_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.get('/api/database/1/view/3', status_code=202) + # test + try: + response = RestClient().get_view(database_id=1, view_id=3) + except ResponseCodeError: + pass + + def test_update_view_succeeds(self): + with requests_mock.Mocker() as mock: + exp = ViewBrief(id=1, + name="Data", + internal_name="data", + database_id=1, + initial_view=False, + query="SELECT id FROM mytable WHERE deg > 0", + query_hash="94c74728b11a690e51d64719868824735f0817b7", + owned_by='8638c043-5145-4be8-a3e4-4b79991b0a16', + is_public=False, + is_schema_public=False) + # mock + mock.put('/api/database/1/view/1', json=exp.model_dump(), status_code=202) + # test + response = RestClient(username='foo', password='bar').update_view(database_id=1, view_id=1, + is_public=False, is_schema_public=False) + self.assertEqual(exp, response) + + def test_update_view_403_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.put('/api/database/1/view/1', status_code=403) + # test + try: + RestClient(username='foo', password='bar').update_view(database_id=1, view_id=1, is_public=False, + is_schema_public=False) + except ForbiddenError: + pass + + def test_update_view_404_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.put('/api/database/1/view/1', status_code=404) + # test + try: + RestClient(username='foo', password='bar').update_view(database_id=1, view_id=1, is_public=False, + is_schema_public=False) + except NotExistsError: + pass + + def test_update_view_unknown_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.put('/api/database/1/view/1', status_code=200) + # test + try: + RestClient(username='foo', password='bar').update_view(database_id=1, view_id=1, is_public=False, + is_schema_public=False) + except ResponseCodeError: + pass + + def test_update_view_anonymous_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.put('/api/database/1/view/1', status_code=403) + # test + try: + RestClient().update_view(database_id=1, view_id=1, is_public=False, is_schema_public=False) + except AuthenticationError: + pass + def test_create_view_succeeds(self): with requests_mock.Mocker() as mock: exp = View(id=3, @@ -121,7 +204,7 @@ class ViewUnitTest(unittest.TestCase): query="SELECT id FROM mytable WHERE deg > 0") self.assertEqual(exp, response) - def test_create_view_malformed_fails(self): + def test_create_view_400_fails(self): with requests_mock.Mocker() as mock: # mock mock.post('/api/database/1/view', status_code=400) @@ -133,7 +216,7 @@ class ViewUnitTest(unittest.TestCase): except MalformedError: pass - def test_create_view_not_allowed_fails(self): + def test_create_view_403_fails(self): with requests_mock.Mocker() as mock: # mock mock.post('/api/database/1/view', status_code=403) @@ -145,7 +228,7 @@ class ViewUnitTest(unittest.TestCase): except ForbiddenError: pass - def test_create_view_not_found_fails(self): + def test_create_view_404_fails(self): with requests_mock.Mocker() as mock: # mock mock.post('/api/database/1/view', status_code=404) @@ -157,7 +240,55 @@ class ViewUnitTest(unittest.TestCase): except NotExistsError: pass - def test_create_view_not_auth_fails(self): + def test_create_view_423_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.post('/api/database/1/view', status_code=423) + # test + try: + client = RestClient(username="a", password="b") + response = client.create_view(database_id=1, name="Data", is_public=True, is_schema_public=True, + query="SELECT id FROM mytable WHERE deg > 0") + except ExternalSystemError: + pass + + def test_create_view_502_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.post('/api/database/1/view', status_code=502) + # test + try: + client = RestClient(username="a", password="b") + response = client.create_view(database_id=1, name="Data", is_public=True, is_schema_public=True, + query="SELECT id FROM mytable WHERE deg > 0") + except ServiceConnectionError: + pass + + def test_create_view_503_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.post('/api/database/1/view', status_code=503) + # test + try: + client = RestClient(username="a", password="b") + response = client.create_view(database_id=1, name="Data", is_public=True, is_schema_public=True, + query="SELECT id FROM mytable WHERE deg > 0") + except ServiceError: + pass + + def test_create_view_unknown_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.post('/api/database/1/view', status_code=200) + # test + try: + client = RestClient(username="a", password="b") + response = client.create_view(database_id=1, name="Data", is_public=True, is_schema_public=True, + query="SELECT id FROM mytable WHERE deg > 0") + except ResponseCodeError: + pass + + def test_create_view_anonymous_fails(self): with requests_mock.Mocker() as mock: # mock mock.post('/api/database/1/view', status_code=404) @@ -176,7 +307,7 @@ class ViewUnitTest(unittest.TestCase): client = RestClient(username="a", password="b") client.delete_view(database_id=1, view_id=3) - def test_delete_view_malformed_fails(self): + def test_delete_view_400_fails(self): with requests_mock.Mocker() as mock: # mock mock.delete('/api/database/1/view/3', status_code=400) @@ -187,7 +318,7 @@ class ViewUnitTest(unittest.TestCase): except MalformedError: pass - def test_delete_view_not_allowed_fails(self): + def test_delete_view_403_fails(self): with requests_mock.Mocker() as mock: # mock mock.delete('/api/database/1/view/3', status_code=403) @@ -198,7 +329,62 @@ class ViewUnitTest(unittest.TestCase): except ForbiddenError: pass - def test_delete_view_not_auth_fails(self): + def test_delete_view_404_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.delete('/api/database/1/view/3', status_code=404) + # test + try: + client = RestClient(username="a", password="b") + client.delete_view(database_id=1, view_id=3) + except NotExistsError: + pass + + def test_delete_view_423_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.delete('/api/database/1/view/3', status_code=423) + # test + try: + client = RestClient(username="a", password="b") + client.delete_view(database_id=1, view_id=3) + except ExternalSystemError: + pass + + def test_delete_view_502_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.delete('/api/database/1/view/3', status_code=502) + # test + try: + client = RestClient(username="a", password="b") + client.delete_view(database_id=1, view_id=3) + except ServiceConnectionError: + pass + + def test_delete_view_503_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.delete('/api/database/1/view/3', status_code=503) + # test + try: + client = RestClient(username="a", password="b") + client.delete_view(database_id=1, view_id=3) + except ServiceError: + pass + + def test_delete_view_unknown_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.delete('/api/database/1/view/3', status_code=200) + # test + try: + client = RestClient(username="a", password="b") + client.delete_view(database_id=1, view_id=3) + except ResponseCodeError: + pass + + def test_delete_view_anonymous_fails(self): with requests_mock.Mocker() as mock: # mock mock.delete('/api/database/1/view/3', status_code=403) @@ -229,7 +415,7 @@ class ViewUnitTest(unittest.TestCase): self.assertEqual(df.shape, response.shape) self.assertTrue(DataFrame.equals(df, response)) - def test_get_view_data_malformed_fails(self): + def test_get_view_data_400_fails(self): with requests_mock.Mocker() as mock: # mock mock.get('/api/database/1/view/3/data', status_code=400) @@ -239,7 +425,7 @@ class ViewUnitTest(unittest.TestCase): except MalformedError: pass - def test_get_view_data_not_allowed_fails(self): + def test_get_view_data_403_fails(self): with requests_mock.Mocker() as mock: # mock mock.get('/api/database/1/view/3/data', status_code=403) @@ -249,6 +435,46 @@ class ViewUnitTest(unittest.TestCase): except ForbiddenError: pass + def test_get_view_data_404_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.get('/api/database/1/view/3/data', status_code=404) + # test + try: + response = RestClient().get_view_data(database_id=1, view_id=3) + except NotExistsError: + pass + + def test_get_view_data_409_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.get('/api/database/1/view/3/data', status_code=409) + # test + try: + response = RestClient().get_view_data(database_id=1, view_id=3) + except ExternalSystemError: + pass + + def test_get_view_data_503_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.get('/api/database/1/view/3/data', status_code=503) + # test + try: + response = RestClient().get_view_data(database_id=1, view_id=3) + except ServiceError: + pass + + def test_get_view_data_unknown_fails(self): + with requests_mock.Mocker() as mock: + # mock + mock.get('/api/database/1/view/3/data', status_code=202) + # test + try: + response = RestClient().get_view_data(database_id=1, view_id=3) + except ResponseCodeError: + pass + def test_get_view_data_count_succeeds(self): with requests_mock.Mocker() as mock: exp = 844737 @@ -258,7 +484,7 @@ class ViewUnitTest(unittest.TestCase): response = RestClient().get_view_data_count(database_id=1, view_id=3) self.assertEqual(exp, response) - def test_get_view_data_count_malformed_fails(self): + def test_get_view_data_count_400_fails(self): with requests_mock.Mocker() as mock: # mock mock.head('/api/database/1/view/3/data', status_code=400) @@ -268,7 +494,7 @@ class ViewUnitTest(unittest.TestCase): except MalformedError: pass - def test_get_view_data_count_not_allowed_fails(self): + def test_get_view_data_count_403_fails(self): with requests_mock.Mocker() as mock: # mock mock.head('/api/database/1/view/3/data', status_code=403)