From 5eb9998dcf9bfa9d447ed8f1604f48086d78fd6a Mon Sep 17 00:00:00 2001 From: quacumque <43530070+0x009922@users.noreply.github.com> Date: Tue, 23 Jul 2024 11:06:17 +0900 Subject: [PATCH 1/6] feat: filter by `deleted`, retry overview, update cache policy hyperledger/iroha-2-docs#497 --- deno.lock | 220 ++++++++++++++++++++++++++++++++++++++++++++++- deps.ts | 2 +- src/agent.ts | 23 ----- src/aggregate.ts | 25 ++---- src/api.ts | 127 +++++++++++++-------------- src/main.ts | 43 +++++---- 6 files changed, 317 insertions(+), 123 deletions(-) delete mode 100644 src/agent.ts diff --git a/deno.lock b/deno.lock index 85204ec..a0528ff 100644 --- a/deno.lock +++ b/deno.lock @@ -1,5 +1,223 @@ { - "version": "2", + "version": "3", + "redirects": { + "https://deno.land/std/datetime/mod.ts": "https://deno.land/std@0.224.0/datetime/mod.ts" + }, "remote": { + "https://deno.land/std@0.193.0/_util/asserts.ts": "178dfc49a464aee693a7e285567b3d0b555dc805ff490505a8aae34f9cfb1462", + "https://deno.land/std@0.193.0/_util/os.ts": "d932f56d41e4f6a6093d56044e29ce637f8dcc43c5a90af43504a889cf1775e3", + "https://deno.land/std@0.193.0/async/deferred.ts": "42790112f36a75a57db4a96d33974a936deb7b04d25c6084a9fa8a49f135def8", + "https://deno.land/std@0.193.0/bytes/bytes_list.ts": "31d664f4d42fa922066405d0e421c56da89d751886ee77bbe25a88bf0310c9d0", + "https://deno.land/std@0.193.0/bytes/concat.ts": "d26d6f3d7922e6d663dacfcd357563b7bf4a380ce5b9c2bbe0c8586662f25ce2", + "https://deno.land/std@0.193.0/bytes/copy.ts": "939d89e302a9761dcf1d9c937c7711174ed74c59eef40a1e4569a05c9de88219", + "https://deno.land/std@0.193.0/bytes/ends_with.ts": "4228811ebc71615d27f065c54b5e815ec1972538772b0f413c0efe05245b472e", + "https://deno.land/std@0.193.0/bytes/equals.ts": "fc190cce412b2136979181b163ec7e05f7e7a947e39102eee4b8c0d62519ddf9", + "https://deno.land/std@0.193.0/bytes/includes_needle.ts": "76a8163126fb2f8bf86fd7f22192c3bb04bf6a20b987a095127c2ca08adf3ba6", + "https://deno.land/std@0.193.0/bytes/index_of_needle.ts": "9c06610e9611b5647ac25952e71a22e09227c9f1b8cbeeb33399bf8bf8a7f649", + "https://deno.land/std@0.193.0/bytes/last_index_of_needle.ts": "f1602f221c3b678bc4f1e1c88a70a15ab7da32c21751dbbc6c957c956951d784", + "https://deno.land/std@0.193.0/bytes/mod.ts": "e869bba1e7a2e3a9cc6c2d55471888429a544e70a840c087672e656e7ba21815", + "https://deno.land/std@0.193.0/bytes/repeat.ts": "6f5e490d8d72bcbf8d84a6bb04690b9b3eb5822c5a11687bca73a2318a842294", + "https://deno.land/std@0.193.0/bytes/starts_with.ts": "3e607a70c9c09f5140b7a7f17a695221abcc7244d20af3eb47ccbb63f5885135", + "https://deno.land/std@0.193.0/crypto/keystack.ts": "877ab0f19eb7d37ad6495190d3c3e39f58e9c52e0b6a966f82fd6df67ca55f90", + "https://deno.land/std@0.193.0/crypto/timing_safe_equal.ts": "0fae34ee02264f309ae0b6e54e9746a7aba3996e5454903ed106967a7a9ef665", + "https://deno.land/std@0.193.0/encoding/base64.ts": "144ae6234c1fbe5b68666c711dc15b1e9ee2aef6d42b3b4345bf9a6c91d70d0d", + "https://deno.land/std@0.193.0/encoding/base64url.ts": "2ed4ba122b20fedf226c5d337cf22ee2024fa73a8f85d915d442af7e9ce1fae1", + "https://deno.land/std@0.193.0/http/_negotiation/common.ts": "14d1a52427ab258a4b7161cd80e1d8a207b7cc64b46e911780f57ead5f4323c6", + "https://deno.land/std@0.193.0/http/_negotiation/encoding.ts": "ff747d107277c88cb7a6a62a08eeb8d56dad91564cbcccb30694d5dc126dcc53", + "https://deno.land/std@0.193.0/http/_negotiation/language.ts": "7bcddd8db3330bdb7ce4fc00a213c5547c1968139864201efd67ef2d0d51887d", + "https://deno.land/std@0.193.0/http/_negotiation/media_type.ts": "58847517cd549384ad677c0fe89e0a4815be36fe7a303ea63cee5f6a1d7e1692", + "https://deno.land/std@0.193.0/http/cookie_map.ts": "d148a5eaf35f19905dd5104126fa47ac71105306dd42f129732365e43108b28a", + "https://deno.land/std@0.193.0/http/etag.ts": "6ad8abbbb1045aabf2307959a2c5565054a8bf01c9824ddee836b1ff22706a58", + "https://deno.land/std@0.193.0/http/http_errors.ts": "bbda34819060af86537cecc9dc8e045f877130808b7e7acde4197c5328e852d0", + "https://deno.land/std@0.193.0/http/http_status.ts": "8a7bcfe3ac025199ad804075385e57f63d055b2aed539d943ccc277616d6f932", + "https://deno.land/std@0.193.0/http/method.ts": "e66c2a015cb46c21ab0bb3589aa4fca43143a506cb324ffdfd42d2edef7bc0c4", + "https://deno.land/std@0.193.0/http/negotiation.ts": "46e74a6bad4b857333a58dc5b50fe8e5a4d5267e97292293ea65f980bd918086", + "https://deno.land/std@0.193.0/http/server_sent_event.ts": "1f3597d175e8935123306a24d7f4423a463667a70953d17b4115af1880459d55", + "https://deno.land/std@0.193.0/http/user_agent.ts": "6f4308670f261118cc6a1518bf37431a5b4f21322b4a4edf0963e182264ce404", + "https://deno.land/std@0.193.0/io/buf_reader.ts": "06fff3337091c49e99ebd2dd790c9a90364c087a2953ea081667400fd6c6cebb", + "https://deno.land/std@0.193.0/io/buf_writer.ts": "48c33c8f00b61dcbc7958706741cec8e59810bd307bc6a326cbd474fe8346dfd", + "https://deno.land/std@0.193.0/io/buffer.ts": "17f4410eaaa60a8a85733e8891349a619eadfbbe42e2f319283ce2b8f29723ab", + "https://deno.land/std@0.193.0/io/copy_n.ts": "0cc7ce07c75130f6fc18621ec1911c36e147eb9570664fee0ea12b1988167590", + "https://deno.land/std@0.193.0/io/limited_reader.ts": "6c9a216f8eef39c1ee2a6b37a29372c8fc63455b2eeb91f06d9646f8f759fc8b", + "https://deno.land/std@0.193.0/io/mod.ts": "2665bcccc1fd6e8627cca167c3e92aaecbd9897556b6f69e6d258070ef63fd9b", + "https://deno.land/std@0.193.0/io/multi_reader.ts": "9c2a0a31686c44b277e16da1d97b4686a986edcee48409b84be25eedbc39b271", + "https://deno.land/std@0.193.0/io/read_delim.ts": "c02b93cc546ae8caad8682ae270863e7ace6daec24c1eddd6faabc95a9d876a3", + "https://deno.land/std@0.193.0/io/read_int.ts": "7cb8bcdfaf1107586c3bacc583d11c64c060196cb070bb13ae8c2061404f911f", + "https://deno.land/std@0.193.0/io/read_lines.ts": "c526c12a20a9386dc910d500f9cdea43cba974e853397790bd146817a7eef8cc", + "https://deno.land/std@0.193.0/io/read_long.ts": "f0aaa420e3da1261c5d33c5e729f09922f3d9fa49f046258d4ff7a00d800c71e", + "https://deno.land/std@0.193.0/io/read_range.ts": "28152daf32e43dd9f7d41d8466852b0d18ad766cd5c4334c91fef6e1b3a74eb5", + "https://deno.land/std@0.193.0/io/read_short.ts": "805cb329574b850b84bf14a92c052c59b5977a492cd780c41df8ad40826c1a20", + "https://deno.land/std@0.193.0/io/read_string_delim.ts": "5dc9f53bdf78e7d4ee1e56b9b60352238ab236a71c3e3b2a713c3d78472a53ce", + "https://deno.land/std@0.193.0/io/slice_long_to_bytes.ts": "48d9bace92684e880e46aa4a2520fc3867f9d7ce212055f76ecc11b22f9644b7", + "https://deno.land/std@0.193.0/io/string_reader.ts": "da0f68251b3d5b5112485dfd4d1b1936135c9b4d921182a7edaf47f74c25cc8f", + "https://deno.land/std@0.193.0/io/string_writer.ts": "8a03c5858c24965a54c6538bed15f32a7c72f5704a12bda56f83a40e28e5433e", + "https://deno.land/std@0.193.0/media_types/_db.ts": "7606d83e31f23ce1a7968cbaee852810c2cf477903a095696cdc62eaab7ce570", + "https://deno.land/std@0.193.0/media_types/_util.ts": "916efbd30b6148a716f110e67a4db29d6949bf4048997b754415dd7e42c52378", + "https://deno.land/std@0.193.0/media_types/content_type.ts": "ad98a5aa2d95f5965b2796072284258710a25e520952376ed432b0937ce743bc", + "https://deno.land/std@0.193.0/media_types/extension.ts": "a7cd28c9417143387cdfed27d4e8607ebcf5b1ec27eb8473d5b000144689fe65", + "https://deno.land/std@0.193.0/media_types/extensions_by_type.ts": "43806d6a52a0d6d965ada9d20e60a982feb40bc7a82268178d94edb764694fed", + "https://deno.land/std@0.193.0/media_types/format_media_type.ts": "f5e1073c05526a6f5a516ac5c5587a1abd043bf1039c71cde1166aa4328c8baf", + "https://deno.land/std@0.193.0/media_types/get_charset.ts": "18b88274796fda5d353806bf409eb1d2ddb3f004eb4bd311662c4cdd8ac173db", + "https://deno.land/std@0.193.0/media_types/mod.ts": "d3f0b99f85053bc0b98ecc24eaa3546dfa09b856dc0bbaf60d8956d2cdd710c8", + "https://deno.land/std@0.193.0/media_types/parse_media_type.ts": "835c4112e1357e95b4f10d7cdea5ae1801967e444f48673ff8f1cb4d32af9920", + "https://deno.land/std@0.193.0/media_types/type_by_extension.ts": "daa801eb0f11cdf199445d0f1b656cf116d47dcf9e5b85cc1e6b4469f5ee0432", + "https://deno.land/std@0.193.0/media_types/vendor/mime-db.v1.52.0.ts": "6925bbcae81ca37241e3f55908d0505724358cda3384eaea707773b2c7e99586", + "https://deno.land/std@0.193.0/path/_constants.ts": "e49961f6f4f48039c0dfed3c3f93e963ca3d92791c9d478ac5b43183413136e0", + "https://deno.land/std@0.193.0/path/_interface.ts": "6471159dfbbc357e03882c2266d21ef9afdb1e4aa771b0545e90db58a0ba314b", + "https://deno.land/std@0.193.0/path/_util.ts": "d7abb1e0dea065f427b89156e28cdeb32b045870acdf865833ba808a73b576d0", + "https://deno.land/std@0.193.0/path/common.ts": "ee7505ab01fd22de3963b64e46cff31f40de34f9f8de1fff6a1bd2fe79380000", + "https://deno.land/std@0.193.0/path/glob.ts": "d479e0a695621c94d3fd7fe7abd4f9499caf32a8de13f25073451c6ef420a4e1", + "https://deno.land/std@0.193.0/path/mod.ts": "f065032a7189404fdac3ad1a1551a9ac84751d2f25c431e101787846c86c79ef", + "https://deno.land/std@0.193.0/path/posix.ts": "8b7c67ac338714b30c816079303d0285dd24af6b284f7ad63da5b27372a2c94d", + "https://deno.land/std@0.193.0/path/separator.ts": "0fb679739d0d1d7bf45b68dacfb4ec7563597a902edbaf3c59b50d5bcadd93b1", + "https://deno.land/std@0.193.0/path/win32.ts": "d186344e5583bcbf8b18af416d13d82b35a317116e6460a5a3953508c3de5bba", + "https://deno.land/std@0.193.0/streams/_common.ts": "f45cba84f0d813de3326466095539602364a9ba521f804cc758f7a475cda692d", + "https://deno.land/std@0.193.0/streams/buffer.ts": "d5b3d7d0299114e5b2ea895a8bf202a687fd915c5282f8096c7bae23b5a04407", + "https://deno.land/std@0.193.0/streams/byte_slice_stream.ts": "225d57263a34325d7c96cb3dafeb478eec0e6fd05cd0458d678752eadd132bb4", + "https://deno.land/std@0.193.0/streams/copy.ts": "75cbc795ff89291df22ddca5252de88b2e16d40c85d02840593386a8a1454f71", + "https://deno.land/std@0.193.0/streams/delimiter_stream.ts": "f69e849b3d1f59f02424497273f411105a6f76a9f13da92aeeb9a2d554236814", + "https://deno.land/std@0.193.0/streams/early_zip_readable_streams.ts": "4005fa74162b943f79899e5d7cb96adcbc0a6b867f9144974ed12d30e0a556e1", + "https://deno.land/std@0.193.0/streams/iterate_reader.ts": "bbec1d45c2df2c0c5920bad0549351446fdc8e0886d99e95959b259dbcdb6072", + "https://deno.land/std@0.193.0/streams/limited_bytes_transform_stream.ts": "05dc592ffaab83257494d22dd53917e56243c26e5e3129b3f13ddbbbc4785048", + "https://deno.land/std@0.193.0/streams/limited_transform_stream.ts": "d69ab790232c1b86f53621ad41ef03c235f2abb4b7a1cd51960ad6e12ee55e38", + "https://deno.land/std@0.193.0/streams/merge_readable_streams.ts": "dc2db0cbf1b14d999aa2aa2a2a1ba24ce58953878f29845ed9319321d0a01fab", + "https://deno.land/std@0.193.0/streams/mod.ts": "c07ec010e700b9ea887dc36ca08729828bc7912f711e4054e24d33fd46282252", + "https://deno.land/std@0.193.0/streams/read_all.ts": "ee319772fb0fd28302f97343cc48dfcf948f154fd0d755d8efe65814b70533be", + "https://deno.land/std@0.193.0/streams/readable_stream_from_iterable.ts": "cd4bb9e9bf6dbe84c213beb1f5085c326624421671473e410cfaecad15f01865", + "https://deno.land/std@0.193.0/streams/readable_stream_from_reader.ts": "bfc416c4576a30aac6b9af22c9dc292c20c6742141ee7c55b5e85460beb0c54e", + "https://deno.land/std@0.193.0/streams/reader_from_iterable.ts": "55f68110dce3f8f2c87b834d95f153bc904257fc65175f9f2abe78455cb8047c", + "https://deno.land/std@0.193.0/streams/reader_from_stream_reader.ts": "fa4971e5615a010e49492c5d1688ca1a4d17472a41e98b498ab89a64ebd7ac73", + "https://deno.land/std@0.193.0/streams/text_delimiter_stream.ts": "20e680ab8b751390e359288ce764f9c47d164af11a263870746eeca4bc7d976b", + "https://deno.land/std@0.193.0/streams/text_line_stream.ts": "0f2c4b33a5fdb2476f2e060974cba1347cefe99a4af33c28a57524b1a34750fa", + "https://deno.land/std@0.193.0/streams/to_transform_stream.ts": "7f55fc0b14cf3ed0f8d10d8f41d05bdc40726e44a65c37f58705d10a615f0159", + "https://deno.land/std@0.193.0/streams/writable_stream_from_writer.ts": "56fff5c82fb736fdd669b567cc0b2bbbe0351002cd13254eae26c366e2bed89a", + "https://deno.land/std@0.193.0/streams/write_all.ts": "aec90152978581ea62d56bb53a5cbf487e6a89c902f87c5969681ffbdf32b998", + "https://deno.land/std@0.193.0/streams/writer_from_stream_writer.ts": "07c7ee025151a190f37fc42cbb01ff93afc949119ebddc6e0d0df14df1bf6950", + "https://deno.land/std@0.193.0/streams/zip_readable_streams.ts": "a9d81aa451240f79230add674809dbee038d93aabe286e2d9671e66591fc86ca", + "https://deno.land/std@0.194.0/_util/asserts.ts": "178dfc49a464aee693a7e285567b3d0b555dc805ff490505a8aae34f9cfb1462", + "https://deno.land/std@0.194.0/bytes/copy.ts": "939d89e302a9761dcf1d9c937c7711174ed74c59eef40a1e4569a05c9de88219", + "https://deno.land/std@0.194.0/collections/filter_values.ts": "5b9feaf17b9a6e5ffccdd36cf6f38fa4ffa94cff2602d381c2ad0c2a97929652", + "https://deno.land/std@0.194.0/collections/without_all.ts": "a89f5da0b5830defed4f59666e188df411d8fece35a5f6ca69be6ca71a95c185", + "https://deno.land/std@0.194.0/dotenv/load.ts": "0636983549b98f29ab75c9a22a42d9723f0a389ece5498fe971e7bb2556a12e2", + "https://deno.land/std@0.194.0/dotenv/mod.ts": "39e5d19e077e55d7e01ea600eb1c6d1e18a8dfdfc65d68826257a576788da3a4", + "https://deno.land/std@0.194.0/fmt/colors.ts": "d67e3cd9f472535241a8e410d33423980bec45047e343577554d3356e1f0ef4e", + "https://deno.land/std@0.194.0/fs/exists.ts": "29c26bca8584a22876be7cb8844f1b6c8fc35e9af514576b78f5c6884d7ed02d", + "https://deno.land/std@0.194.0/io/buf_writer.ts": "48c33c8f00b61dcbc7958706741cec8e59810bd307bc6a326cbd474fe8346dfd", + "https://deno.land/std@0.194.0/log/handlers.ts": "38871ecbfa67b0d39dc3384210439ac9a13cba6118b912236f9011b5989b9a4d", + "https://deno.land/std@0.194.0/log/levels.ts": "6309147664e9e008cd6671610f2505c4c95f181f6bae4816a84b33e0aec66859", + "https://deno.land/std@0.194.0/log/logger.ts": "257ceb47e3f5f872068073de9809b015a7400e8d86dd40563c1d80169e578f71", + "https://deno.land/std@0.194.0/log/mod.ts": "36d156ad18de3f1806c6ddafa4965129be99ccafc27f1813de528d65b6c528bf", + "https://deno.land/std@0.197.0/collections/_comparators.ts": "fa7f9a44cea1d270098a2a5a6f8bb30c61b595c1b1f983bd67c6297d766adffa", + "https://deno.land/std@0.197.0/collections/_utils.ts": "5114abc026ddef71207a79609b984614e66a63a4bda17d819d56b0e72c51527e", + "https://deno.land/std@0.197.0/collections/aggregate_groups.ts": "22c7115a2e3d057979066a2238d3ba23b04acdd35491e82d08d2e052b1d7c068", + "https://deno.land/std@0.197.0/collections/associate_by.ts": "64869182eeed3be9222137d071224ac6a01bcc3c5236610306f99e3e3107ec47", + "https://deno.land/std@0.197.0/collections/associate_with.ts": "cf9735d7b4d345275fd3b7420eaba9726a2844e847f2d9e4be9f103f6f3e2c8b", + "https://deno.land/std@0.197.0/collections/binary_heap.ts": "6c889ad21a86593d5d321e3404918f0449c2cde82ba1b57cbc5ffcd6a1d063ae", + "https://deno.land/std@0.197.0/collections/chunk.ts": "f82c52a82ad9338018570c42f6de0fb132fcb14914c31a444e360ac104d7b55b", + "https://deno.land/std@0.197.0/collections/deep_merge.ts": "9db788ba56cb05b65c77166b789e58e125dff159b7f41bf4d19dc1cba19ecb8b", + "https://deno.land/std@0.197.0/collections/distinct.ts": "01595bd8e608a5e2de8a8631055587e6f01d68861e463eef34f8e295713500b8", + "https://deno.land/std@0.197.0/collections/distinct_by.ts": "3afe11d81eafb30c7c9dbf568d94357f3d88153292c00671b72cd695deae6602", + "https://deno.land/std@0.197.0/collections/drop_last_while.ts": "44287a09e0831246a724aa140ef8ef41617a1317710265c19b436e880a4fea8c", + "https://deno.land/std@0.197.0/collections/drop_while.ts": "74cfa9d6b74b3503d5dc70c8ab96af21e7cb2eaff8b9f11aca2aca403e56aeae", + "https://deno.land/std@0.197.0/collections/filter_entries.ts": "77d3f0b3dddcb43e92fc745dd66d01b4922d0eff2a3e2bd25d9d826889f203d8", + "https://deno.land/std@0.197.0/collections/filter_keys.ts": "a29cfe8730ddb54e9e071ea45e8a82e166c7629d18675652def70c1bf80e2ef6", + "https://deno.land/std@0.197.0/collections/filter_values.ts": "16e1fc456a7969e770ec5b89edf5ac97b295ca534b47c1a83f061b409aad7814", + "https://deno.land/std@0.197.0/collections/find_single.ts": "d941876fbec0c1d8b7f41a52f8c1b03198c5695dfaa8d7a12e7a6317d2e6085b", + "https://deno.land/std@0.197.0/collections/first_not_nullish_of.ts": "08d92c4f9c66ff1b1f64a48fa7c1e557e3e6d43f48a46d53a98c3383041b98ae", + "https://deno.land/std@0.197.0/collections/group_by.ts": "f90f483598b38f0174f946589f3af25b1c3cf89d4f3b14d4f0fbe3aaaca0abdd", + "https://deno.land/std@0.197.0/collections/includes_value.ts": "8284a0b3178465b6565d234c1995117320b248dbcff332f77c5e891a11459e0c", + "https://deno.land/std@0.197.0/collections/intersect.ts": "0b4924e2aceaddd5e55591bd9d648cf75745151795ee8a76beddf4facec77548", + "https://deno.land/std@0.197.0/collections/join_to_string.ts": "a487ccc99dbac988698cf5b7c6cc4bb50eefd1784042d0b958a77af413683fe0", + "https://deno.land/std@0.197.0/collections/map_entries.ts": "282b0e5141fbe73a45903674597fb10ec2b211fab8cbac85874eebd97f522a62", + "https://deno.land/std@0.197.0/collections/map_keys.ts": "3dd2cf3a940f1432628cb0252b919d268e8bb38fe8bcd78153a440909173ae98", + "https://deno.land/std@0.197.0/collections/map_not_nullish.ts": "8793a340ca245664902a309ac02861b9eb01780f7b78aa5ad329da7d95e011ec", + "https://deno.land/std@0.197.0/collections/map_values.ts": "c88f306b2b3ec84043e16ab4e1b062055ab32cf4d68bb1d7447afaafa0a1b3bf", + "https://deno.land/std@0.197.0/collections/max_by.ts": "9d5940986aac51b2e4feaebef9cd8bf6e1eceeee5edcf3303e334b737f99520d", + "https://deno.land/std@0.197.0/collections/max_of.ts": "aa29268f102159364bc296eea9f45b0d4f1b041ae9d0037c3dce1c43e21e30be", + "https://deno.land/std@0.197.0/collections/max_with.ts": "035cf8c2c98391570380c3725a4f2b161dac2e9dc97fb2dc6b862a5dc589588e", + "https://deno.land/std@0.197.0/collections/min_by.ts": "0fcf1973e8e309da83070dc22def47f468e410d7da462a294c5513ae2c5d6479", + "https://deno.land/std@0.197.0/collections/min_of.ts": "7f2b64620f4f54b75badddfac17e0a84660457233d0921b4c9d9ab31bd3323a8", + "https://deno.land/std@0.197.0/collections/min_with.ts": "ecca4ba5da922fb4b1be515949c59f287e580166985f2b12ee711f768004b33f", + "https://deno.land/std@0.197.0/collections/mod.ts": "05648f56cbc0383f2c3180ee60a7c99bbd637232e98e19b97a5d61d1f3043b8a", + "https://deno.land/std@0.197.0/collections/partition.ts": "a1fb2a21a5000c7f0006b3dbe8d603324325bb579764f4ff42fa0a06336c6d13", + "https://deno.land/std@0.197.0/collections/partition_entries.ts": "7a30b99b64a31af8e9d6da24239f7778b1cff9cd2f240f41aa2f535d647d54f9", + "https://deno.land/std@0.197.0/collections/permutations.ts": "3e3a9d7a8cc806a9ce9ae8fb0c7f3d37d884b3c7429fcaca4bc5d177d92d8217", + "https://deno.land/std@0.197.0/collections/reduce_groups.ts": "31e2f977cb3597f98ded2a56e3b5c122d4983ac8c79de184957d17d62ee0b2f1", + "https://deno.land/std@0.197.0/collections/running_reduce.ts": "e25f165456b946e7689a27e0e7fbb2f53d8189ef7cd0937b903e737bd37bacc6", + "https://deno.land/std@0.197.0/collections/sample.ts": "6330fb6567573b252a2ed9da475e54c4b98ca06e6266d34e5f0392095020c86b", + "https://deno.land/std@0.197.0/collections/sliding_windows.ts": "d1c8d18d86b8f02b407379e8681881880b7e3e173d152c5d8dc89d458031b740", + "https://deno.land/std@0.197.0/collections/sort_by.ts": "1207755af756a5da04bebff39146c93cbe54f7870a5d67cf6922e871d96a01d5", + "https://deno.land/std@0.197.0/collections/sum_of.ts": "76db7f04a66bdfa4911c4a592b868c854c690d6f0f83140c315f70caf742c761", + "https://deno.land/std@0.197.0/collections/take_last_while.ts": "9adcfe94a3dd56fbd55951099a121267aa42d8ccbc8152ed3aa210f86c74ca57", + "https://deno.land/std@0.197.0/collections/take_while.ts": "cd9a494d56e6a0a9c3f1e1123d1370633f0d4c6525ccf8aa3bd00b006742bddf", + "https://deno.land/std@0.197.0/collections/union.ts": "0577babef67dd38dc584accbcf75605318346046fe4ff476f0f6e2a1b1092391", + "https://deno.land/std@0.197.0/collections/unzip.ts": "3d3469c616b04200ff6a883e0d6dc82cac4c205aa65742e375afb8816be75b35", + "https://deno.land/std@0.197.0/collections/without_all.ts": "1e3cccb1ed0659455b473c0766d9414b7710d8cef48862c899f445178f66b779", + "https://deno.land/std@0.197.0/collections/zip.ts": "82710f115f1ed61bd0ce7b8ba0b38ecc8b38f7687ba3a768ee59d24e5efb1aad", + "https://deno.land/std@0.224.0/datetime/_date_time_formatter.ts": "b810c4c0d8f7aafe765fb963efdbf704acceb0730f5b242a7ec46df37834307c", + "https://deno.land/std@0.224.0/datetime/constants.ts": "5df80a84e301da6db5793804122c034d2d090da31f1e1c5fdaa831fc70a45da7", + "https://deno.land/std@0.224.0/datetime/day_of_year.ts": "5e513e239bd473e86d6a869d154720e9b6b8c86e1f14330ed5bb97575d9fa07b", + "https://deno.land/std@0.224.0/datetime/difference.ts": "8da08374910cae7d316cd7669912b70465f36d4e159e2c9f525a441f91fa9c7a", + "https://deno.land/std@0.224.0/datetime/format.ts": "8a550406530533c4aba101d471695f7f96c0eae61c1346748708eb333290d5f8", + "https://deno.land/std@0.224.0/datetime/is_leap.ts": "77f6eb9a4b7ea2a2dedbfecf75fd0a3f8f0e1610f708480e692f401f29e72650", + "https://deno.land/std@0.224.0/datetime/mod.ts": "6a49163dd0c7986658ed425b3555da5312252e5f5156bced6ae7199e651aa83b", + "https://deno.land/std@0.224.0/datetime/parse.ts": "d6dea8b3e394908dfd016bbe4545f260da4032493294aee1d10a3cf194c191cb", + "https://deno.land/std@0.224.0/datetime/week_of_year.ts": "ecaff056953753db3dabc9a769f80ff7ea7be79ae1e34edb5e2bda336f685757", + "https://deno.land/std@0.53.0/fmt/colors.ts": "ec9d653672a9a3c7b6eafe53c5bc797364a2db2dcf766ab649c1155fea7a80b2", + "https://deno.land/std@0.98.0/async/deferred.ts": "624bef4b755b71394620508a0c234a93cb8020cbd1b04bfcdad41c174392cef6", + "https://deno.land/std@0.98.0/async/delay.ts": "9de1d8d07d1927767ab7f82434b883f3d8294fb19cad819691a2ad81a728cf3d", + "https://deno.land/std@0.98.0/async/mod.ts": "f24e4a94f9fb7de78e8345e9590e1bf23da28a212541970413a023094448031b", + "https://deno.land/std@0.98.0/async/mux_async_iterator.ts": "62abff3af9ff619e8f2adc96fc70d4ca020fa48a50c23c13f12d02ed2b760dbe", + "https://deno.land/std@0.98.0/async/pool.ts": "353ce4f91865da203a097aa6f33de8966340c91b6f4a055611c8c5d534afd12f", + "https://deno.land/std@0.98.0/async/tee.ts": "6b8f1322b6dd2396202cfbe9cde9cab158d1e962cfd9197b0a97c6657bee79ce", + "https://deno.land/x/oak@v12.6.0/application.ts": "3028d3f6fa5ee743de013881550d054372c11d83c45099c2d794033786d27008", + "https://deno.land/x/oak@v12.6.0/body.ts": "1899761b97fc9d776f3710b2637fb047ba29b968609afc6c0e5219b1108e703c", + "https://deno.land/x/oak@v12.6.0/buf_reader.ts": "26640736541598dbd9f2b84a9d0595756afff03c9ca55b66eef1911f7798b56d", + "https://deno.land/x/oak@v12.6.0/content_disposition.ts": "8b8c3cb2fba7138cd5b7f82fc3b5ea39b33db924a824b28261659db7e164621e", + "https://deno.land/x/oak@v12.6.0/context.ts": "895a2d40186b89c28ba3947bf08a9335f1a11fc33133f760082536b74c53d1ca", + "https://deno.land/x/oak@v12.6.0/deps.ts": "6c601cd79f39489ad37d87587ad0c6fe53d838946ffa5a3e7e9600e7a01dc6d9", + "https://deno.land/x/oak@v12.6.0/etag.ts": "32e47726b41698aefdd71faac5aaf2907c9bdd41ef18a7693863be4f8fee115d", + "https://deno.land/x/oak@v12.6.0/forwarded.ts": "e656f96a85574e2a6ee54dc35efc9f72d543c9ae0923760ef426ee7369eef01c", + "https://deno.land/x/oak@v12.6.0/headers.ts": "769fd042d34fbcd5667cbd745b5c65d335cc8430e822dbf1f87d65313cab4b47", + "https://deno.land/x/oak@v12.6.0/helpers.ts": "6b03c6a2be06ec775d54449e442a2bac234aa952948ca758356eab6dc87af618", + "https://deno.land/x/oak@v12.6.0/http_server_native.ts": "6232bed3d7bc17136d1a7f7685a6f8a79c943694463f71cf743eac58e75ec34c", + "https://deno.land/x/oak@v12.6.0/http_server_native_request.ts": "552b174b5e13e92de8897d5b6f716b1e5a53543115481d65a651a41e4ca29ec9", + "https://deno.land/x/oak@v12.6.0/isMediaType.ts": "62d638abcf837ece3a8f07a4b7ca59794135cb0d4b73194c7d5837efd4161005", + "https://deno.land/x/oak@v12.6.0/mediaTyper.ts": "042b853fc8e9c3f6c628dd389e03ef481552bf07242efc3f8a1af042102a6105", + "https://deno.land/x/oak@v12.6.0/middleware.ts": "c7f7a0424a6dd99a00e4b8d7d6e131efc0facc8dea781845d713b63df8ef1862", + "https://deno.land/x/oak@v12.6.0/middleware/proxy.ts": "6f2799cf60d926e7a8d13ff757a59d7f0f930407db7ee9b81e7c064138eb89ff", + "https://deno.land/x/oak@v12.6.0/mod.ts": "f6aa47ad1b6099470c9a884cccad9d3ac0fd242ba940896291ab76cd26cf554b", + "https://deno.land/x/oak@v12.6.0/multipart.ts": "1484e01b98f5135f2aa09f7d0ce1e7be39109bf9f045ac660e941619d04e3d29", + "https://deno.land/x/oak@v12.6.0/range.ts": "1ca15fc1ac21c650c34e6997a75af2af9d9d8eb6fe2d5d1dadeac3dfd4a9c152", + "https://deno.land/x/oak@v12.6.0/request.ts": "794e5e12c2d369c2b8a6c998be5ca96c6e0315cae73069b85bc537901811073c", + "https://deno.land/x/oak@v12.6.0/response.ts": "7172285e58947057f36d6536b52a9cdffbdb03616555fa8e94b87f469a105f8a", + "https://deno.land/x/oak@v12.6.0/router.ts": "0f53d6249f9e8f89f2522b2b810b9302d0f22593c184b16b24b03bf2b7d42ea1", + "https://deno.land/x/oak@v12.6.0/send.ts": "5ec49f106294593f468317a0c885da4f3274bf6d0fe9e16a7304391730b4f4fb", + "https://deno.land/x/oak@v12.6.0/structured_clone.ts": "c3888b14d1eec558345bfbf13d0993d59bd45aaa8588444e35dd558c3a921cd8", + "https://deno.land/x/oak@v12.6.0/testing.ts": "37d684d57bb8e5150fb5eb2677e66b04dcb422709cf2c5a74c1df94d52aa02e2", + "https://deno.land/x/oak@v12.6.0/util.ts": "0a3fdffb114859c2de84e1783efa3a544af4d2af8c6f08e0d25655de9d3e69bb", + "https://deno.land/x/oak_logger@1.0.0/mod.ts": "10253fd9517a71d3c0771fcf2d9bd9297874160794d470597afce4102b0d51b3", + "https://deno.land/x/path_to_regexp@v6.2.1/index.ts": "894060567837bae8fc9c5cbd4d0a05e9024672083d5883b525c031eea940e556", + "https://deno.land/x/retry@v2.0.0/deps.ts": "62bc07039958b86fcb32e739cc030358417ee1f21872d54c108787d7cea202dc", + "https://deno.land/x/retry@v2.0.0/misc.ts": "1576f1b578a8da8c9f3e21988a77cb66081bc04d73965e98f635e7107bd394ea", + "https://deno.land/x/retry@v2.0.0/mod.ts": "a02882eca91f62cda4871ff55b83e9b7ac7173fe7e37028e3ff24d5a58c723e4", + "https://deno.land/x/retry@v2.0.0/retry/decorator.ts": "d95aa83651c355be3c01290721cf1c1c121ef442031abd9b91b06486b9908978", + "https://deno.land/x/retry@v2.0.0/retry/options.ts": "b97a77a8dbad759602c92f3b08a2e99466844ff672e9cf52523c6f94cb47619b", + "https://deno.land/x/retry@v2.0.0/retry/retry.ts": "2d8de90696082287aa31ee7723d3fb9b7d8f10f06651aa38494364e69a371a6e", + "https://deno.land/x/retry@v2.0.0/retry/tooManyTries.ts": "e63db29ff25b32a7938da60112a7a0280b3dffa5d888b25d3225d417057af0fa", + "https://deno.land/x/retry@v2.0.0/retry/utils/options.ts": "ad21c186f419f29f4dde70631ef4cf47302fc63f99c9d259108cc4fc10b64c91", + "https://deno.land/x/retry@v2.0.0/retry/utils/tools.ts": "0873dd98867c3befa40777b4c39752443b7e4a357194719b3f7e52a6af3b96d5", + "https://deno.land/x/retry@v2.0.0/retry/utils/untilDefined/decorators.ts": "b0638e5d8f29ae91591fc0c22d59aaeca98875b25f27965f00dac8ac62827240", + "https://deno.land/x/retry@v2.0.0/retry/utils/untilDefined/retry.ts": "419ff8e8e70d3afd9c72e4c613da1a33f1226701fd38a0529f053848ed295169", + "https://deno.land/x/retry@v2.0.0/retry/utils/untilResponse/decorators.ts": "0696c8292acb316a78bcd228c141b2039fe2dcf3089ae5d93c65e1e2b0778240", + "https://deno.land/x/retry@v2.0.0/retry/utils/untilResponse/retry.ts": "756c9260656c1e506437d20a09c03d56a52c03deb4d90cf5ec04fe444a7c5a60", + "https://deno.land/x/retry@v2.0.0/retry/utils/untilTruthy/decorators.ts": "669d6abfd4efacd0e3404b869710a54fe14452b96af9a8a6a9277a06b4736083", + "https://deno.land/x/retry@v2.0.0/retry/utils/untilTruthy/retry.ts": "b9a84430b3116c973175a07e0fffed70db9ea5f6154ec4f56f267ec3df8e5e3d", + "https://deno.land/x/retry@v2.0.0/wait/decorators.ts": "8bdb7953fc3408369afc5bba03a1ac285a81fe1472a688418cf8d0391eeef7ff", + "https://deno.land/x/retry@v2.0.0/wait/options.ts": "159e50c401e5fbb5513aa25cffb703758d9618e80b8a40b6af2ba51d3153a19a", + "https://deno.land/x/retry@v2.0.0/wait/timeoutError.ts": "61b6b17e26cd16d7f612169185c8e4e63271673c15ad6b8bb4b20fbd9418b1fb", + "https://deno.land/x/retry@v2.0.0/wait/wait.ts": "508bd97fced6b35393c358339e9a89d3cef9497dd2d95beaed40a83fef7e3d4f", + "https://esm.sh/ts-pattern@5.0.3": "79b44b9cc426127a554fd38782475a321e67f72c3e87ce89fd002eb27e13dd33", + "https://esm.sh/v135/ts-pattern@5.0.3/denonext/ts-pattern.mjs": "e65268905deb0a5b572b141e5a0a0a42bb3bff0be37a5aa4990bc942e50d615d" } } diff --git a/deps.ts b/deps.ts index 6428008..167f6a0 100644 --- a/deps.ts +++ b/deps.ts @@ -1,10 +1,10 @@ export * as log from "https://deno.land/std@0.194.0/log/mod.ts"; export { getLogger } from "https://deno.land/std@0.194.0/log/mod.ts"; -export { ms } from "https://deno.land/x/ms@v0.1.0/ms.ts"; export * as oak from "https://deno.land/x/oak@v12.6.0/mod.ts"; export { default as oakLogger } from "https://deno.land/x/oak_logger@1.0.0/mod.ts"; export { match, P } from "https://esm.sh/ts-pattern@5.0.3"; export { sortBy } from "https://deno.land/std@0.197.0/collections/mod.ts"; +export { retry } from "https://deno.land/x/retry@v2.0.0/mod.ts"; export async function dotenvLoad() { await import("https://deno.land/std@0.194.0/dotenv/load.ts"); diff --git a/src/agent.ts b/src/agent.ts deleted file mode 100644 index 45aa4a4..0000000 --- a/src/agent.ts +++ /dev/null @@ -1,23 +0,0 @@ -import Api from "./api.ts"; - -export default class Agent { - #getter: () => Promise; - #refresh: number; - #currentPromise!: Promise; - - public constructor(getter: () => Promise, refreshMs: number) { - this.#getter = getter; - this.#refresh = refreshMs; - - const init = () => { - this.#currentPromise = getter(); - }; - - init(); - setInterval(init, refreshMs); - } - - public get(): Promise { - return this.#currentPromise; - } -} diff --git a/src/aggregate.ts b/src/aggregate.ts index 1a90461..33dee7e 100644 --- a/src/aggregate.ts +++ b/src/aggregate.ts @@ -83,9 +83,12 @@ async function getTestCaseData( ): Promise { const entries = await Promise.all( testCaseIdList.map(async (id): Promise<[number, TestCaseMeta][]> => { - await delay(50000); - const custom_fields = await api.getTestCaseCustomFields(id); - const map = customFieldsToMap(custom_fields); + const { deleted, customFields } = await api.getTestCaseOverview(id); + if (deleted) { + logger().debug({ msg: "ignore deleted test case", id }); + return []; + } + const map = customFieldsToMap(customFields); if (!map.has(CUSTOM_FIELD_SDK)) { logger().warning({ @@ -121,12 +124,6 @@ async function getTestCaseData( function customFieldsToMap( input: ApiTestCaseCustomFieldData[], ): Map { - if (!input) { - logger().warning({ - msg: `Missing test!` - }) - return new Map(null); - } else { const entries = input .map((x) => ({ id: x.customField.id, @@ -134,13 +131,7 @@ function customFieldsToMap( value: x.name, })) .map((x): [string, CustomFieldData] => [x.name, x]); - return new Map(entries); -} - -} - -function delay(ms: number) { - return new Promise( resolve => setTimeout(resolve, ms) ); + return new Map(entries); } function aggregateStories( @@ -216,4 +207,4 @@ function pickResults( map.has(item.testCaseId) ? map : map.set(item.testCaseId, item), new Map(), ); -} \ No newline at end of file +} diff --git a/src/api.ts b/src/api.ts index ff17f0b..3697563 100644 --- a/src/api.ts +++ b/src/api.ts @@ -1,6 +1,4 @@ -import { getLogger } from "../deps.ts"; - -const ENABLE_CACHE = false; +import { getLogger, retry } from "../deps.ts"; const logger = () => getLogger("api"); @@ -33,6 +31,12 @@ export interface ApiTestCaseCustomFieldData { export type ApiCustomFieldKind = "Story" | "Permission" | "SDK Test Id" | "SDK"; +export interface ApiTestCaseOverview { + deleted: boolean; + customFields: ApiTestCaseCustomFieldData[]; + // there are other fields too, ignore +} + export default class Api { #config: Config; @@ -58,58 +62,64 @@ export default class Api { content: ApiTestCaseResult[]; } - return cacheData("cache/test-results.json", () => { - const URL = `${this.baseUrl}/api/rs/testresult/__search?` + - new URLSearchParams({ - projectId: "1", - rql: - `not cf["SDK"] is null and createdDate > ${params.createdDateAfter.getTime()}`, - page: "0", - size: "999999", - sort: "created_date,DESC", - }); - logger().debug({ msg: "Request", URL }); - return fetch(URL, { - headers: this.commonHeaders(), - }) - .then((x) => x.json() as Promise) - .then((x) => x.content) - .then((result) => { - logger().debug({ msg: "Found test cases", result }); - return result; - }); - }); + const URL = `${this.baseUrl}/api/rs/testresult/__search?` + + new URLSearchParams({ + projectId: "1", + rql: + `not cf["SDK"] is null and createdDate > ${params.createdDateAfter.getTime()}`, + page: "0", + size: "999999", + sort: "created_date,DESC", + }); + logger().debug({ msg: "Request", URL }); + return fetch(URL, { + headers: this.commonHeaders(), + }) + .then((x) => x.json() as Promise) + .then((x) => x.content) + .then((result) => { + logger().debug({ msg: "Found test cases", result }); + return result; + }); } - public async getTestCaseCustomFields( + public async getTestCaseOverview( id: number, - ): Promise { - return cacheData(`cache/test-case-${id}.json`, () => { - logger().debug({ msg: "Loading test case custom fields", id }); - return fetch(`${this.baseUrl}/api/rs/testcase/${id}/cfv`, { - headers: this.commonHeaders(), - }) - .then ((x) => { - if (x.status !== 200) { - logger().error({ msg: "Failed to load test case custom fields", id, status: x.status }); - } - return x.text(); + ): Promise { + logger().debug({ msg: "Loading test case custom fields", id }); + + const result = await retry( + () => + fetch(`${this.baseUrl}/api/rs/testcase/${id}/overview`, { + headers: this.commonHeaders(), }) - .then((text) => { - try { - const json = JSON.parse(text); // Attempt to parse the text as JSON - logger().debug({ msg: "Test case custom fields", id, data: json }); - return json; - } catch (error) { - logger().error({ msg: "Invalid JSON response", id, error: error.message }); - return; - } - }) - .then((x) => { - logger().debug({ msg: "Test case custom fields", id, data: x }); - return x; - }); - }); + .then((x) => { + if (x.status !== 200) { + logger().error({ + msg: "Failed to load test case custom fields", + id, + status: x.status, + }); + } + return x.text(); + }) + .then((text) => { + try { + const json = JSON.parse(text) as ApiTestCaseOverview; // Attempt to parse the text as JSON + return json; + } catch (error) { + logger().error({ + msg: "Invalid JSON response", + id, + error: error.message, + }); + throw error; + } + }), + { maxTry: 3 }, + ); + + return result; } private commonHeaders() { @@ -119,18 +129,3 @@ export default class Api { }); } } - -async function cacheData(file: string, fn: () => Promise): Promise { - if (!ENABLE_CACHE) return fn(); - - try { - const content = await Deno.readTextFile(file).then((x) => JSON.parse(x)); - logger().debug({ msg: "Loaded cached", file }); - return content; - } catch { - const data = await fn(); - await Deno.writeTextFile(file, JSON.stringify(data)); - logger().debug({ msg: "Written cache", file }); - return data; - } -} \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index 5b63d15..c7a46a4 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,9 +1,8 @@ import Api from "./api.ts"; -import { getMatrix } from "./aggregate.ts"; +import { getMatrix, Matrix } from "./aggregate.ts"; import * as web from "./web.ts"; import { get as getConfig } from "./config.ts"; -import Agent from "./agent.ts"; -import { log, match, ms, P } from "../deps.ts"; +import { log, match, P } from "../deps.ts"; const CONFIG = { apiToken: getConfig("ALLURE_API_TOKEN"), @@ -41,22 +40,36 @@ const api = new Api({ baseUrl: CONFIG.allureBaseUrl, }); -const agent = new Agent(async () => { - try { - log.info("Getting matrix"); - const data = await getMatrix(api); - log.info("Getting matrix complete"); - return data; - } catch (err) { - log.error("Getting matrix failed"); - console.error(err); - throw err; +// 24 hours +const CACHE_TTL = 60_000 * 24; + +class State { + #data: null | { matrix: Matrix; lastUpdated: Date } = null; + #promise: null | Promise = null; + + getMatrix(): Promise { + if (this.#promise) return this.#promise; + const now = Date.now(); + if (!this.#data || this.#data.lastUpdated.getTime() + CACHE_TTL < now) { + log.debug("No data/cache is stale, reloading matrix"); + this.#promise = getMatrix(api).then((matrix) => { + this.#data = { matrix, lastUpdated: new Date() }; + return matrix; + }).finally(() => { + this.#promise = null; + }); + return this.#promise; + } + log.debug("Using cached data"); + return Promise.resolve(this.#data.matrix); } -}, ms("2h") as number); +} + +const state = new State(); await web.run({ port: CONFIG.port, provider: { - getMatrix: () => agent.get(), + getMatrix: () => state.getMatrix(), }, }); From 761334a186278dfdc03e2fabaa74f4afeb6422e8 Mon Sep 17 00:00:00 2001 From: quacumque <43530070+0x009922@users.noreply.github.com> Date: Tue, 23 Jul 2024 11:14:01 +0900 Subject: [PATCH 2/6] docs: add livemd --- README.md | 4 + allure_compat_matrix.livemd | 157 ++++++++++++++++++++++++++++++++++++ 2 files changed, 161 insertions(+) create mode 100644 allure_compat_matrix.livemd diff --git a/README.md b/README.md index f0ec884..0afcd92 100644 --- a/README.md +++ b/README.md @@ -52,3 +52,7 @@ If needed remove deno.lock first ```bash deno cache --lock=deno.lock deps.ts ``` + +## Prototyping + +There is [`allure_compat_matrix.livemd`](./allure_compat_matrix.livemd) - a [Livebook](https://livebook.dev/) notebook used for prototyping of the data flow. It can be used as a reference and as a tool for further updates. Please refer to the Livebook documentation on how to use it if you want. diff --git a/allure_compat_matrix.livemd b/allure_compat_matrix.livemd new file mode 100644 index 0000000..d7f9144 --- /dev/null +++ b/allure_compat_matrix.livemd @@ -0,0 +1,157 @@ +# Allure, Compat Matrix + +```elixir +Mix.install([ + {:req, "~> 0.5.4"}, + {:kino, "~> 0.13.2"} +]) +``` + +## Buildin the matrix + +Make sure to define `LB_ALLURE_API_TOKEN` in the environment. + +```elixir +api_url = "https://soramitsu.testops.cloud" +api_token = System.get_env("LB_ALLURE_API_TOKEN") + +create_date_after = + Kino.Input.utc_datetime("create date after (UTC)", + default: DateTime.utc_now() |> DateTime.add(-365, :day) + ) +``` + +```elixir +create_date_after = + Kino.Input.read(create_date_after) |> DateTime.from_naive!("Etc/UTC") |> DateTime.to_unix() + +headers = %{"Authorization" => "Api-Token #{api_token}"} + +response = + Req.get!("#{api_url}/api/rs/testresult/__search?", + params: [ + projectId: 1, + rql: "not cf[\"SDK\"] is null and createdDate > #{create_date_after}", + page: 0, + size: 999_999, + sort: "created_date,DESC" + ], + headers: headers + ) + + +%{"content" => content} = response.body +``` + +```elixir +require Logger + +meta = + content + |> Stream.map(& &1["testCaseId"]) + |> Enum.into(MapSet.new()) + |> Task.async_stream(fn id -> + response = Req.get!("#{api_url}/api/rs/testcase/#{id}/overview", headers: headers) + Logger.debug("Loaded overview of #{id}") + + case response.body do + %{"deleted" => true} -> + Logger.debug("Test case #{id} is deleted, ignoring") + nil + + %{"customFields" => custom_fields} -> + custom_fields = + custom_fields + |> Enum.map(fn %{"customField" => %{"id" => id, "name" => name}, "name" => value} -> + {name, %{id: id, value: value}} + end) + |> Enum.into(%{}) + + case custom_fields do + %{"SDK" => sdk, "Story" => story} -> + {id, %{sdk: sdk, story: story}} + + bad -> + Logger.warning( + "test case #{id} doesn't have SDK or Story custom fields: #{inspect(bad)}" + ) + + nil + end + + unknown -> + Logger.warning("response not recognised: #{inspect(unknown)}") + nil + end + end) + |> Stream.map(fn {:ok, data} -> data end) + |> Stream.reject(&(&1 == nil)) + |> Enum.into(%{}) +``` + +```elixir +results_filtered = + content + |> Stream.map(&{&1["testCaseId"], &1}) + |> Enum.dedup_by(&elem(&1, 0)) + |> Enum.into(%{}) +``` + +```elixir +stories = + meta + |> Map.to_list() + |> Enum.reduce(%{}, fn {test_case_id, %{story: story, sdk: sdk}}, acc -> + result = Map.fetch!(results_filtered, test_case_id) + + Map.update(acc, story.value, %{}, fn story_map -> + Map.update(story_map, sdk.value, [], fn list -> [result["status"] | list] end) + end) + end) +``` + +```elixir +sdks = + stories + |> Map.values() + |> Stream.flat_map(&Map.keys/1) + |> Enum.into(MapSet.new()) + |> MapSet.to_list() +``` + +```elixir +matrix = + stories + |> Enum.map(fn {story, story_sdk_results} -> + results = + sdks + |> Stream.map(&Map.get(story_sdk_results, &1)) + |> Enum.map(fn + nil -> :no_data + statuses -> if Enum.all?(statuses, &(&1 == "passed")), do: :ok, else: :failed + end) + + %{name: story, results: results} + end) + +%{ + sdks: sdks, + stories: matrix +} +``` + +```elixir +matrix +|> Enum.map(fn %{name: name, results: results} -> + %{name: name} + |> Map.merge( + sdks + |> Enum.with_index() + |> Enum.map(fn {x, i} -> {x, Enum.at(results, i)} end) + |> Enum.into(%{}) + ) +end) +|> Kino.DataTable.new() +``` + + From c9c906769abbaa22b86655f48b662ffe13a9c5b3 Mon Sep 17 00:00:00 2001 From: quacumque <43530070+0x009922@users.noreply.github.com> Date: Tue, 23 Jul 2024 17:31:09 +0900 Subject: [PATCH 3/6] fix: 24 hours, not minutes --- src/main.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.ts b/src/main.ts index c7a46a4..aa202d8 100644 --- a/src/main.ts +++ b/src/main.ts @@ -41,7 +41,7 @@ const api = new Api({ }); // 24 hours -const CACHE_TTL = 60_000 * 24; +const CACHE_TTL = 60_000 * 60 * 24; class State { #data: null | { matrix: Matrix; lastUpdated: Date } = null; From fcfbe7b7fa3fd8ff03eccee66a70df4a493fb5ef Mon Sep 17 00:00:00 2001 From: quacumque <43530070+0x009922@users.noreply.github.com> Date: Wed, 24 Jul 2024 10:37:28 +0900 Subject: [PATCH 4/6] feat: new cache strategy - load data on init - always return latest successful data - update data in background on each request - all without data races! --- src/main.ts | 37 ++++++++--------------------- src/util.spec.ts | 60 ++++++++++++++++++++++++++++++++++++++++++++++++ src/util.ts | 59 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 128 insertions(+), 28 deletions(-) create mode 100644 src/util.spec.ts create mode 100644 src/util.ts diff --git a/src/main.ts b/src/main.ts index aa202d8..79d3205 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,8 +1,9 @@ import Api from "./api.ts"; -import { getMatrix, Matrix } from "./aggregate.ts"; +import { getMatrix } from "./aggregate.ts"; import * as web from "./web.ts"; import { get as getConfig } from "./config.ts"; import { log, match, P } from "../deps.ts"; +import { useFreshData } from "./util.ts"; const CONFIG = { apiToken: getConfig("ALLURE_API_TOKEN"), @@ -40,36 +41,16 @@ const api = new Api({ baseUrl: CONFIG.allureBaseUrl, }); -// 24 hours -const CACHE_TTL = 60_000 * 60 * 24; - -class State { - #data: null | { matrix: Matrix; lastUpdated: Date } = null; - #promise: null | Promise = null; - - getMatrix(): Promise { - if (this.#promise) return this.#promise; - const now = Date.now(); - if (!this.#data || this.#data.lastUpdated.getTime() + CACHE_TTL < now) { - log.debug("No data/cache is stale, reloading matrix"); - this.#promise = getMatrix(api).then((matrix) => { - this.#data = { matrix, lastUpdated: new Date() }; - return matrix; - }).finally(() => { - this.#promise = null; - }); - return this.#promise; - } - log.debug("Using cached data"); - return Promise.resolve(this.#data.matrix); - } -} - -const state = new State(); +const { get: getMatrixWithCache } = useFreshData(async () => { + log.info("Getting matrix"); + const data = await getMatrix(api); + log.info("Matrix is ready"); + return data; +}); await web.run({ port: CONFIG.port, provider: { - getMatrix: () => state.getMatrix(), + getMatrix: getMatrixWithCache, }, }); diff --git a/src/util.spec.ts b/src/util.spec.ts new file mode 100644 index 0000000..87a91db --- /dev/null +++ b/src/util.spec.ts @@ -0,0 +1,60 @@ +// Doesn't work with Deno! +// In order to run tests, install vitest globally using some node.js based package manager and then run `vitest` + +import { describe, expect, test, vi } from "vitest"; +import { useFreshData } from "./util.ts"; + +describe("useFreshData", () => { + test("fetches data before first get", async () => { + let called = false; + + useFreshData(() => { + called = true; + return Promise.resolve(null); + }); + + await expect.poll(() => called).toBe(true); + }); + + test("initial call fails, then succeeds", async () => { + const data = vi.fn().mockRejectedValueOnce("foo").mockResolvedValueOnce( + "ok", + ); + + const { get } = useFreshData(data); + + expect(() => get()).rejects.toThrow("foo"); + await expect.poll(() => get()).toBe("ok"); + }); + + test("initially ok, then fails, then ok", async () => { + const data = vi.fn().mockResolvedValueOnce(1).mockRejectedValueOnce("foo") + .mockResolvedValueOnce(2); + + const { get } = useFreshData(data); + + expect(await get()).toBe(1); + // waitUntil disallows errors + vi.waitUntil(async () => { + const value = await get(); + return value === 2; + }); + }); + + test("updates data 5 times in a row", async () => { + const { get } = useFreshData( + vi.fn() + .mockResolvedValueOnce(1) + .mockResolvedValueOnce(2) + .mockResolvedValueOnce(3) + .mockResolvedValueOnce(4) + .mockResolvedValueOnce(5), + ); + + expect(await get()).toBe(1); + expect(await get()).toBe(1); + expect(await get()).toBe(2); + expect(await get()).toBe(3); + expect(await get()).toBe(4); + }); +}); diff --git a/src/util.ts b/src/util.ts new file mode 100644 index 0000000..77ecdc9 --- /dev/null +++ b/src/util.ts @@ -0,0 +1,59 @@ +export function useFreshData( + fn: () => Promise, +): { get: () => Promise } { + let state: { data: null | { some: T }; promise: null | Promise } = { + data: null, + promise: null, + }; + + function createPromise(): Promise { + const promise = fn().then((data) => { + if (state.promise === promise) { + state.data = { some: data }; + state.promise = null; + } + return data; + }).catch((err) => { + if (state.promise === promise) { + state.promise = null; + } + throw err; + }); + return promise; + } + + async function get(): Promise { + if (state.data && state.promise) { + return (state.data.some); + } + if (state.data) { + const promise = createPromise(); + promise.catch((err) => { + console.error(err); + }); + state.promise = promise; + return (state.data.some); + } + if (state.promise) { + const data = await state.promise; + state = { data: { some: data }, promise: null }; + return data; + } + const promise = createPromise(); + state = { data: null, promise }; + try { + const data = await promise; + state = { data: { some: data }, promise: null }; + return data; + } catch (err) { + state.promise = null; + throw err; + } + } + + get().catch((err) => { + console.error(err); + }); + + return { get }; +} From 2eab53f8756f51fadf2c2b2a057515c8cf61a5e7 Mon Sep 17 00:00:00 2001 From: quacumque <43530070+0x009922@users.noreply.github.com> Date: Wed, 24 Jul 2024 10:37:34 +0900 Subject: [PATCH 5/6] docs: format --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0afcd92..b066655 100644 --- a/README.md +++ b/README.md @@ -55,4 +55,7 @@ deno cache --lock=deno.lock deps.ts ## Prototyping -There is [`allure_compat_matrix.livemd`](./allure_compat_matrix.livemd) - a [Livebook](https://livebook.dev/) notebook used for prototyping of the data flow. It can be used as a reference and as a tool for further updates. Please refer to the Livebook documentation on how to use it if you want. +There is [`allure_compat_matrix.livemd`](./allure_compat_matrix.livemd) - a +[Livebook](https://livebook.dev/) notebook used for prototyping of the data +flow. It can be used as a reference and as a tool for further updates. Please +refer to the Livebook documentation on how to use it if you want. From 8b68a23515c3a8948c1efa61f6f0ee28ee8a7bcb Mon Sep 17 00:00:00 2001 From: Ramil Mustafin Date: Wed, 24 Jul 2024 12:48:09 +0300 Subject: [PATCH 6/6] ci: update docker version --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index e890a2a..9276be2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM denoland/deno:alpine-1.35.2 +FROM denoland/deno:alpine-1.45.3 WORKDIR /app COPY deps.ts deno.lock ./ RUN deno cache --lock=deno.lock deps.ts