From 0525524595aceba8b2dde787d9b5191e3018572b Mon Sep 17 00:00:00 2001 From: Apisit Ritruengroj Date: Thu, 13 Nov 2025 20:53:40 +0700 Subject: [PATCH 01/40] initial --- .../tests/api_tests/utils/signed_doc.py | 46 +++++++++++++++++-- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/catalyst-gateway/tests/api_tests/utils/signed_doc.py b/catalyst-gateway/tests/api_tests/utils/signed_doc.py index b55aef15369..a79cacadefd 100644 --- a/catalyst-gateway/tests/api_tests/utils/signed_doc.py +++ b/catalyst-gateway/tests/api_tests/utils/signed_doc.py @@ -60,6 +60,31 @@ def build_and_sign( ) +DOC_TYPE = { + "brand_parameters": "3e4808cc-c86e-467b-9702-d60baa9d1fca", + "brand_parameters_form_template": "fd3c1735-80b1-4eea-8d63-5f436d97ea31", + "campaign_parameters": "0110ea96-a555-47ce-8408-36efe6ed6f7c", + "campaign_parameters_form_template": "7e8f5fa2-44ce-49c8-bfd5-02af42c179a3", + "category_parameters": "48c20109-362a-4d32-9bba-e0a9cf8b45be", + "category_parameters_form_template": "65b1e8b0-51f1-46a5-9970-72cdf26884be", + "comment_moderation_action": "84a4b502-3b7e-47fd-84e4-6fee08794bd7", + "contest_delegation": "764f17fb-cc50-4979-b14a-b213dbac5994", + "contest_parameters": "788ff4c6-d65a-451f-bb33-575fe056b411", + "contest_parameters_form_template": "08a1e16d-354d-4f64-8812-4692924b113b", + "presentation_template": "cb99b9bd-681a-49d8-9836-89107c02e8ef", + "proposal": "7808d2ba-d511-40af-84e8-c0d1625fdfdc", + "proposal_comment": "b679ded3-0e7c-41ba-89f8-da62a17898ea", + "proposal_comment_form_template": "0b8424d4-ebfd-46e3-9577-1775a69d290c", + "proposal_form_template": "0ce8ab38-9258-4fbc-a62e-7faa6e58318f", + "proposal_moderation_action": "a552451a-8e5b-409d-83a0-21eac26bbf8c", + "proposal_submission_action": "5e60e623-ad02-4a1b-a1ac-406db978ee48", + "rep_nomination": "bf9abd97-5d1f-4429-8e80-740fea371a9c", + "rep_nomination_form_template": "431561a5-9c2b-4de1-8e0d-78eb4887e35d", + "rep_profile": "0f2c86a2-ffda-40b0-ad38-23709e1c10b3", + "rep_profile_form_template": "564cbea3-44d3-4303-b75a-d9fdda7e5a80", +} + + BRAND_ID = "0199e71b-401e-7160-9139-a398c4d7b8fa" PROPOSAL_FORM_TEMPLATE_ID = "0199e71b-4025-7323-bc4a-d39e35762521" @@ -67,7 +92,7 @@ def build_and_sign( # return a Proposal document which is already published to the cat-gateway and the corresponding RoleID @pytest.fixture def proposal_doc_factory(rbac_chain_factory): - def __proposal_doc_factory() -> tuple[SignedDocument, RoleID]: + def __factory__() -> tuple[SignedDocument, RoleID]: role_id = RoleID.PROPOSER rbac_chain = rbac_chain_factory() proposal_doc_id = uuid_v7.uuid_v7() @@ -75,7 +100,7 @@ def __proposal_doc_factory() -> tuple[SignedDocument, RoleID]: "id": proposal_doc_id, "ver": proposal_doc_id, # Proposal document type - "type": "7808d2ba-d511-40af-84e8-c0d1625fdfdc", + "type": DOC_TYPE["proposal"], "content-type": "application/json", "content-encoding": "br", # referenced to the defined proposal template id, comes from the 'templates/data.rs' file @@ -104,7 +129,22 @@ def __proposal_doc_factory() -> tuple[SignedDocument, RoleID]: return doc, role_id - return __proposal_doc_factory + return __factory__ + + +@pytest.fixture +def proposal_form_template_doc_factory(rbac_chain_factory): + None + + +@pytest.fixture +def category_doc_factory(rbac_chain_factory): + None + + +@pytest.fixture +def category_form_template_factory(rbac_chain_factory): + None def build_signed_doc( From da47677d0be49c17a1868d973bd7022dd57efe2d Mon Sep 17 00:00:00 2001 From: Apisit Ritruengroj Date: Thu, 13 Nov 2025 22:13:12 +0700 Subject: [PATCH 02/40] feat: tmp setup --- .../tests/api_tests/utils/signed_doc.py | 170 ++++++++++++++---- 1 file changed, 133 insertions(+), 37 deletions(-) diff --git a/catalyst-gateway/tests/api_tests/utils/signed_doc.py b/catalyst-gateway/tests/api_tests/utils/signed_doc.py index a79cacadefd..0950f9c864b 100644 --- a/catalyst-gateway/tests/api_tests/utils/signed_doc.py +++ b/catalyst-gateway/tests/api_tests/utils/signed_doc.py @@ -11,6 +11,31 @@ from tempfile import NamedTemporaryFile +DOC_TYPE = { + "brand_parameters": "3e4808cc-c86e-467b-9702-d60baa9d1fca", + "brand_parameters_form_template": "fd3c1735-80b1-4eea-8d63-5f436d97ea31", + "campaign_parameters": "0110ea96-a555-47ce-8408-36efe6ed6f7c", + "campaign_parameters_form_template": "7e8f5fa2-44ce-49c8-bfd5-02af42c179a3", + "category_parameters": "48c20109-362a-4d32-9bba-e0a9cf8b45be", + "category_parameters_form_template": "65b1e8b0-51f1-46a5-9970-72cdf26884be", + "comment_moderation_action": "84a4b502-3b7e-47fd-84e4-6fee08794bd7", + "contest_delegation": "764f17fb-cc50-4979-b14a-b213dbac5994", + "contest_parameters": "788ff4c6-d65a-451f-bb33-575fe056b411", + "contest_parameters_form_template": "08a1e16d-354d-4f64-8812-4692924b113b", + "presentation_template": "cb99b9bd-681a-49d8-9836-89107c02e8ef", + "proposal": "7808d2ba-d511-40af-84e8-c0d1625fdfdc", + "proposal_comment": "b679ded3-0e7c-41ba-89f8-da62a17898ea", + "proposal_comment_form_template": "0b8424d4-ebfd-46e3-9577-1775a69d290c", + "proposal_form_template": "0ce8ab38-9258-4fbc-a62e-7faa6e58318f", + "proposal_moderation_action": "a552451a-8e5b-409d-83a0-21eac26bbf8c", + "proposal_submission_action": "5e60e623-ad02-4a1b-a1ac-406db978ee48", + "rep_nomination": "bf9abd97-5d1f-4429-8e80-740fea371a9c", + "rep_nomination_form_template": "431561a5-9c2b-4de1-8e0d-78eb4887e35d", + "rep_profile": "0f2c86a2-ffda-40b0-ad38-23709e1c10b3", + "rep_profile_form_template": "564cbea3-44d3-4303-b75a-d9fdda7e5a80", +} + + class SignedDocumentBase: def __init__(self, metadata: Dict[str, Any], content: Dict[str, Any]): self.metadata = metadata @@ -60,31 +85,6 @@ def build_and_sign( ) -DOC_TYPE = { - "brand_parameters": "3e4808cc-c86e-467b-9702-d60baa9d1fca", - "brand_parameters_form_template": "fd3c1735-80b1-4eea-8d63-5f436d97ea31", - "campaign_parameters": "0110ea96-a555-47ce-8408-36efe6ed6f7c", - "campaign_parameters_form_template": "7e8f5fa2-44ce-49c8-bfd5-02af42c179a3", - "category_parameters": "48c20109-362a-4d32-9bba-e0a9cf8b45be", - "category_parameters_form_template": "65b1e8b0-51f1-46a5-9970-72cdf26884be", - "comment_moderation_action": "84a4b502-3b7e-47fd-84e4-6fee08794bd7", - "contest_delegation": "764f17fb-cc50-4979-b14a-b213dbac5994", - "contest_parameters": "788ff4c6-d65a-451f-bb33-575fe056b411", - "contest_parameters_form_template": "08a1e16d-354d-4f64-8812-4692924b113b", - "presentation_template": "cb99b9bd-681a-49d8-9836-89107c02e8ef", - "proposal": "7808d2ba-d511-40af-84e8-c0d1625fdfdc", - "proposal_comment": "b679ded3-0e7c-41ba-89f8-da62a17898ea", - "proposal_comment_form_template": "0b8424d4-ebfd-46e3-9577-1775a69d290c", - "proposal_form_template": "0ce8ab38-9258-4fbc-a62e-7faa6e58318f", - "proposal_moderation_action": "a552451a-8e5b-409d-83a0-21eac26bbf8c", - "proposal_submission_action": "5e60e623-ad02-4a1b-a1ac-406db978ee48", - "rep_nomination": "bf9abd97-5d1f-4429-8e80-740fea371a9c", - "rep_nomination_form_template": "431561a5-9c2b-4de1-8e0d-78eb4887e35d", - "rep_profile": "0f2c86a2-ffda-40b0-ad38-23709e1c10b3", - "rep_profile_form_template": "564cbea3-44d3-4303-b75a-d9fdda7e5a80", -} - - BRAND_ID = "0199e71b-401e-7160-9139-a398c4d7b8fa" PROPOSAL_FORM_TEMPLATE_ID = "0199e71b-4025-7323-bc4a-d39e35762521" @@ -95,10 +95,10 @@ def proposal_doc_factory(rbac_chain_factory): def __factory__() -> tuple[SignedDocument, RoleID]: role_id = RoleID.PROPOSER rbac_chain = rbac_chain_factory() - proposal_doc_id = uuid_v7.uuid_v7() - proposal_metadata_json = { - "id": proposal_doc_id, - "ver": proposal_doc_id, + doc_id = uuid_v7.uuid_v7() + metadata = { + "id": doc_id, + "ver": doc_id, # Proposal document type "type": DOC_TYPE["proposal"], "content-type": "application/json", @@ -114,11 +114,12 @@ def __factory__() -> tuple[SignedDocument, RoleID]: # referenced to the defined category id, comes from the 'templates/data.rs' file "parameters": [{"id": BRAND_ID, "ver": BRAND_ID, "cid": "0x"}], } - with open("./test_data/signed_docs/proposal.json", "r") as proposal_json_file: - proposal_json = json.load(proposal_json_file) + with open("./test_data/signed_docs/proposal.json", "r") as json_file: + body = json.load(json_file) - doc = SignedDocument(proposal_metadata_json, proposal_json) + doc = SignedDocument(metadata, body) (cat_id, sk_hex) = rbac_chain.cat_id_for_role(role_id) + resp = document.put( data=doc.build_and_sign(cat_id, sk_hex), token=rbac_chain.auth_token(), @@ -134,17 +135,112 @@ def __factory__() -> tuple[SignedDocument, RoleID]: @pytest.fixture def proposal_form_template_doc_factory(rbac_chain_factory): - None + def __factory__() -> tuple[SignedDocument, RoleID]: + role_id = RoleID.PROPOSER + rbac_chain = rbac_chain_factory() + doc_id = uuid_v7.uuid_v7() + metadata = { + "id": doc_id, + "ver": doc_id, + "type": DOC_TYPE["proposal_form_template"], + "content-type": "application/json", + "content-encoding": "br", + # referenced to the defined category id, comes from the 'templates/data.rs' file + "parameters": [{"id": BRAND_ID, "ver": BRAND_ID, "cid": "0x"}], + } + with open("./test_data/signed_docs/proposal_form_template.json", "r") as json_file: + content = json.load(json_file) + + doc = SignedDocument(metadata, content) + (cat_id, sk_hex) = rbac_chain.cat_id_for_role(role_id) + + resp = document.put( + data=doc.build_and_sign(cat_id, sk_hex), + token=rbac_chain.auth_token(), + ) + assert ( + resp.status_code == 201 + ), f"Failed to publish document: {resp.status_code} - {resp.text}" + + return doc, role_id + + return __factory__ @pytest.fixture -def category_doc_factory(rbac_chain_factory): - None +def category_parameters_doc_factory(rbac_chain_factory): + def __factory__() -> tuple[SignedDocument, RoleID]: + role_id = RoleID.PROPOSER + rbac_chain = rbac_chain_factory() + doc_id = uuid_v7.uuid_v7() + metadata = { + "id": doc_id, + "ver": doc_id, + "type": DOC_TYPE["category_parameters"], + "content-type": "application/json", + "content-encoding": "br", + # referenced to the defined proposal template id, comes from the 'templates/data.rs' file + "template": [ + { + "id": PROPOSAL_FORM_TEMPLATE_ID, + "ver": PROPOSAL_FORM_TEMPLATE_ID, + "cid": "0x", + } + ], + # referenced to the defined category id, comes from the 'templates/data.rs' file + "parameters": [{"id": BRAND_ID, "ver": BRAND_ID, "cid": "0x"}], + } + with open("./test_data/signed_docs/category_parameters.json", "r") as json_file: + content = json.load(json_file) + + doc = SignedDocument(metadata, content) + (cat_id, sk_hex) = rbac_chain.cat_id_for_role(role_id) + + resp = document.put( + data=doc.build_and_sign(cat_id, sk_hex), + token=rbac_chain.auth_token(), + ) + assert ( + resp.status_code == 201 + ), f"Failed to publish document: {resp.status_code} - {resp.text}" + + return doc, role_id + + return __factory__ @pytest.fixture -def category_form_template_factory(rbac_chain_factory): - None +def category_parameters_form_template_doc_factory(rbac_chain_factory): + def __factory__() -> tuple[SignedDocument, RoleID]: + role_id = RoleID.PROPOSER + rbac_chain = rbac_chain_factory() + doc_id = uuid_v7.uuid_v7() + metadata = { + "id": doc_id, + "ver": doc_id, + "type": DOC_TYPE["category_parameters_form_template"], + "content-type": "application/json", + "content-encoding": "br", + # referenced to the defined category id, comes from the 'templates/data.rs' file + "parameters": [{"id": BRAND_ID, "ver": BRAND_ID, "cid": "0x"}], + } + with open("./test_data/signed_docs/category_parameters_form_template.json", "r") as json_file: + content = json.load(json_file) + + doc = SignedDocument(metadata, content) + (cat_id, sk_hex) = rbac_chain.cat_id_for_role(role_id) + + resp = document.put( + data=doc.build_and_sign(cat_id, sk_hex), + token=rbac_chain.auth_token(), + ) + assert ( + resp.status_code == 201 + ), f"Failed to publish document: {resp.status_code} - {resp.text}" + + return doc, role_id + + return __factory__ def build_signed_doc( From 18fa6bb9ae3d65bdfaff34bfbdeb97947179fb3d Mon Sep 17 00:00:00 2001 From: Apisit Ritruengroj Date: Mon, 17 Nov 2025 20:09:06 +0700 Subject: [PATCH 03/40] feat: prepare test data --- .../bin/src/db/event/signed_docs/tests/mod.rs | 5 +- .../event-db/seed/test_signed_documents.sql | 32 - .../integration/signed_doc/test_signed_doc.py | 2 +- .../signed_docs/category_parameters.json | 17 + .../category_parameters_form_template.json | 0 .../proposal_comment_form_template.json | 62 + .../signed_docs/proposal_form_template.json | 1497 +++++++++++++++++ 7 files changed, 1579 insertions(+), 36 deletions(-) delete mode 100644 catalyst-gateway/event-db/seed/test_signed_documents.sql create mode 100644 catalyst-gateway/tests/api_tests/test_data/signed_docs/category_parameters.json create mode 100644 catalyst-gateway/tests/api_tests/test_data/signed_docs/category_parameters_form_template.json create mode 100644 catalyst-gateway/tests/api_tests/test_data/signed_docs/proposal_comment_form_template.json create mode 100644 catalyst-gateway/tests/api_tests/test_data/signed_docs/proposal_form_template.json diff --git a/catalyst-gateway/bin/src/db/event/signed_docs/tests/mod.rs b/catalyst-gateway/bin/src/db/event/signed_docs/tests/mod.rs index 9aca89bb6c0..77aa8c1f9c6 100644 --- a/catalyst-gateway/bin/src/db/event/signed_docs/tests/mod.rs +++ b/catalyst-gateway/bin/src/db/event/signed_docs/tests/mod.rs @@ -189,9 +189,8 @@ async fn filter_by_type( } } -/// Pre-seeded documents `f14.sql`, `f15.sql`, `old_format_signed_doc.sql` and -/// `test_signed_documents.sql` -const PRE_SEED_DATA: usize = 57; +/// Pre-seeded documents `f14.sql`, `f15.sql`, `old_format_signed_doc.sql` +const PRE_SEED_DATA: usize = 55; async fn filter_all(docs: &[FullSignedDoc]) { let filter = DocsQueryFilter::all(); diff --git a/catalyst-gateway/event-db/seed/test_signed_documents.sql b/catalyst-gateway/event-db/seed/test_signed_documents.sql deleted file mode 100644 index cddea953a51..00000000000 --- a/catalyst-gateway/event-db/seed/test_signed_documents.sql +++ /dev/null @@ -1,32 +0,0 @@ --- Catalyst Signed Documents test data - -INSERT INTO signed_docs ( - id, - ver, - type, - authors, - metadata, - payload, - raw -) -VALUES --- Proposal Form Template document -( - '0199e71b-4025-7323-bc4a-d39e35762521', - '0199e71b-4025-7323-bc4a-d39e35762521', - '0ce8ab38-9258-4fbc-a62e-7faa6e58318f', - ARRAY[]::TEXT [], - '{"content-encoding":"br","content-type":"application/json","id":"0199e71b-4025-7323-bc4a-d39e35762521","type":"0ce8ab38-9258-4fbc-a62e-7faa6e58318f","ver":"0199e71b-4025-7323-bc4a-d39e35762521","parameters":[{"id":"0199e71b-401e-7160-9139-a398c4d7b8fa","ver":"0199e71b-401e-7160-9139-a398c4d7b8fa","cid":"0x"}]}', -- noqa: LT05 - '{}', - DECODE('d8628458aea603776170706c69636174696f6e2f736368656d612b6a736f6e6a706172616d65746572738183d825500199e71b401e71609139a398c4d7b8fad825500199e71b401e71609139a398c4d7b8faa163636964d82a4063766572d825500199e71b40257323bc4ad39e35762521626964d825500199e71b40257323bc4ad39e3576252170636f6e74656e742d656e636f64696e676262726474797065d825500ce8ab3892584fbca62e7faa6e58318fa0468b00807b7d038183584ba104584769642e636174616c7973743a2f2f63617264616e6f2f485562336e69537751656a4c64366857596179684150567a63694a2d7150625a4a6457534e7837715f33672f3130352f30a05840a7844bd49c79c4a7f2425a52bc16f1a853d6110342b4bd0f7a87098fc02519c5c9ec5e1fece64e9b2249dea17f61a8fd1ab30bbd6e04324a4d77d6ab0e2cd501', 'hex') -- noqa: LT05 -), --- Brand Parameters document -( - '0199e71b-401e-7160-9139-a398c4d7b8fa', - '0199e71b-401e-7160-9139-a398c4d7b8fa', - '3e4808cc-c86e-467b-9702-d60baa9d1fca', - ARRAY[]::TEXT [], - '{"content-encoding":"br","content-type":"application/json","id":"0199e71b-401e-7160-9139-a398c4d7b8fa","type":"3e4808cc-c86e-467b-9702-d60baa9d1fca","ver":"0199e71b-401e-7160-9139-a398c4d7b8fa","template":[{"id":"0199e71b-401d-7362-848b-a4090b1f3862","ver":"0199e71b-401d-7362-848b-a4090b1f3862","cid":"0x"}]}', -- noqa: LT05 - '{}', - DECODE('d8628458a5a670636f6e74656e742d656e636f64696e6762627203706170706c69636174696f6e2f6a736f6e626964d825500199e71b401e71609139a398c4d7b8fa6874656d706c6174658183d825500199e71b401d7362848ba4090b1f3862d825500199e71b401d7362848ba4090b1f3862a163636964d82a406474797065d825503e4808ccc86e467b9702d60baa9d1fca63766572d825500199e71b401e71609139a398c4d7b8faa0468b00807b7d038183584ba104584769642e636174616c7973743a2f2f63617264616e6f2f6150766c4b387269455659757a69767470456f4469356f35494a37576733537643354f796e716750484d772f3130352f30a0584014bee2346bd6c78b7a443c17805d985c290a2aad53ff5bb12e05b6b165349365d9255af3a13fe7bfdf3afbf5f7011e2de48906d83b315b3ff71fb0b1fe7b1a07', 'hex') -- noqa: LT05 -); diff --git a/catalyst-gateway/tests/api_tests/integration/signed_doc/test_signed_doc.py b/catalyst-gateway/tests/api_tests/integration/signed_doc/test_signed_doc.py index 7eae3d847bd..0878cdbd8a0 100644 --- a/catalyst-gateway/tests/api_tests/integration/signed_doc/test_signed_doc.py +++ b/catalyst-gateway/tests/api_tests/integration/signed_doc/test_signed_doc.py @@ -10,8 +10,8 @@ @pytest.mark.preprod_indexing def test_document_put_and_get_endpoints(proposal_doc_factory, rbac_chain_factory): - (proposal_doc, role_id) = proposal_doc_factory() rbac_chain = rbac_chain_factory() + (proposal_doc, role_id) = proposal_doc_factory() (cat_id, sk_hex) = rbac_chain.cat_id_for_role(role_id) proposal_doc_id = proposal_doc.metadata["id"] diff --git a/catalyst-gateway/tests/api_tests/test_data/signed_docs/category_parameters.json b/catalyst-gateway/tests/api_tests/test_data/signed_docs/category_parameters.json new file mode 100644 index 00000000000..32ab2023fdd --- /dev/null +++ b/catalyst-gateway/tests/api_tests/test_data/signed_docs/category_parameters.json @@ -0,0 +1,17 @@ +[ + { + "content-encoding": "br", + "content-type": "application/json", + "id": "0199802c-21b4-7dc8-8537-7eae5ea4c4d3", + "parameters": [ + { + "cid": "0x", + "id": "0199802c-21b4-7d91-986d-0e913cd81391", + "ver": "0199802c-21b4-7d91-986d-0e913cd81391" + } + ], + "type": "48c20109-362a-4d32-9bba-e0a9cf8b45be", + "ver": "0199802c-21b4-7dc8-8537-7eae5ea4c4d3" + }, + {} +] \ No newline at end of file diff --git a/catalyst-gateway/tests/api_tests/test_data/signed_docs/category_parameters_form_template.json b/catalyst-gateway/tests/api_tests/test_data/signed_docs/category_parameters_form_template.json new file mode 100644 index 00000000000..e69de29bb2d diff --git a/catalyst-gateway/tests/api_tests/test_data/signed_docs/proposal_comment_form_template.json b/catalyst-gateway/tests/api_tests/test_data/signed_docs/proposal_comment_form_template.json new file mode 100644 index 00000000000..7cb1ee9a841 --- /dev/null +++ b/catalyst-gateway/tests/api_tests/test_data/signed_docs/proposal_comment_form_template.json @@ -0,0 +1,62 @@ +[ + { + "content-encoding": "br", + "content-type": "application/json", + "id": "0199802c-21b4-7b2c-aafd-0af557e8408c", + "parameters": [ + { + "cid": "0x", + "id": "0199802c-21b4-721f-aa1d-5123b006879e", + "ver": "0199802c-21b4-721f-aa1d-5123b006879e" + } + ], + "type": "0b8424d4-ebfd-46e3-9577-1775a69d290c", + "ver": "0199802c-21b4-7b2c-aafd-0af557e8408c" + }, + { + "$id": "https://cardano.org/schemas/catalyst/comments", + "$schema": "http://json-schema.org/draft-07/schema#", + "definitions": { + "multiLineTextEntry": { + "$comment": "UI - Multiline text entry without any markup or rich text capability.", + "pattern": "^[\\S\\s]+$", + "type": "string", + "x-note": "Enter multiple lines of plain text. You can use line breaks but no special formatting." + } + }, + "description": "Schema for comments on Catalyst proposals", + "maintainers": [ + { + "name": "Catalyst Team", + "url": "https://projectcatalyst.io/" + } + ], + "properties": { + "comment": { + "description": "The comments on the proposal", + "properties": { + "content": { + "$ref": "#/definitions/multiLineTextEntry", + "description": "The comment text content", + "maxLength": 2000, + "minLength": 1 + } + }, + "required": [ + "content" + ], + "type": "object" + } + }, + "required": [ + "comment" + ], + "title": "Proposal Comments Schema", + "type": "object", + "x-changelog": { + "2025-06-16": [ + "Updating maxLength for content text" + ] + } + } +] \ No newline at end of file diff --git a/catalyst-gateway/tests/api_tests/test_data/signed_docs/proposal_form_template.json b/catalyst-gateway/tests/api_tests/test_data/signed_docs/proposal_form_template.json new file mode 100644 index 00000000000..0bcecd4eff4 --- /dev/null +++ b/catalyst-gateway/tests/api_tests/test_data/signed_docs/proposal_form_template.json @@ -0,0 +1,1497 @@ +[ + { + "content-encoding": "br", + "content-type": "application/json", + "id": "0199802c-21b4-7d6c-aacd-54aa31fe1e4c", + "parameters": [ + { + "cid": "0x", + "id": "0199802c-21b4-7161-a16e-a77af492780f", + "ver": "0199802c-21b4-7161-a16e-a77af492780f" + } + ], + "type": "0ce8ab38-9258-4fbc-a62e-7faa6e58318f", + "ver": "0199802c-21b4-7d6c-aacd-54aa31fe1e4c" + }, + { + "$id": "https://raw.githubusercontent.com/input-output-hk/catalyst-voices/refs/heads/main/docs/src/architecture/08_concepts/document_templates/proposal/f15/0199802c-21b4-7d6c-aacd-54aa31fe1e4c.schema.json", + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "definitions": { + "agreementConfirmation": { + "$comment": "UI - A Boolean choice, defaults to `false` but its invalid if its not set to `true`.", + "const": true, + "default": false, + "format": "agreementConfirmation", + "type": "boolean", + "x-note": "Select Yes or No." + }, + "borderGroup": { + "$comment": "UI - Border Group for better UI rendering", + "type": "object", + "x-border-color": "#ff0000" + }, + "currency": { + "title": "Currency/Token amount", + "type": "integer" + }, + "dropDownSingleSelect": { + "$comment": "UI - Drop Down Selection of a single entry from the defined enum.", + "contentMediaType": "text/plain", + "format": "dropDownSingleSelect", + "pattern": "^.*$", + "type": "string", + "x-note": "Select one option from the dropdown menu. Only one choice is allowed." + }, + "durationInMonths": { + "$comment": "UI - A Duration represented in total months.", + "format": "datetime:duration:months", + "type": "integer", + "x-note": "Enter the duration of the proposal in months." + }, + "languageCode": { + "$comment": "UI - ISO 639-1 language code selection", + "default": "en", + "description": "Two-letter ISO 639-1 language code", + "enum": [ + "aa", + "ab", + "af", + "ak", + "am", + "ar", + "as", + "ay", + "az", + "ba", + "be", + "bg", + "bh", + "bi", + "bn", + "bo", + "br", + "bs", + "ca", + "ce", + "ch", + "co", + "cs", + "cu", + "cv", + "cy", + "da", + "de", + "dv", + "dz", + "ee", + "el", + "en", + "eo", + "es", + "et", + "eu", + "fa", + "ff", + "fi", + "fj", + "fo", + "fr", + "fy", + "ga", + "gd", + "gl", + "gn", + "gu", + "gv", + "ha", + "he", + "hi", + "ho", + "hr", + "ht", + "hu", + "hy", + "hz", + "ia", + "id", + "ie", + "ig", + "ii", + "ik", + "io", + "is", + "it", + "iu", + "ja", + "jv", + "ka", + "kg", + "ki", + "kj", + "kk", + "kl", + "km", + "kn", + "ko", + "kr", + "ks", + "ku", + "kv", + "kw", + "ky", + "la", + "lb", + "lg", + "li", + "ln", + "lo", + "lt", + "lu", + "lv", + "mg", + "mh", + "mi", + "mk", + "ml", + "mn", + "mr", + "ms", + "mt", + "my", + "na", + "nb", + "nd", + "ne", + "ng", + "nl", + "nn", + "no", + "nr", + "nv", + "ny", + "oc", + "oj", + "om", + "or", + "os", + "pa", + "pi", + "pl", + "ps", + "pt", + "qu", + "rm", + "rn", + "ro", + "ru", + "rw", + "sa", + "sc", + "sd", + "se", + "sg", + "si", + "sk", + "sl", + "sm", + "sn", + "so", + "sq", + "sr", + "ss", + "st", + "su", + "sv", + "sw", + "ta", + "te", + "tg", + "th", + "ti", + "tk", + "tl", + "tn", + "to", + "tr", + "ts", + "tt", + "tw", + "ty", + "ug", + "uk", + "ur", + "uz", + "ve", + "vi", + "vo", + "wa", + "wo", + "xh", + "yi", + "yo", + "za", + "zh", + "zu" + ], + "title": "Language Code", + "type": "string", + "x-note": "Select the ISO 639-1 two-letter code for the language. For example: ''en'' for English, ''es'' for Spanish, ''fr'' for French, etc." + }, + "multiLineTextEntry": { + "$comment": "UI - Multiline text entry without any markup or rich text capability.", + "contentMediaType": "text/plain", + "pattern": "^[\\S\\s]*$", + "type": "string", + "x-note": "Enter multiple lines of plain text. You can use line breaks but no special formatting." + }, + "multiLineTextEntryListMarkdown": { + "$comment": "UI - A Growable List of markdown formatted text fields.", + "default": [], + "format": "multiLineTextEntryListMarkdown", + "items": { + "$ref": "#/definitions/multiLineTextEntryMarkdown", + "maxLength": 10240 + }, + "type": "array", + "uniqueItems": true, + "x-note": "Add multiple markdown-formatted text entries. Each entry can include rich formatting and should be unique." + }, + "multiLineTextEntryMarkdown": { + "$comment": "UI - Multiline text entry with Markdown content.", + "contentMediaType": "text/markdown", + "pattern": "^[\\S\\s]*$", + "type": "string", + "x-note": "Use Markdown formatting for rich text. Available formatting:\n- Headers: # for h1, ## for h2, etc.\n- Lists: * or - for bullets, 1. for numbered\n- Emphasis: *italic* or **bold**\n- Links: [text](url)\n- Code: `inline` or ```block```" + }, + "multiSelect": { + "$comment": "UI - Multiselect from the given items.", + "format": "multiSelect", + "type": "array", + "uniqueItems": true, + "x-note": "Select multiple options from the dropdown menu. Multiple choices are allowed." + }, + "nestedQuestions": { + "$comment": "UI - The container for a nested question set.", + "additionalProperties": false, + "format": "nestedQuestions", + "type": "object", + "x-note": "Add multiple questions. Each question should be unique." + }, + "nestedQuestionsList": { + "$comment": "UI - A Growable List of Questions. The contents are an object, that can have any UI elements within.", + "default": [], + "format": "nestedQuestionsList", + "type": "array", + "uniqueItems": true, + "x-note": "Add multiple questions. Each question should be unique." + }, + "radioButtonSelect": { + "$comment": "UI - Radio Button Selection", + "format": "radioButtonSelect", + "type": "string", + "x-note": "Select one option from a list of radio buttons" + }, + "schemaReferenceNonUI": { + "$comment": "NOT UI: used to identify the kind of template document used.", + "format": "path", + "readOnly": true, + "type": "string" + }, + "section": { + "$comment": "UI - Logical Document Sub-Section Break.", + "additionalProperties": false, + "type": "object", + "x-note": "Subsections containing specific details about the proposal." + }, + "segment": { + "$comment": "UI - Logical Document Section Break.", + "additionalProperties": false, + "properties": { + "budget": { + "type": "object" + }, + "category_details": { + "type": "object" + }, + "category_questions": { + "type": "object" + }, + "consent_confirmation": { + "type": "object" + }, + "dependencies": { + "type": "object" + }, + "feasibility": { + "type": "object" + }, + "impact": { + "type": "object" + }, + "milestones": { + "type": "object" + }, + "ongoing_projects": { + "type": "object" + }, + "open_source": { + "type": "object" + }, + "problem": { + "type": "object" + }, + "proposer": { + "type": "object" + }, + "self_assessment": { + "type": "object" + }, + "self_assessment_checklist": { + "type": "object" + }, + "solution": { + "type": "object" + }, + "supportingLinks": { + "type": "object" + }, + "team": { + "type": "object" + }, + "theme": { + "type": "object" + }, + "time": { + "type": "object" + }, + "title": { + "type": "object" + }, + "translation": { + "type": "object" + }, + "value": { + "type": "object" + } + }, + "type": "object", + "x-note": "Major sections of the proposal. Each segment contains sections of information grouped together." + }, + "singleGroupedTagSelector": { + "$comment": "UI - A selector where a top level selection, gives a single choice from a list of tags.", + "additionalProperties": true, + "format": "singleGroupedTagSelector", + "type": "object", + "x-note": "Select one option from the dropdown menu. Only one choice is allowed." + }, + "singleLineHttpsURLEntry": { + "$comment": "UI - Single Line text entry for HTTPS Urls.", + "format": "uri", + "maxLength": 255, + "pattern": "^https://[^\\s]+$", + "type": "string", + "x-note": "Must start with ''https://'' and is followed by one or more non-whitespace characters, ending at the end of the string." + }, + "singleLineHttpsURLEntryList": { + "$comment": "UI - A Growable List of HTTPS URLs.", + "default": [], + "format": "singleLineHttpsURLEntryList", + "items": { + "$ref": "#/definitions/singleLineHttpsURLEntry", + "maxLength": 255 + }, + "type": "array", + "uniqueItems": true, + "x-note": "Enter multiple HTTPS URLs. Each URL should be unique and under 1024 characters." + }, + "singleLineTextEntry": { + "$comment": "UI - Single Line text entry without any markup or rich text capability.", + "contentMediaType": "text/plain", + "pattern": "^.*$", + "type": "string", + "x-note": "Enter a single line of text. No formatting, line breaks, or special characters are allowed." + }, + "singleLineTextEntryList": { + "$comment": "UI - A Growable List of single line text (no markup or richtext).", + "default": [], + "format": "singleLineTextEntryList", + "items": { + "$ref": "#/definitions/singleLineTextEntry", + "maxLength": 1024 + }, + "type": "array", + "uniqueItems": true, + "x-note": "Add multiple single-line text entries. Each entry should be unique and under 1024 characters." + }, + "tagGroup": { + "$comment": "UI - An individual group within a singleGroupedTagSelector.", + "format": "tagGroup", + "pattern": "^.*$", + "type": "string", + "x-note": "Select one option from the dropdown menu. Only one choice is allowed." + }, + "tagInput": { + "$comment": "UI - A tag-style input field for adding/removing items with visual tags", + "default": [], + "format": "tag-input", + "type": "array", + "uniqueItems": true, + "x-note": "Add or remove items that will be displayed as tags with remove buttons" + }, + "tagSelection": { + "$comment": "UI - An individual tag within the group of a singleGroupedTagSelector.", + "format": "tagSelection", + "pattern": "^.*$", + "type": "string", + "x-note": "Select one option from the dropdown menu. Only one choice is allowed." + }, + "tokenValueCardanoADA": { + "$comment": "UI - A Token Value denominated in Cardano ADA.", + "format": "token:cardano:ada", + "type": "integer", + "x-note": "Enter the amount of Cardano ADA to be used in the proposal." + }, + "yesNoChoice": { + "$comment": "UI - A Boolean choice, represented as a Yes/No selection. Yes = true.", + "default": false, + "format": "yesNoChoice", + "type": "boolean", + "x-note": "Select Yes or No." + } + }, + "description": "Schema for the F15 Midnight: Compact DApps", + "maintainers": [ + { + "name": "Catalyst Team", + "url": "https://projectcatalyst.io/" + } + ], + "properties": { + "$schema": { + "$ref": "#/definitions/schemaReferenceNonUI", + "const": "./0199802c-21b4-7d6c-aacd-54aa31fe1e4c.schema.json", + "default": "./0199802c-21b4-7d6c-aacd-54aa31fe1e4c.schema.json" + }, + "agreements": { + "$ref": "#/definitions/segment", + "properties": { + "consent_confirmation": { + "$ref": "#/definitions/section", + "properties": { + "terms_and_conditions": { + "$ref": "#/definitions/agreementConfirmation", + "description": "I confirm that I have read, understand and shall adhere to the [Project Catalyst Platform Terms of Use](https://docs.projectcatalyst.io/current-fund/fund-basics/project-catalyst-terms-and-conditions/project-catalyst-platform-terms-of-use), [Project Catalyst Platform Privacy Policy](https://docs.projectcatalyst.io/current-fund/fund-basics/project-catalyst-terms-and-conditions/project-catalyst-platform-privacy-policy), [Project Catalyst Terms and Conditions – Midnight Compact DApps Submissions](https://docs.projectcatalyst.io/current-fund/fund-basics/project-catalyst-terms-and-conditions/project-catalyst-terms-and-conditions-midnight-compact-dapps-submissions), [Fund Rules](https://docs.projectcatalyst.io/current-fund/fund-basics/fund-rules), [Privacy Policy](https://docs.projectcatalyst.io/current-fund/fund-basics/project-catalyst-terms-and-conditions/catalyst-fc-privacy-policy). I understand that providing accurate and truthful information is essential for my proposal to remain eligible to participate in the current Fund.", + "title": "I Agree" + } + }, + "required": [ + "terms_and_conditions" + ], + "title": "Consent & Confirmation", + "x-order": [ + "terms_and_conditions" + ] + }, + "ongoing_projects": { + "$ref": "#/definitions/section", + "properties": { + "has_ongoing_projects": { + "$ref": "#/definitions/yesNoChoice", + "description": "Do you have not yet completed projects?", + "title": "Have you previously submitted a project in Catalyst that was funded and is not yet completed?", + "x-placeholder": "Select if you or co-proposers have any ongoing projects" + }, + "projects": { + "$ref": "#/definitions/tagInput", + "description": "If you answered “yes” to the previous question, please select first how many there are in total and then list all IDs for any projects you are currently involved in that have not been completed yet.", + "items": { + "properties": { + "project_id": { + "description": "Seven digit project ID from Catalyst website", + "maxLength": 7, + "minLength": 7, + "pattern": "^[0-9]{7}$", + "title": "Project ID", + "type": "string" + } + }, + "required": [ + "project_id" + ], + "type": "object" + }, + "maxItems": 15, + "minItems": 0, + "title": "Project ID''s", + "x-guidance": "You can find a seven digit project ID on each original proposal page listed on Catalyst website. [Search for yours here.](https://projectcatalyst.io/search)", + "x-subsection": false + } + }, + "required": [ + "has_ongoing_projects", + "projects" + ], + "title": "Ongoing Projects", + "x-guidance": "Important Notice\n\nPlease review [Fund Rules](https://docs.projectcatalyst.io/current-fund/fund-basics/fund-rules) carefully, as eligibility to participate may be affected by the status of your ongoing projects. Providing accurate information is mandatory, failure to do so will result in your proposal being deemed ineligible in the current Fund.", + "x-order": [ + "has_ongoing_projects", + "projects" + ] + } + }, + "title": "Required Acknowledgements", + "x-icon": "double_check", + "x-order": [ + "ongoing_projects", + "consent_confirmation" + ] + }, + "campaign_category": { + "$ref": "#/definitions/segment", + "description": "Determine the eligibility of the proposal for this category", + "properties": { + "category_details": { + "$ref": "#/definitions/section", + "properties": { + "details": { + "title": "Selected Category", + "type": "object" + } + }, + "title": "Selected Category" + }, + "category_questions": { + "$ref": "#/definitions/section", + "description": "Answer the following questions to determine the eligibility of your proposal.", + "properties": { + "build_goal": { + "$ref": "#/definitions/multiLineTextEntryMarkdown", + "description": "Please provide your rationale.", + "maxLength": 10240, + "minLength": 200, + "title": "What exactly will you build? List the Compact contract(s) and key functions/proofs, the demo UI flow, Lace (Midnight) wallet integration, and your basic test plan.", + "x-guidance": "Usefulness (Pattern & Necessity)\n\n- Clear mapping from problem to data-protection pattern (e.g., selective disclosure for KYC, private votes, confidential asset ops).\n- Why this requires Midnight + Compact versus a public ledger.\n- Novelty relative to existing examples; what new building block it adds.", + "x-placeholder": "Please provide your rationale." + }, + "dapp_purpose": { + "$ref": "#/definitions/multiLineTextEntryMarkdown", + "description": "Please provide your rationale.", + "maxLength": 10240, + "minLength": 200, + "title": "What is useful about your DApp within one of the specified industry or enterprise verticals?", + "x-guidance": "General Guidance\n\n- Be concise; prefer bullets over prose.\n- This is a code-first PoC, not a company pitch. No pitch decks/marketing.\n- Include concrete details (e.g., file paths, API/entrypoints, test commands).\n- If helpful, include a simple architecture diagram or sequence.\n- Keep within 3 months and 3 milestones; each milestone needs deliverables + acceptance criteria + evidence.\n- Use correct nomenclature (Compact ≠ TypeScript; privacy-enhancing/data protection).", + "x-placeholder": "Please provide your rationale." + }, + "developer_reuse": { + "$ref": "#/definitions/multiLineTextEntryMarkdown", + "description": "Please provide your rationale.", + "maxLength": 10240, + "minLength": 200, + "title": "How will other developers learn from and reuse your repo? Describe repo structure, README contents, docs/tutorials, test instructions, and extension points. Which developer personas benefit, and how will you gauge impact (forks, stars, issues, remixes)?", + "x-guidance": "Developer Value\n\n- Identified developer personas (e.g., DApp devs, zk-curious, integrators) and how this example helps them.\n- Potential to attract new builders (clarity, reusability) and measurable signals (forks, stars, issues, downstream examples)", + "x-placeholder": "Please provide your rationale." + } + }, + "required": [ + "dapp_purpose", + "build_goal", + "developer_reuse" + ], + "title": "Category Questions", + "x-guidance": "General Guidance\n\nIn consideration of the Reviewers assessing your proposal, be concise and use bulleted lists where possible. ", + "x-order": [ + "dapp_purpose", + "build_goal", + "developer_reuse" + ] + } + }, + "title": "Campaign Category", + "x-icon": "top-bar", + "x-order": [ + "category_details", + "category_questions" + ] + }, + "details": { + "$ref": "#/definitions/segment", + "properties": { + "feasibility": { + "$ref": "#/definitions/section", + "properties": { + "feasibility": { + "$ref": "#/definitions/multiLineTextEntryMarkdown", + "description": "What is your capability to deliver your project with high levels of trust and accountability? How do you intend to validate if your approach is feasible?", + "maxLength": 10240, + "minLength": 200, + "title": "What is your capability to deliver your project with high levels of trust and accountability? How do you intend to validate if your approach is feasible?", + "x-guidance": "Please describe your existing capabilities that demonstrate how and why you believe you’re best suited to deliver this project?\nPlease include the steps or processes that demonstrate that you can be trusted to manage funds properly." + } + }, + "required": [ + "feasibility" + ], + "title": "Capabilities & Feasibility" + }, + "impact": { + "$ref": "#/definitions/section", + "properties": { + "impact": { + "$ref": "#/definitions/multiLineTextEntryMarkdown", + "description": "Please define the positive impact your project will have on Midnight ecosystem", + "maxLength": 10240, + "minLength": 200, + "title": "Please define the positive impact your project will have on Midnight ecosystem", + "x-guidance": "Please include here a description of how you intend to measure impact (whether quantitative or qualitative) and how and with whom you will share your outputs:\n\n- In what way will the success of your project bring value to the Midnight ecosystem?\n- How will you measure this impact?\n- How will you share the outputs and opportunities that result from your project?" + } + }, + "required": [ + "impact" + ], + "title": "Impact" + }, + "solution": { + "$ref": "#/definitions/section", + "properties": { + "solution": { + "$ref": "#/definitions/multiLineTextEntryMarkdown", + "description": "Please describe your proposed solution and how it addresses the problem", + "maxLength": 10240, + "minLength": 200, + "title": "Please describe your proposed solution and how it addresses the problem", + "x-guidance": "How you write this section will depend on what type of proposal you are writing. You might want to include details on:\n\n- How do you perceive the problem you are solving?\n- What are your reasons for approaching it in the way that you have?\n- Who will your project engage?\n\n Explain what is unique about your solution, who will benefit, and why this is important to Midnight." + } + }, + "required": [ + "solution" + ], + "title": "Solution" + } + }, + "required": [ + "solution", + "impact", + "feasibility" + ], + "title": "Your Project and Solution", + "x-icon": "chart-pie", + "x-order": [ + "solution", + "impact", + "feasibility" + ] + }, + "milestones": { + "$ref": "#/definitions/segment", + "properties": { + "milestones": { + "$ref": "#/definitions/section", + "description": "What are the key milestones you need to achieve in order to complete your project successfully?", + "properties": { + "milestone_list": { + "description": "Please refer to the Guidance card on the right for details", + "items": { + "properties": { + "acceptance_criteria": { + "$ref": "#/definitions/multiLineTextEntryMarkdown", + "description": "Specific conditions that must be met", + "maxLength": 2000, + "minLength": 200, + "title": "Acceptance Criteria", + "x-placeholder": "Define the criteria for success in this milestone" + }, + "cost": { + "$ref": "#/definitions/currency", + "description": "The cost of this milestone in $USDM", + "format": "token:usdm", + "multipleOf": 1, + "title": "Cost", + "x-placeholder": "Enter the cost associated with this milestone" + }, + "delivery_month": { + "$ref": "#/definitions/durationInMonths", + "description": "The month when this milestone will be delivered", + "maximum": 3, + "minimum": 1, + "title": "Delivery Month", + "x-placeholder": "Enter the month when this milestone is expected to be delivered" + }, + "evidence": { + "$ref": "#/definitions/multiLineTextEntryMarkdown", + "description": "How you will demonstrate achievement", + "maxLength": 2000, + "minLength": 100, + "title": "Evidence of Completion", + "x-placeholder": "Specify the evidence that will demonstrate this milestone is complete" + }, + "outputs": { + "$ref": "#/definitions/multiLineTextEntryMarkdown", + "description": "What will be delivered in this milestone", + "maxLength": 2000, + "minLength": 200, + "title": "Milestone Outputs", + "x-placeholder": "Describe the deliverables or outcomes for this milestone" + }, + "progress": { + "$ref": "#/definitions/dropDownSingleSelect", + "description": "Current status of the milestone", + "enum": [ + "10 %", + "20 %", + "30 %", + "40 %", + "50 %", + "60 %", + "70 %", + "80 %", + "90 %", + "100 %" + ], + "title": "Progress", + "x-placeholder": "Enter the percentage of overall project completion progress for this milestone (10-100%)" + }, + "title": { + "$ref": "#/definitions/singleLineTextEntry", + "description": "A clear, concise title for this milestone", + "maxLength": 100, + "title": "Milestone Title", + "x-placeholder": "Enter the title for this milestone" + } + }, + "required": [ + "title", + "outputs", + "acceptance_criteria", + "evidence", + "delivery_month", + "cost" + ], + "title": "Milestone", + "type": "object", + "x-guidance": "Please follow this [guideline here](https://docs.projectcatalyst.io/current-fund/project-onboarding/milestone-based-proposals) to understand how to build your indicative milestones. Each milestone must have declared:\n\nA. **Milestone outputs**\n - What will be delivered\n\nB. **Acceptance criteria**\n - What conditions must be met\n\nC. **Evidence of completion**\n - How you will prove the milestone is complete", + "x-subsection": true + }, + "maxItems": 3, + "minItems": 3, + "title": "What are the key milestones you need to achieve in order to complete your project successfully?", + "type": "array", + "x-guidance": "For Midnight Grant Amounts 2 milestones, plus the final one including Project Close-out Report and Video, must be included (**3 milestones in total**)\n\nExpected Project outcomes:\n\n- M1: Publish public repository of required assets + the smart contract\n\n- M2: Connect smart contract to user interface (UI), make assets available into the repository, and have documentation\n\n- M3: Proof of Concept UI demonstrated in the Project Completion Video, testing suite complete, and Project Completion Report" + } + }, + "required": [ + "milestone_list" + ], + "title": "Project Milestones", + "x-guidance": "Please note milestones are only indicative during the proposal submission stage. Each funded project will formalize a milestone schedule and amounts during the onboarding stage." + } + }, + "title": "Milestones", + "x-icon": "flag", + "x-order": [ + "milestones" + ] + }, + "pitch": { + "$ref": "#/definitions/segment", + "properties": { + "budget": { + "$ref": "#/definitions/section", + "properties": { + "costs": { + "$ref": "#/definitions/multiLineTextEntryMarkdown", + "description": "Please provide a cost breakdown of the proposed work and resources", + "maxLength": 10240, + "minLength": 200, + "title": "Please provide a cost breakdown of the proposed work and resources", + "x-guidance": "Give an account of how the work is budgeted. It may be helpful to refer to your plan and timeline, list the resources you will need at each stage, and what they cost.\n\nIt is your responsibility to properly manage the funds provided. Make sure to reference [Fund Rules](https://docs.projectcatalyst.io/current-fund/fund-basics/fund-rules) to understand eligibility around costs." + } + }, + "required": [ + "costs" + ], + "title": "Budget & Costs" + }, + "team": { + "$ref": "#/definitions/section", + "properties": { + "who": { + "$ref": "#/definitions/multiLineTextEntryMarkdown", + "description": "Who is participating in the project team and what are their roles?", + "maxLength": 10240, + "minLength": 100, + "title": "Who is participating in the project team and what are their roles?", + "x-guidance": "List your team and collaborators, include links to their Linkedin profiles (or similar) and state what aspect of the project each participant will undertake.\n\nIf additional team members need to be recruited, please state what skills gaps you are looking to fill so readers understand what other roles will be needed to complete the project.\n\nYou are expected to have already engaged the relevant members of the organizations referenced so you have their commitment and capacity to support the project. If you have not taken any steps to this effect, it is likely that the resources will not be available if you are approved for funding, which can jeopardize the project before it has even begun.\n\nYour proposal will be publicly available, so make sure to obtain any consent required before including confidential or third party information.\n\nAll applicants must disclose their project team’s roles and scope of services across all submitted proposals. Failure to disclose this information may lead to disqualification from the current fund round." + } + }, + "required": [ + "who" + ], + "title": "Project Team" + }, + "value": { + "$ref": "#/definitions/section", + "properties": { + "note": { + "$ref": "#/definitions/multiLineTextEntryMarkdown", + "description": "How does the cost of the project represent value for the Midnight ecosystem?", + "maxLength": 10240, + "minLength": 200, + "title": "How does the cost of the project represent value for the Midnight ecosystem?", + "x-guidance": "Provide a compelling reason about how your project represents a good use of funds and justify how it will create value for the Midnight ecosystem." + } + }, + "required": [ + "note" + ], + "title": "Value for Money" + } + }, + "required": [ + "team", + "budget", + "value" + ], + "title": "Final Pitch", + "x-icon": "presentation-chart-line", + "x-order": [ + "team", + "budget", + "value" + ] + }, + "self_assessment": { + "$ref": "#/definitions/segment", + "properties": { + "self_assessment_checklist": { + "$ref": "#/definitions/section", + "properties": { + "developer_experience": { + "$ref": "#/definitions/agreementConfirmation", + "description": "Developer Experience Focus:", + "title": "I confirm that the proposal clearly defines which part of the developer journey it improves and how it makes building on Midnight easier and more productive." + }, + "open_source_commitment": { + "$ref": "#/definitions/agreementConfirmation", + "description": "Open Source Commitment:", + "title": "I confirm that the proposal explicitly states the chosen permissive open-source license (e.g., MIT, Apache 2.0) and commits to a public code repository." + }, + "quality_documentation": { + "$ref": "#/definitions/agreementConfirmation", + "description": "High-Quality Documentation:", + "title": "I confirm that a plan for creating and maintaining clear, comprehensive documentation is a core part of the proposal''s scope." + }, + "realistic_scope": { + "$ref": "#/definitions/agreementConfirmation", + "description": "Realistic Scope:", + "title": "I confirm that the budget and timeline (3 months) are realistic for delivering the proposed tool or resource." + }, + "strategic_fit": { + "$ref": "#/definitions/agreementConfirmation", + "description": "Strategic Fit:", + "title": "I confirm that the proposal clearly provides a basic prototype reference application for one of the areas of interest." + }, + "verifiable_builder_credentials": { + "$ref": "#/definitions/agreementConfirmation", + "description": "Verifiable Builder Credentials:", + "title": "I confirm that the team provides evidence of their technical ability and experience in creating developer tools or high-quality technical content (e.g., GitHub, portfolio)." + } + }, + "required": [ + "strategic_fit", + "developer_experience", + "open_source_commitment", + "verifiable_builder_credentials", + "quality_documentation", + "realistic_scope" + ], + "title": "Self-Assessment Checklist", + "x-guidance": "Use this checklist to ensure your proposal meets all foundational and content requirements before submission.", + "x-order": [ + "strategic_fit", + "developer_experience", + "open_source_commitment", + "verifiable_builder_credentials", + "quality_documentation", + "realistic_scope" + ] + } + }, + "required": [ + "self_assessment_checklist" + ], + "title": "Self-Assessment", + "x-icon": "shield-check", + "x-order": [ + "self_assessment_checklist" + ] + }, + "setup": { + "$ref": "#/definitions/segment", + "description": "Proposal title", + "properties": { + "proposer": { + "$ref": "#/definitions/section", + "description": "Proposer Information", + "properties": { + "applicant": { + "$ref": "#/definitions/singleLineTextEntry", + "description": "Name and surname of main applicant", + "maxLength": 100, + "minLength": 2, + "title": "Applicant name and surname", + "x-guidance": "The applicant is the individual or entity responsible for the proposed project.", + "x-placeholder": "Enter the full name of the applicant submitting the proposal" + }, + "type": { + "$ref": "#/definitions/radioButtonSelect", + "description": "Are you submitting this proposal as an individual or as an entity (whether formally incorporated or not)", + "enum": [ + "Individual", + "Entity (Incorporated)", + "Entity (Not Incorporated)" + ], + "title": "Are you submitting this proposal as an individual or as an entity (whether formally incorporated or not)?", + "x-guidance": "Please select from one of the following:\n\n1. Individual\n2. Entity (Incorporated)\n3. Entity (Not Incorporated)" + } + }, + "required": [ + "applicant", + "type" + ], + "title": "Applicant", + "x-order": [ + "applicant", + "type" + ] + }, + "title": { + "$ref": "#/definitions/section", + "description": "Proposal title", + "properties": { + "title": { + "$ref": "#/definitions/singleLineTextEntry", + "description": "**Proposal title**\n\nPlease note we suggest you use no more than 60 characters for your proposal title so that it can be easily viewed in the voting app.", + "maxLength": 60, + "minLength": 3, + "title": "Please provide your proposal title", + "x-guidance": "This is the first detail about your proposal that voters see so it should clearly express what the proposal is about. A clear, unambiguous, and concise title is very important.", + "x-placeholder": "Summarize your proposal in 60 characters or less" + } + }, + "required": [ + "title" + ], + "title": "Proposal title" + } + }, + "required": [ + "title", + "proposer" + ], + "title": "Proposal setup", + "x-icon": "view-grid", + "x-order": [ + "title", + "proposer" + ] + }, + "summary": { + "$ref": "#/definitions/segment", + "description": "Key information about your proposal", + "properties": { + "budget": { + "$ref": "#/definitions/section", + "properties": { + "requestedFunds": { + "$ref": "#/definitions/currency", + "description": "Requested amount should be between $USDM 2,500 or $USDM 10,000", + "format": "token:usdm", + "maximum": 10000, + "minimum": 2500, + "title": "Enter the amount of funding you are requesting in $USDM", + "x-guidance": "Each funding category has a minimum and maximum amount of funding a single proposal can request.\n\n**Midnight: Compact DApps**\n\n- Minimum Funding Amount per proposal: **$USDM 2,500**\n- Maximum Funding Amount per proposal: **$USDM 10,000**", + "x-placeholder": "Requested amount should be between $USDM 2,500 or $USDM 10,000" + } + }, + "required": [ + "requestedFunds" + ], + "title": "Budget Information", + "x-order": [ + "requestedFunds" + ] + }, + "dependencies": { + "$ref": "#/definitions/section", + "description": "External dependencies and requirements for project success", + "properties": { + "dependencyDetail": { + "$ref": "#/definitions/singleLineTextEntry", + "description": "Here you should list any dependencies and prerequisites for your project''s success. These are usually external factors (such as third-party suppliers, external resources, third-party software, etc.) that may cause a delay, since a project has less control over them. In case of third party software, indicate whether you have the necessary licenses and permission to use such software.", + "maxLength": 10240, + "minLength": 15, + "title": "Describe any dependencies or write ''No dependencies''", + "x-guidance": "If “YES”, please describe the dependencies and why you believe them essential to the project''s delivery. If “NO”, please write \"No dependencies\"." + }, + "hasDependency": { + "$ref": "#/definitions/yesNoChoice", + "description": "Does your project have any dependencies on other organizations, technical or otherwise?", + "title": "Does your project have any dependencies on other organizations, technical or otherwise?", + "x-guidance": "List dependencies and prerequisites for your project’s success. These are usually external factors (such as third-party suppliers, external resources, third-party software, etc.) that may cause a delay or represent a risk due to less control over resources.\n\nIn the case of third-party software, indicate whether you have the necessary licenses and permission to use such software.", + "x-placeholder": "Select if your project has any dependencies" + } + }, + "required": [ + "hasDependency", + "dependencyDetail" + ], + "title": "Project Dependencies" + }, + "open_source": { + "$ref": "#/definitions/section", + "description": "Will your project''s output be fully open source? Open source refers to something people can modify and share because its design is publicly accessible.", + "properties": { + "isOpenSource": { + "$ref": "#/definitions/yesNoChoice", + "description": "Select Yes if the project is open source, No if it is not.", + "title": "Will your project''s outputs be fully open source?", + "x-guidance": "If you answered “YES” to the question above:\n\nIf you declare that the project will be open source in the application form, it must remain open source throughout the entire lifecycle of the project, with a publicly accessible repository.\n\nPlease indicate the type of open source license you intend to use and provide any additional information relevant to the open source status of your project outputs.\n\nIf you answered “NO” to the question above:\n\nYou will not be eligible for this funding." + }, + "openSourceInformation": { + "$ref": "#/definitions/multiLineTextEntry", + "description": "Please provide here more information on the open source status of your project outputs", + "maxLength": 500, + "title": "Please provide here more information on the open source status of your project outputs", + "x-guidance": "Open source licenses ensure intellectual property (IP) (such as source code) remains publicly accessible and licensed in a way that allows anyone to use, modify, and share the IP freely, typically under licenses approved by the Open Source Initiative.\n\nProprietary IP (such as source code) is not publicly available and the rights to use, modify, or redistribute the proprietary IP are restricted and typically reserved by the IP''s owner.\n\nhttps://opensource.org/licenses", + "x-placeholder": "Describe the open source status of your project and the license type" + } + }, + "required": [ + "isOpenSource", + "openSourceInformation" + ], + "title": "Project Open Source", + "x-order": [ + "isOpenSource", + "openSourceInformation" + ] + }, + "problem": { + "$ref": "#/definitions/section", + "description": "Define the problem your proposal aims to solve", + "properties": { + "statement": { + "$ref": "#/definitions/multiLineTextEntry", + "description": "Clearly define the problem you aim to solve. This will be visible in the Catalyst voting app.", + "maxLength": 200, + "minLength": 50, + "title": "What is the problem you want to solve?", + "x-guidance": "Ensure you present a well-defined problem. What is the core issue that you hope to fix? Remember: the reader might not recognize the problem unless you state it clearly.\n\nThis answer will be displayed on the Catalyst voting app, so voters will see it even if they do not open your proposal to read it in detail.", + "x-placeholder": "Describe the problem to be solved with your proposed project" + } + }, + "required": [ + "statement" + ], + "title": "Problem Statement" + }, + "solution": { + "$ref": "#/definitions/section", + "description": "Describe your proposed solution to the problem", + "properties": { + "summary": { + "$ref": "#/definitions/multiLineTextEntry", + "description": "Briefly describe your solution. Focus on what you will do or create to solve the problem.", + "maxLength": 200, + "minLength": 50, + "title": "Summarize your solution to the problem", + "x-guidance": "Focus on what you are going to do, or change to solve the problem. Avoid “There should be a way to...” and ensure “We will build a...!”.\n\nClearly state how the solution addresses the problem you have identified. Make sure you connect the ''why'' and the ''how'' of your solution.\n\nThis answer will be displayed on the Catalyst voting app, so voters will see it even if they do not open your proposal and read it in detail.", + "x-placeholder": "Describe how you will solve the problem" + } + }, + "required": [ + "summary" + ], + "title": "Solution Overview" + }, + "supportingLinks": { + "$ref": "#/definitions/section", + "description": "Additional resources and documentation for your proposal", + "properties": { + "links": { + "$ref": "#/definitions/singleLineHttpsURLEntryList", + "description": "Links to any relevant documentation, code repositories, or marketing materials. All links must use HTTPS. Please make sure that submitters own or have rights to share any linked content.", + "maxItems": 5, + "minItems": 0, + "title": "Supporting links" + } + }, + "title": "Supporting Documentation", + "x-guidance": "Provide **GitHub handles for all builders** and **1–3 representative repositories** that show your ability to ship code. Optionally include a link to an early PoC repo for this proposal and any **technical write-ups** (READMEs/docs).\n\n**Do not include** websites, social media, pitch decks, or marketing materials-this fund evaluates **code**, not marketing. All links must use HTTPS.", + "x-placeholder": "Links to any relevant documentation, code repositories, or marketing materials. All links must use HTTPS. Please make sure that submitters own or have rights to share any linked content." + }, + "time": { + "$ref": "#/definitions/section", + "properties": { + "duration": { + "$ref": "#/definitions/durationInMonths", + "description": "Specify the expected duration of your project. Projects must be completable within 2-12 months.", + "maximum": 3, + "minimum": 3, + "title": "Please specify how many months you expect your project to last ", + "x-guidance": "Midnight projects must be completed within 3 months.\n\nProjects can be completed earlier if milestone [Proof-of-Achievements](https://docs.projectcatalyst.io/current-fund/project-onboarding/milestone-based-proposals) and [Project Completion](https://docs.projectcatalyst.io/current-fund/general-information/project-close-out-report-and-project-close-out-video-pcr-and-pcv) steps are submitted and approved ahead of the expected delivery due dates in compliance with the [Fund Rules](https://docs.projectcatalyst.io/current-fund/fund-basics/fund-rules).", + "x-placeholder": "Select the expected duration of your project (3 months)" + } + }, + "required": [ + "duration" + ], + "title": "Time" + }, + "translation": { + "$ref": "#/definitions/section", + "description": "Information about the proposal''s language and translation status", + "properties": { + "isTranslated": { + "$ref": "#/definitions/yesNoChoice", + "description": "Indicate if your proposal has been auto-translated into English from another language", + "title": "Please indicate if your proposal has been auto-translated", + "x-guidance": "Tick “YES” to remind readers that your proposal has been automatically translated and that there may be translation inaccuracies.\n\nIf you wish, you can link a document with the proposal in your native language. \n\nTick “NO” if your proposal has not been automatically translated into English from another language.", + "x-placeholder": "Select if your proposal has been auto-translated into English" + }, + "originalDocumentLink": { + "$ref": "#/definitions/singleLineHttpsURLEntry", + "description": "Provide a link to the original proposal document in its original language", + "title": "Original Document Link" + }, + "originalLanguage": { + "$ref": "#/definitions/languageCode", + "description": "If auto-translated, specify the original language of your proposal", + "title": "Original Language" + } + }, + "required": [ + "isTranslated" + ], + "title": "Translation Information" + } + }, + "title": "Proposal Summary", + "x-icon": "light-bulb", + "x-order": [ + "budget", + "time", + "translation", + "problem", + "solution", + "supportingLinks", + "dependencies", + "open_source" + ] + }, + "theme": { + "$ref": "#/definitions/segment", + "properties": { + "theme": { + "$ref": "#/definitions/section", + "description": "Long-term vision and categorization of your project", + "properties": { + "grouped_tag": { + "$ref": "#/definitions/singleGroupedTagSelector", + "oneOf": [ + { + "properties": { + "group": { + "$ref": "#/definitions/tagGroup", + "const": "Governance" + }, + "tag": { + "$ref": "#/definitions/tagSelection", + "enum": [ + "Governance", + "DAO" + ] + } + } + }, + { + "properties": { + "group": { + "$ref": "#/definitions/tagGroup", + "const": "Education" + }, + "tag": { + "$ref": "#/definitions/tagSelection", + "enum": [ + "Education", + "Learn to Earn", + "Training", + "Translation" + ] + } + } + }, + { + "properties": { + "group": { + "$ref": "#/definitions/tagGroup", + "const": "Community & Outreach" + }, + "tag": { + "$ref": "#/definitions/tagSelection", + "enum": [ + "Connected Community", + "Community", + "Community Outreach", + "Social Media" + ] + } + } + }, + { + "properties": { + "group": { + "$ref": "#/definitions/tagGroup", + "const": "Development & Tools" + }, + "tag": { + "$ref": "#/definitions/tagSelection", + "enum": [ + "Developer Tools", + "Infrastructure", + "Analytics", + "AI", + "Research", + "UTXO", + "P2P", + "Layer2" + ] + } + } + }, + { + "properties": { + "group": { + "$ref": "#/definitions/tagGroup", + "const": "Identity & Security" + }, + "tag": { + "$ref": "#/definitions/tagSelection", + "enum": [ + "Identity & Verification", + "Cybersecurity", + "Security", + "Authentication", + "Privacy" + ] + } + } + }, + { + "properties": { + "group": { + "$ref": "#/definitions/tagGroup", + "const": "DeFi" + }, + "tag": { + "$ref": "#/definitions/tagSelection", + "enum": [ + "DeFi", + "DEX", + "Payments", + "Stablecoin", + "Risk Management", + "Yield", + "Staking", + "Lending" + ] + } + } + }, + { + "properties": { + "group": { + "$ref": "#/definitions/tagGroup", + "const": "Real World Applications (RWA)" + }, + "tag": { + "$ref": "#/definitions/tagSelection", + "enum": [ + "Wallet", + "Marketplace", + "Manufacturing", + "IoT", + "Financial Services", + "E-commerce", + "Business Services", + "Supply Chain", + "Real Estate", + "Healthcare", + "Tourism", + "Entertainment", + "RWA", + "Music", + "Tokenization" + ] + } + } + }, + { + "properties": { + "group": { + "$ref": "#/definitions/tagGroup", + "const": "Events & Marketing" + }, + "tag": { + "$ref": "#/definitions/tagSelection", + "enum": [ + "Events", + "Marketing", + "Hackathons", + "Accelerator", + "Incubator" + ] + } + } + }, + { + "properties": { + "group": { + "$ref": "#/definitions/tagGroup", + "const": "Interoperability" + }, + "tag": { + "$ref": "#/definitions/tagSelection", + "enum": [ + "Cross-chain", + "Interoperability", + "Off-chain", + "Legal", + "Policy", + "Advocacy", + "Standards" + ] + } + } + }, + { + "properties": { + "group": { + "$ref": "#/definitions/tagGroup", + "const": "Sustainability" + }, + "tag": { + "$ref": "#/definitions/tagSelection", + "enum": [ + "Sustainability", + "Environment", + "Agriculture" + ] + } + } + }, + { + "properties": { + "group": { + "$ref": "#/definitions/tagGroup", + "const": "Smart Contracts" + }, + "tag": { + "$ref": "#/definitions/tagSelection", + "enum": [ + "Smart Contracts", + "Audit", + "Oracles" + ] + } + } + }, + { + "properties": { + "group": { + "$ref": "#/definitions/tagGroup", + "const": "GameFi" + }, + "tag": { + "$ref": "#/definitions/tagSelection", + "enum": [ + "Games", + "Gaming", + "GameFi", + "Entertainment", + "Metaverse" + ] + } + } + }, + { + "properties": { + "group": { + "$ref": "#/definitions/tagGroup", + "const": "NFT" + }, + "tag": { + "$ref": "#/definitions/tagSelection", + "enum": [ + "NFT", + "CNFT", + "Collectibles", + "Digital Twin" + ] + } + } + } + ], + "title": "Please choose the most relevant theme and tag related to the outcomes of your proposal" + } + }, + "required": [ + "grouped_tag" + ], + "title": "Theme", + "x-order": [ + "theme" + ] + } + }, + "title": "Theme Selection", + "x-icon": "tag", + "x-order": [ + "theme" + ] + } + }, + "required": [ + "setup", + "summary", + "theme", + "details", + "milestones", + "pitch", + "self_assessment", + "agreements" + ], + "title": "F15 Midnight: Compact DApps", + "type": "object", + "x-changelog": { + "2025-10-08": [ + "F15 Template Setup" + ], + "2025-11-05": [ + "Included two additional links in the Consent & Confirmation question" + ] + }, + "x-order": [ + "setup", + "summary", + "theme", + "campaign_category", + "details", + "milestones", + "pitch", + "self_assessment", + "agreements" + ] + } +] \ No newline at end of file From b475f9648e33f6f2cbaeecd9e23014553ea49848 Mon Sep 17 00:00:00 2001 From: Apisit Ritruengroj Date: Mon, 17 Nov 2025 20:43:41 +0700 Subject: [PATCH 04/40] chore: minor --- .../tests/api_tests/utils/signed_doc.py | 79 ++++++------------- 1 file changed, 22 insertions(+), 57 deletions(-) diff --git a/catalyst-gateway/tests/api_tests/utils/signed_doc.py b/catalyst-gateway/tests/api_tests/utils/signed_doc.py index 0950f9c864b..b7a83be77a7 100644 --- a/catalyst-gateway/tests/api_tests/utils/signed_doc.py +++ b/catalyst-gateway/tests/api_tests/utils/signed_doc.py @@ -103,32 +103,28 @@ def __factory__() -> tuple[SignedDocument, RoleID]: "type": DOC_TYPE["proposal"], "content-type": "application/json", "content-encoding": "br", - # referenced to the defined proposal template id, comes from the 'templates/data.rs' file - "template": [ - { - "id": PROPOSAL_FORM_TEMPLATE_ID, - "ver": PROPOSAL_FORM_TEMPLATE_ID, - "cid": "0x", - } + "parameters": [ + # referenced to the defined proposal template id, comes from the 'templates/data.rs' file + {"id": PROPOSAL_FORM_TEMPLATE_ID, "ver": PROPOSAL_FORM_TEMPLATE_ID, "cid": "0x"}, + # referenced to the defined category id, comes from the 'templates/data.rs' file + {"id": BRAND_ID, "ver": BRAND_ID, "cid": "0x"}, ], - # referenced to the defined category id, comes from the 'templates/data.rs' file - "parameters": [{"id": BRAND_ID, "ver": BRAND_ID, "cid": "0x"}], } with open("./test_data/signed_docs/proposal.json", "r") as json_file: body = json.load(json_file) - doc = SignedDocument(metadata, body) + doc_builder = SignedDocument(metadata, body) (cat_id, sk_hex) = rbac_chain.cat_id_for_role(role_id) resp = document.put( - data=doc.build_and_sign(cat_id, sk_hex), + data=doc_builder.build_and_sign(cat_id, sk_hex), token=rbac_chain.auth_token(), ) assert ( resp.status_code == 201 ), f"Failed to publish document: {resp.status_code} - {resp.text}" - return doc, role_id + return doc_builder, role_id return __factory__ @@ -137,32 +133,22 @@ def __factory__() -> tuple[SignedDocument, RoleID]: def proposal_form_template_doc_factory(rbac_chain_factory): def __factory__() -> tuple[SignedDocument, RoleID]: role_id = RoleID.PROPOSER - rbac_chain = rbac_chain_factory() - doc_id = uuid_v7.uuid_v7() - metadata = { - "id": doc_id, - "ver": doc_id, - "type": DOC_TYPE["proposal_form_template"], - "content-type": "application/json", - "content-encoding": "br", - # referenced to the defined category id, comes from the 'templates/data.rs' file - "parameters": [{"id": BRAND_ID, "ver": BRAND_ID, "cid": "0x"}], - } with open("./test_data/signed_docs/proposal_form_template.json", "r") as json_file: - content = json.load(json_file) + metadata, content = json.load(json_file) - doc = SignedDocument(metadata, content) + doc_builder = SignedDocument(metadata, content) + rbac_chain = rbac_chain_factory() (cat_id, sk_hex) = rbac_chain.cat_id_for_role(role_id) resp = document.put( - data=doc.build_and_sign(cat_id, sk_hex), + data=doc_builder.build_and_sign(cat_id, sk_hex), token=rbac_chain.auth_token(), ) assert ( resp.status_code == 201 ), f"Failed to publish document: {resp.status_code} - {resp.text}" - return doc, role_id + return doc_builder, role_id return __factory__ @@ -170,41 +156,22 @@ def __factory__() -> tuple[SignedDocument, RoleID]: @pytest.fixture def category_parameters_doc_factory(rbac_chain_factory): def __factory__() -> tuple[SignedDocument, RoleID]: - role_id = RoleID.PROPOSER - rbac_chain = rbac_chain_factory() - doc_id = uuid_v7.uuid_v7() - metadata = { - "id": doc_id, - "ver": doc_id, - "type": DOC_TYPE["category_parameters"], - "content-type": "application/json", - "content-encoding": "br", - # referenced to the defined proposal template id, comes from the 'templates/data.rs' file - "template": [ - { - "id": PROPOSAL_FORM_TEMPLATE_ID, - "ver": PROPOSAL_FORM_TEMPLATE_ID, - "cid": "0x", - } - ], - # referenced to the defined category id, comes from the 'templates/data.rs' file - "parameters": [{"id": BRAND_ID, "ver": BRAND_ID, "cid": "0x"}], - } with open("./test_data/signed_docs/category_parameters.json", "r") as json_file: - content = json.load(json_file) + metadata, content = json.load(json_file) - doc = SignedDocument(metadata, content) - (cat_id, sk_hex) = rbac_chain.cat_id_for_role(role_id) + doc_builder = SignedDocument(metadata, content) + rbac_chain = rbac_chain_factory() + (cat_id, sk_hex) = rbac_chain.cat_id_for_role(RoleID.PROPOSER) resp = document.put( - data=doc.build_and_sign(cat_id, sk_hex), + data=doc_builder.build_and_sign(cat_id, sk_hex), token=rbac_chain.auth_token(), ) assert ( resp.status_code == 201 ), f"Failed to publish document: {resp.status_code} - {resp.text}" - return doc, role_id + return doc_builder, RoleID.PROPOSER return __factory__ @@ -221,24 +188,22 @@ def __factory__() -> tuple[SignedDocument, RoleID]: "type": DOC_TYPE["category_parameters_form_template"], "content-type": "application/json", "content-encoding": "br", - # referenced to the defined category id, comes from the 'templates/data.rs' file - "parameters": [{"id": BRAND_ID, "ver": BRAND_ID, "cid": "0x"}], } with open("./test_data/signed_docs/category_parameters_form_template.json", "r") as json_file: content = json.load(json_file) - doc = SignedDocument(metadata, content) + doc_builder = SignedDocument(metadata, content) (cat_id, sk_hex) = rbac_chain.cat_id_for_role(role_id) resp = document.put( - data=doc.build_and_sign(cat_id, sk_hex), + data=doc_builder.build_and_sign(cat_id, sk_hex), token=rbac_chain.auth_token(), ) assert ( resp.status_code == 201 ), f"Failed to publish document: {resp.status_code} - {resp.text}" - return doc, role_id + return doc_builder, role_id return __factory__ From 14fbd5d44d84cdd0766fc9f5b8b6477e86001d4a Mon Sep 17 00:00:00 2001 From: Apisit Ritruengroj Date: Tue, 18 Nov 2025 21:00:32 +0700 Subject: [PATCH 05/40] chore: preparing minor --- .../integration/signed_doc/test_v1_signed_doc.py | 2 +- .../{proposal.json => proposal.deprecated.json} | 0 .../tests/api_tests/utils/signed_doc.py | 15 ++++----------- 3 files changed, 5 insertions(+), 12 deletions(-) rename catalyst-gateway/tests/api_tests/test_data/signed_docs/{proposal.json => proposal.deprecated.json} (100%) diff --git a/catalyst-gateway/tests/api_tests/integration/signed_doc/test_v1_signed_doc.py b/catalyst-gateway/tests/api_tests/integration/signed_doc/test_v1_signed_doc.py index 763526dfc94..9b2ed59f38b 100644 --- a/catalyst-gateway/tests/api_tests/integration/signed_doc/test_v1_signed_doc.py +++ b/catalyst-gateway/tests/api_tests/integration/signed_doc/test_v1_signed_doc.py @@ -272,7 +272,7 @@ def deprecated_proposal(rbac_chain): "ver": "0194d490-30bf-7473-81c8-a0eaef369619", }, } - with open("./test_data/signed_docs/proposal.json", "r") as json_file: + with open("./test_data/signed_docs/proposal.deprecated.json", "r") as json_file: content = json.load(json_file) doc = SignedDocumentV1(proposal_metadata_json, content) diff --git a/catalyst-gateway/tests/api_tests/test_data/signed_docs/proposal.json b/catalyst-gateway/tests/api_tests/test_data/signed_docs/proposal.deprecated.json similarity index 100% rename from catalyst-gateway/tests/api_tests/test_data/signed_docs/proposal.json rename to catalyst-gateway/tests/api_tests/test_data/signed_docs/proposal.deprecated.json diff --git a/catalyst-gateway/tests/api_tests/utils/signed_doc.py b/catalyst-gateway/tests/api_tests/utils/signed_doc.py index b7a83be77a7..2a04825fa52 100644 --- a/catalyst-gateway/tests/api_tests/utils/signed_doc.py +++ b/catalyst-gateway/tests/api_tests/utils/signed_doc.py @@ -85,14 +85,10 @@ def build_and_sign( ) -BRAND_ID = "0199e71b-401e-7160-9139-a398c4d7b8fa" -PROPOSAL_FORM_TEMPLATE_ID = "0199e71b-4025-7323-bc4a-d39e35762521" - - # return a Proposal document which is already published to the cat-gateway and the corresponding RoleID @pytest.fixture def proposal_doc_factory(rbac_chain_factory): - def __factory__() -> tuple[SignedDocument, RoleID]: + def __factory__(template_id: str) -> tuple[SignedDocument, RoleID]: role_id = RoleID.PROPOSER rbac_chain = rbac_chain_factory() doc_id = uuid_v7.uuid_v7() @@ -104,14 +100,11 @@ def __factory__() -> tuple[SignedDocument, RoleID]: "content-type": "application/json", "content-encoding": "br", "parameters": [ - # referenced to the defined proposal template id, comes from the 'templates/data.rs' file - {"id": PROPOSAL_FORM_TEMPLATE_ID, "ver": PROPOSAL_FORM_TEMPLATE_ID, "cid": "0x"}, - # referenced to the defined category id, comes from the 'templates/data.rs' file - {"id": BRAND_ID, "ver": BRAND_ID, "cid": "0x"}, + {"id": template_id, "ver": template_id, "cid": "0x"}, ], } - with open("./test_data/signed_docs/proposal.json", "r") as json_file: - body = json.load(json_file) + # TODO: auto generate body from the given schema + body = {} doc_builder = SignedDocument(metadata, body) (cat_id, sk_hex) = rbac_chain.cat_id_for_role(role_id) From 4e055f0bc4cc4150b3f7c1b0388800173ed15bd6 Mon Sep 17 00:00:00 2001 From: Apisit Ritruengroj Date: Tue, 18 Nov 2025 21:26:50 +0700 Subject: [PATCH 06/40] feat: apply json auto generate --- catalyst-gateway/tests/api_tests/utils/signed_doc.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/catalyst-gateway/tests/api_tests/utils/signed_doc.py b/catalyst-gateway/tests/api_tests/utils/signed_doc.py index 2a04825fa52..8fd93d1e125 100644 --- a/catalyst-gateway/tests/api_tests/utils/signed_doc.py +++ b/catalyst-gateway/tests/api_tests/utils/signed_doc.py @@ -9,6 +9,7 @@ from utils import signed_doc, uuid_v7 from utils.rbac_chain import rbac_chain_factory, RoleID from tempfile import NamedTemporaryFile +from jsf import JSF DOC_TYPE = { @@ -88,7 +89,7 @@ def build_and_sign( # return a Proposal document which is already published to the cat-gateway and the corresponding RoleID @pytest.fixture def proposal_doc_factory(rbac_chain_factory): - def __factory__(template_id: str) -> tuple[SignedDocument, RoleID]: + def __factory__(template_id: str, template_schema: Any) -> tuple[SignedDocument, RoleID]: role_id = RoleID.PROPOSER rbac_chain = rbac_chain_factory() doc_id = uuid_v7.uuid_v7() @@ -104,7 +105,7 @@ def __factory__(template_id: str) -> tuple[SignedDocument, RoleID]: ], } # TODO: auto generate body from the given schema - body = {} + body = JSF(template_schema).generate() doc_builder = SignedDocument(metadata, body) (cat_id, sk_hex) = rbac_chain.cat_id_for_role(role_id) From c624f2e1d992f31f2fccc00c08247742230c7e01 Mon Sep 17 00:00:00 2001 From: Apisit Ritruengroj Date: Tue, 18 Nov 2025 21:27:16 +0700 Subject: [PATCH 07/40] feat: apply json auto generate --- catalyst-gateway/tests/api_tests/utils/signed_doc.py | 1 - 1 file changed, 1 deletion(-) diff --git a/catalyst-gateway/tests/api_tests/utils/signed_doc.py b/catalyst-gateway/tests/api_tests/utils/signed_doc.py index 8fd93d1e125..720affdab73 100644 --- a/catalyst-gateway/tests/api_tests/utils/signed_doc.py +++ b/catalyst-gateway/tests/api_tests/utils/signed_doc.py @@ -104,7 +104,6 @@ def __factory__(template_id: str, template_schema: Any) -> tuple[SignedDocument, {"id": template_id, "ver": template_id, "cid": "0x"}, ], } - # TODO: auto generate body from the given schema body = JSF(template_schema).generate() doc_builder = SignedDocument(metadata, body) From e9ede07e04dba1fa13df77fbd1ced64cd5ebd14b Mon Sep 17 00:00:00 2001 From: Apisit Ritruengroj Date: Thu, 20 Nov 2025 11:36:19 +0700 Subject: [PATCH 08/40] feat: general factory --- .../category_parameters_form_template.json | 0 .../tests/api_tests/utils/signed_doc.py | 56 ++++++++++++------- 2 files changed, 35 insertions(+), 21 deletions(-) delete mode 100644 catalyst-gateway/tests/api_tests/test_data/signed_docs/category_parameters_form_template.json diff --git a/catalyst-gateway/tests/api_tests/test_data/signed_docs/category_parameters_form_template.json b/catalyst-gateway/tests/api_tests/test_data/signed_docs/category_parameters_form_template.json deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/catalyst-gateway/tests/api_tests/utils/signed_doc.py b/catalyst-gateway/tests/api_tests/utils/signed_doc.py index 720affdab73..882ea1f1b6f 100644 --- a/catalyst-gateway/tests/api_tests/utils/signed_doc.py +++ b/catalyst-gateway/tests/api_tests/utils/signed_doc.py @@ -100,9 +100,7 @@ def __factory__(template_id: str, template_schema: Any) -> tuple[SignedDocument, "type": DOC_TYPE["proposal"], "content-type": "application/json", "content-encoding": "br", - "parameters": [ - {"id": template_id, "ver": template_id, "cid": "0x"}, - ], + "parameters": [], } body = JSF(template_schema).generate() @@ -172,34 +170,50 @@ def __factory__() -> tuple[SignedDocument, RoleID]: @pytest.fixture def category_parameters_form_template_doc_factory(rbac_chain_factory): def __factory__() -> tuple[SignedDocument, RoleID]: - role_id = RoleID.PROPOSER - rbac_chain = rbac_chain_factory() - doc_id = uuid_v7.uuid_v7() - metadata = { - "id": doc_id, - "ver": doc_id, - "type": DOC_TYPE["category_parameters_form_template"], - "content-type": "application/json", - "content-encoding": "br", - } - with open("./test_data/signed_docs/category_parameters_form_template.json", "r") as json_file: - content = json.load(json_file) - - doc_builder = SignedDocument(metadata, content) - (cat_id, sk_hex) = rbac_chain.cat_id_for_role(role_id) + builder = generic_doc_builder( + rbac_chain_factory(), + DOC_TYPE["category_parameters_form_template"], + [], + {} + ) resp = document.put( - data=doc_builder.build_and_sign(cat_id, sk_hex), - token=rbac_chain.auth_token(), + data=builder["signed_doc"], + token=builder["auth_token"], ) assert ( resp.status_code == 201 ), f"Failed to publish document: {resp.status_code} - {resp.text}" - return doc_builder, role_id + return builder["doc_builder"], builder["role_id"] return __factory__ +def generic_doc_builder(rbac_chain, doc_type: str, parameters: list[dict], content: Any): + role_id = RoleID.PROPOSER + doc_id = uuid_v7.uuid_v7() + metadata = { + "content-encoding": "br", + "content-type": "application/json", + "id": doc_id, + "ver": doc_id, + "type": doc_type, + "parameters": parameters, + } + + doc_builder = SignedDocument(metadata, content) + (cat_id, sk_hex) = rbac_chain.cat_id_for_role(role_id) + + return { + "metadata": metadata, + "content": content, + "doc_builder": doc_builder, + "signed_doc": doc_builder.build_and_sign(cat_id, sk_hex), + "auth_token": rbac_chain.auth_token(), + "cat_id": cat_id, + "role_id": role_id + } + def build_signed_doc( metadata_json: Dict[str, Any], From e66d7010e3c2641b6363950f841ea01dcc7a2177 Mon Sep 17 00:00:00 2001 From: Apisit Ritruengroj Date: Thu, 20 Nov 2025 14:52:24 +0700 Subject: [PATCH 09/40] feat: campaign setup --- .../tests/api_tests/utils/signed_doc.py | 48 +++++++++++++++++-- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/catalyst-gateway/tests/api_tests/utils/signed_doc.py b/catalyst-gateway/tests/api_tests/utils/signed_doc.py index 882ea1f1b6f..81e85ba398a 100644 --- a/catalyst-gateway/tests/api_tests/utils/signed_doc.py +++ b/catalyst-gateway/tests/api_tests/utils/signed_doc.py @@ -88,21 +88,58 @@ def build_and_sign( # return a Proposal document which is already published to the cat-gateway and the corresponding RoleID @pytest.fixture -def proposal_doc_factory(rbac_chain_factory): - def __factory__(template_id: str, template_schema: Any) -> tuple[SignedDocument, RoleID]: +def proposal_doc_factory(rbac_chain_factory, proposal_form_template_doc_factory): + def __factory__() -> tuple[SignedDocument, RoleID]: role_id = RoleID.PROPOSER + template = proposal_form_template_doc_factory() rbac_chain = rbac_chain_factory() doc_id = uuid_v7.uuid_v7() + metadata = { "id": doc_id, "ver": doc_id, - # Proposal document type "type": DOC_TYPE["proposal"], "content-type": "application/json", "content-encoding": "br", + "parameters": [ + # TODO: + ], + } + body = JSF(template).generate() + + doc_builder = SignedDocument(metadata, body) + (cat_id, sk_hex) = rbac_chain.cat_id_for_role(role_id) + + resp = document.put( + data=doc_builder.build_and_sign(cat_id, sk_hex), + token=rbac_chain.auth_token(), + ) + assert ( + resp.status_code == 201 + ), f"Failed to publish document: {resp.status_code} - {resp.text}" + + return doc_builder, role_id + + return __factory__ + + +@pytest.fixture +def campaign_parameters_doc_factory(rbac_chain_factory): + def __factory__() -> tuple[SignedDocument, RoleID]: + role_id = RoleID.PROPOSER + template = proposal_form_template_doc_factory() + rbac_chain = rbac_chain_factory() + doc_id = uuid_v7.uuid_v7() + + metadata = { + "id": doc_id, + "ver": doc_id, + "type": DOC_TYPE["campaign_parameters"], + "content-type": "application/json", + "content-encoding": "br", "parameters": [], } - body = JSF(template_schema).generate() + body = JSF(template).generate() doc_builder = SignedDocument(metadata, body) (cat_id, sk_hex) = rbac_chain.cat_id_for_role(role_id) @@ -119,6 +156,9 @@ def __factory__(template_id: str, template_schema: Any) -> tuple[SignedDocument, return __factory__ +@pytest.fixture +def campaign_parameters_form_template_doc_factory(rbac_chain_factory): + None @pytest.fixture def proposal_form_template_doc_factory(rbac_chain_factory): From 29631a51089f2aa25da656aad834f707533609e7 Mon Sep 17 00:00:00 2001 From: Apisit Ritruengroj Date: Thu, 20 Nov 2025 18:02:25 +0700 Subject: [PATCH 10/40] feat: minor rework --- .../signed_docs/category_parameters.json | 17 -- .../tests/api_tests/utils/signed_doc.py | 216 +++++++++--------- 2 files changed, 112 insertions(+), 121 deletions(-) delete mode 100644 catalyst-gateway/tests/api_tests/test_data/signed_docs/category_parameters.json diff --git a/catalyst-gateway/tests/api_tests/test_data/signed_docs/category_parameters.json b/catalyst-gateway/tests/api_tests/test_data/signed_docs/category_parameters.json deleted file mode 100644 index 32ab2023fdd..00000000000 --- a/catalyst-gateway/tests/api_tests/test_data/signed_docs/category_parameters.json +++ /dev/null @@ -1,17 +0,0 @@ -[ - { - "content-encoding": "br", - "content-type": "application/json", - "id": "0199802c-21b4-7dc8-8537-7eae5ea4c4d3", - "parameters": [ - { - "cid": "0x", - "id": "0199802c-21b4-7d91-986d-0e913cd81391", - "ver": "0199802c-21b4-7d91-986d-0e913cd81391" - } - ], - "type": "48c20109-362a-4d32-9bba-e0a9cf8b45be", - "ver": "0199802c-21b4-7dc8-8537-7eae5ea4c4d3" - }, - {} -] \ No newline at end of file diff --git a/catalyst-gateway/tests/api_tests/utils/signed_doc.py b/catalyst-gateway/tests/api_tests/utils/signed_doc.py index 81e85ba398a..73b4004ac23 100644 --- a/catalyst-gateway/tests/api_tests/utils/signed_doc.py +++ b/catalyst-gateway/tests/api_tests/utils/signed_doc.py @@ -86,62 +86,103 @@ def build_and_sign( ) -# return a Proposal document which is already published to the cat-gateway and the corresponding RoleID -@pytest.fixture -def proposal_doc_factory(rbac_chain_factory, proposal_form_template_doc_factory): - def __factory__() -> tuple[SignedDocument, RoleID]: - role_id = RoleID.PROPOSER - template = proposal_form_template_doc_factory() - rbac_chain = rbac_chain_factory() - doc_id = uuid_v7.uuid_v7() +def generic_doc_builder(rbac_chain, doc_type: str, parameters: list[dict], content: Any): + role_id = RoleID.PROPOSER + doc_id = uuid_v7.uuid_v7() + metadata = { + "content-encoding": "br", + "content-type": "application/json", + "id": doc_id, + "ver": doc_id, + "type": doc_type, + "parameters": parameters, + } - metadata = { - "id": doc_id, - "ver": doc_id, - "type": DOC_TYPE["proposal"], - "content-type": "application/json", - "content-encoding": "br", - "parameters": [ - # TODO: - ], - } - body = JSF(template).generate() + doc_builder = SignedDocument(metadata, content) + (cat_id, sk_hex) = rbac_chain.cat_id_for_role(role_id) - doc_builder = SignedDocument(metadata, body) - (cat_id, sk_hex) = rbac_chain.cat_id_for_role(role_id) - - resp = document.put( - data=doc_builder.build_and_sign(cat_id, sk_hex), - token=rbac_chain.auth_token(), + return { + "metadata": metadata, + "content": content, + "doc_builder": doc_builder, + "signed_doc": doc_builder.build_and_sign(cat_id, sk_hex), + "auth_token": rbac_chain.auth_token(), + "cat_id": cat_id, + "role_id": role_id + } + + +def build_signed_doc( + metadata_json: Dict[str, Any], + doc_content_json: Dict[str, Any], + bip32_sk_hex: str, + # corresponded to the `bip32_sk_hex` `cat_id` string + cat_id: str, + mk_signed_doc_path, +) -> str: + with NamedTemporaryFile() as metadata_file, NamedTemporaryFile() as doc_content_file, NamedTemporaryFile() as signed_doc_file: + + json_str = json.dumps(metadata_json) + metadata_file.write(json_str.encode(encoding="utf-8")) + metadata_file.flush() + + json_str = json.dumps(doc_content_json) + doc_content_file.write(json_str.encode(encoding="utf-8")) + doc_content_file.flush() + + subprocess.run( + [ + mk_signed_doc_path, + "build", + doc_content_file.name, + signed_doc_file.name, + metadata_file.name, + ], + capture_output=True, ) - assert ( - resp.status_code == 201 - ), f"Failed to publish document: {resp.status_code} - {resp.text}" - return doc_builder, role_id + subprocess.run( + [ + mk_signed_doc_path, + "sign", + signed_doc_file.name, + bip32_sk_hex, + cat_id, + ], + capture_output=True, + ) - return __factory__ + signed_doc_hex = signed_doc_file.read().hex() + return signed_doc_hex + +# ------------------- # +# Signed Docs Factory # +# ------------------- # +# return a Proposal document which is already published to the cat-gateway and the corresponding RoleID @pytest.fixture -def campaign_parameters_doc_factory(rbac_chain_factory): - def __factory__() -> tuple[SignedDocument, RoleID]: +def proposal_doc_factory(rbac_chain_factory, proposal_form_template_doc_factory): + def __factory__(parameter: SignedDocumentBase) -> tuple[str, Any, Any]: role_id = RoleID.PROPOSER - template = proposal_form_template_doc_factory() + template: SignedDocumentBase = proposal_form_template_doc_factory() rbac_chain = rbac_chain_factory() doc_id = uuid_v7.uuid_v7() metadata = { "id": doc_id, "ver": doc_id, - "type": DOC_TYPE["campaign_parameters"], + "type": DOC_TYPE["proposal"], "content-type": "application/json", "content-encoding": "br", - "parameters": [], + "parameters": [ + { "id": template.metadata["id"], "ver": template.metadata["ver"], "cid": "0x" }, + { "id": parameter.metadata["id"], "ver": parameter.metadata["ver"], "cid": "0x" }, + ], } - body = JSF(template).generate() + content = JSF(template.content).generate() - doc_builder = SignedDocument(metadata, body) + doc_builder = SignedDocument(metadata, content) (cat_id, sk_hex) = rbac_chain.cat_id_for_role(role_id) resp = document.put( @@ -152,17 +193,14 @@ def __factory__() -> tuple[SignedDocument, RoleID]: resp.status_code == 201 ), f"Failed to publish document: {resp.status_code} - {resp.text}" - return doc_builder, role_id + return doc_id, metadata, content return __factory__ -@pytest.fixture -def campaign_parameters_form_template_doc_factory(rbac_chain_factory): - None @pytest.fixture def proposal_form_template_doc_factory(rbac_chain_factory): - def __factory__() -> tuple[SignedDocument, RoleID]: + def __factory__() -> SignedDocumentBase: role_id = RoleID.PROPOSER with open("./test_data/signed_docs/proposal_form_template.json", "r") as json_file: metadata, content = json.load(json_file) @@ -179,7 +217,7 @@ def __factory__() -> tuple[SignedDocument, RoleID]: resp.status_code == 201 ), f"Failed to publish document: {resp.status_code} - {resp.text}" - return doc_builder, role_id + return SignedDocumentBase(metadata, content) return __factory__ @@ -229,71 +267,41 @@ def __factory__() -> tuple[SignedDocument, RoleID]: return __factory__ -def generic_doc_builder(rbac_chain, doc_type: str, parameters: list[dict], content: Any): - role_id = RoleID.PROPOSER - doc_id = uuid_v7.uuid_v7() - metadata = { - "content-encoding": "br", - "content-type": "application/json", - "id": doc_id, - "ver": doc_id, - "type": doc_type, - "parameters": parameters, - } - - doc_builder = SignedDocument(metadata, content) - (cat_id, sk_hex) = rbac_chain.cat_id_for_role(role_id) - - return { - "metadata": metadata, - "content": content, - "doc_builder": doc_builder, - "signed_doc": doc_builder.build_and_sign(cat_id, sk_hex), - "auth_token": rbac_chain.auth_token(), - "cat_id": cat_id, - "role_id": role_id - } +@pytest.fixture +def campaign_parameters_doc_factory(rbac_chain_factory): + def __factory__() -> tuple[SignedDocument, RoleID]: + role_id = RoleID.PROPOSER + template = proposal_form_template_doc_factory() + rbac_chain = rbac_chain_factory() + doc_id = uuid_v7.uuid_v7() -def build_signed_doc( - metadata_json: Dict[str, Any], - doc_content_json: Dict[str, Any], - bip32_sk_hex: str, - # corresponded to the `bip32_sk_hex` `cat_id` string - cat_id: str, - mk_signed_doc_path, -) -> str: - with NamedTemporaryFile() as metadata_file, NamedTemporaryFile() as doc_content_file, NamedTemporaryFile() as signed_doc_file: + metadata = { + "id": doc_id, + "ver": doc_id, + "type": DOC_TYPE["campaign_parameters"], + "content-type": "application/json", + "content-encoding": "br", + "parameters": [], + } + body = JSF(template).generate() - json_str = json.dumps(metadata_json) - metadata_file.write(json_str.encode(encoding="utf-8")) - metadata_file.flush() + doc_builder = SignedDocument(metadata, body) + (cat_id, sk_hex) = rbac_chain.cat_id_for_role(role_id) + + resp = document.put( + data=doc_builder.build_and_sign(cat_id, sk_hex), + token=rbac_chain.auth_token(), + ) + assert ( + resp.status_code == 201 + ), f"Failed to publish document: {resp.status_code} - {resp.text}" - json_str = json.dumps(doc_content_json) - doc_content_file.write(json_str.encode(encoding="utf-8")) - doc_content_file.flush() + return doc_builder, role_id - subprocess.run( - [ - mk_signed_doc_path, - "build", - doc_content_file.name, - signed_doc_file.name, - metadata_file.name, - ], - capture_output=True, - ) + return __factory__ - subprocess.run( - [ - mk_signed_doc_path, - "sign", - signed_doc_file.name, - bip32_sk_hex, - cat_id, - ], - capture_output=True, - ) - signed_doc_hex = signed_doc_file.read().hex() - return signed_doc_hex +@pytest.fixture +def campaign_parameters_form_template_doc_factory(rbac_chain_factory): + None From adf3ba09f158f9bddab971f42fe17290e7b3f32b Mon Sep 17 00:00:00 2001 From: Apisit Ritruengroj Date: Thu, 20 Nov 2025 18:45:00 +0700 Subject: [PATCH 11/40] feat: pre final --- .../integration/signed_doc/test_signed_doc.py | 8 +- .../tests/api_tests/utils/signed_doc.py | 242 ++++++++++++------ 2 files changed, 171 insertions(+), 79 deletions(-) diff --git a/catalyst-gateway/tests/api_tests/integration/signed_doc/test_signed_doc.py b/catalyst-gateway/tests/api_tests/integration/signed_doc/test_signed_doc.py index 0878cdbd8a0..76bd66e332f 100644 --- a/catalyst-gateway/tests/api_tests/integration/signed_doc/test_signed_doc.py +++ b/catalyst-gateway/tests/api_tests/integration/signed_doc/test_signed_doc.py @@ -3,15 +3,13 @@ from api.v1 import document as document_v1 from api.v2 import document as document_v2 from utils.rbac_chain import rbac_chain_factory -from utils.signed_doc import ( - proposal_doc_factory, -) +from utils.signed_doc import proposal_doc_factory, ProposalParameterType @pytest.mark.preprod_indexing def test_document_put_and_get_endpoints(proposal_doc_factory, rbac_chain_factory): rbac_chain = rbac_chain_factory() - (proposal_doc, role_id) = proposal_doc_factory() + (proposal_doc, role_id) = proposal_doc_factory(ProposalParameterType.CATEGORY) (cat_id, sk_hex) = rbac_chain.cat_id_for_role(role_id) proposal_doc_id = proposal_doc.metadata["id"] @@ -77,7 +75,7 @@ def test_document_index_endpoint( proposal_doc_factory, rbac_chain_factory, ): - (doc, role_id) = proposal_doc_factory() + (doc, role_id) = proposal_doc_factory(ProposalParameterType.CATEGORY) rbac_chain = rbac_chain_factory() (cat_id, sk_hex) = rbac_chain.cat_id_for_role(role_id) diff --git a/catalyst-gateway/tests/api_tests/utils/signed_doc.py b/catalyst-gateway/tests/api_tests/utils/signed_doc.py index 73b4004ac23..0bdf0ad8a65 100644 --- a/catalyst-gateway/tests/api_tests/utils/signed_doc.py +++ b/catalyst-gateway/tests/api_tests/utils/signed_doc.py @@ -10,6 +10,8 @@ from utils.rbac_chain import rbac_chain_factory, RoleID from tempfile import NamedTemporaryFile from jsf import JSF +from rbac_chain import RBACChain +from enum import IntEnum DOC_TYPE = { @@ -86,7 +88,16 @@ def build_and_sign( ) -def generic_doc_builder(rbac_chain, doc_type: str, parameters: list[dict], content: Any): +class DocBuilderReturns: + def __init__(self, doc_builder: SignedDocument, signed_doc: str, auth_token: str, cat_id: str, role_id: RoleID): + self.doc_builder = doc_builder + self.signed_doc = signed_doc + self.auth_token = auth_token + self.cat_id = cat_id + self.role_id = role_id + + +def generic_doc_builder(rbac_chain: RBACChain, doc_type: str, parameters: list[dict], content: Any) -> DocBuilderReturns: role_id = RoleID.PROPOSER doc_id = uuid_v7.uuid_v7() metadata = { @@ -101,15 +112,13 @@ def generic_doc_builder(rbac_chain, doc_type: str, parameters: list[dict], conte doc_builder = SignedDocument(metadata, content) (cat_id, sk_hex) = rbac_chain.cat_id_for_role(role_id) - return { - "metadata": metadata, - "content": content, - "doc_builder": doc_builder, - "signed_doc": doc_builder.build_and_sign(cat_id, sk_hex), - "auth_token": rbac_chain.auth_token(), - "cat_id": cat_id, - "role_id": role_id - } + return DocBuilderReturns( + doc_builder=doc_builder, + signed_doc=doc_builder.build_and_sign(cat_id, sk_hex), + auth_token=rbac_chain.auth_token(), + cat_id=cat_id, + role_id=role_id, + ) def build_signed_doc( @@ -160,40 +169,48 @@ def build_signed_doc( # Signed Docs Factory # # ------------------- # + +class ProposalParameterType(IntEnum): + CATEGORY = 0 + CAMPAIGN = 1 + BRAND = 2 + + # return a Proposal document which is already published to the cat-gateway and the corresponding RoleID @pytest.fixture -def proposal_doc_factory(rbac_chain_factory, proposal_form_template_doc_factory): - def __factory__(parameter: SignedDocumentBase) -> tuple[str, Any, Any]: - role_id = RoleID.PROPOSER +def proposal_doc_factory(rbac_chain_factory, proposal_form_template_doc_factory, category_parameters_doc_factory, campaign_parameters_doc_factory, brand_parameters_doc_factory): + def __factory__(parameter_type: ProposalParameterType) -> tuple[SignedDocument, RoleID]: + param: SignedDocumentBase + if parameter_type == ProposalParameterType.CATEGORY: + param = category_parameters_doc_factory() + elif parameter_type == ProposalParameterType.CAMPAIGN: + param = campaign_parameters_doc_factory() + elif parameter_type == ProposalParameterType.BRAND: + param = brand_parameters_doc_factory() + else: + raise Exception("Invalid parameter type for proposal document") + template: SignedDocumentBase = proposal_form_template_doc_factory() - rbac_chain = rbac_chain_factory() - doc_id = uuid_v7.uuid_v7() - - metadata = { - "id": doc_id, - "ver": doc_id, - "type": DOC_TYPE["proposal"], - "content-type": "application/json", - "content-encoding": "br", - "parameters": [ + + result = generic_doc_builder( + rbac_chain_factory(), + DOC_TYPE["proposal"], + [ { "id": template.metadata["id"], "ver": template.metadata["ver"], "cid": "0x" }, - { "id": parameter.metadata["id"], "ver": parameter.metadata["ver"], "cid": "0x" }, + { "id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x" } ], - } - content = JSF(template.content).generate() - - doc_builder = SignedDocument(metadata, content) - (cat_id, sk_hex) = rbac_chain.cat_id_for_role(role_id) + JSF(template.content).generate() + ) resp = document.put( - data=doc_builder.build_and_sign(cat_id, sk_hex), - token=rbac_chain.auth_token(), + data=result.signed_doc, + token=result.auth_token, ) assert ( resp.status_code == 201 ), f"Failed to publish document: {resp.status_code} - {resp.text}" - return doc_id, metadata, content + return result.doc_builder, result.role_id return __factory__ @@ -223,85 +240,162 @@ def __factory__() -> SignedDocumentBase: @pytest.fixture -def category_parameters_doc_factory(rbac_chain_factory): - def __factory__() -> tuple[SignedDocument, RoleID]: - with open("./test_data/signed_docs/category_parameters.json", "r") as json_file: - metadata, content = json.load(json_file) +def category_parameters_doc_factory(rbac_chain_factory, category_parameters_form_template_doc_factory, campaign_parameters_doc_factory): + def __factory__() -> SignedDocumentBase: + template: SignedDocumentBase = category_parameters_form_template_doc_factory() + param: SignedDocumentBase = campaign_parameters_doc_factory() - doc_builder = SignedDocument(metadata, content) - rbac_chain = rbac_chain_factory() - (cat_id, sk_hex) = rbac_chain.cat_id_for_role(RoleID.PROPOSER) + result = generic_doc_builder( + rbac_chain_factory(), + DOC_TYPE["category_parameters"], + [ + { "id": template.metadata["id"], "ver": template.metadata["ver"], "cid": "0x" }, + { "id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x" } + ], + JSF(template.content).generate() + ) resp = document.put( - data=doc_builder.build_and_sign(cat_id, sk_hex), - token=rbac_chain.auth_token(), + data=result.signed_doc, + token=result.auth_token, ) assert ( resp.status_code == 201 ), f"Failed to publish document: {resp.status_code} - {resp.text}" - return doc_builder, RoleID.PROPOSER + return SignedDocumentBase(result.doc_builder.metadata, result.doc_builder.content) return __factory__ @pytest.fixture -def category_parameters_form_template_doc_factory(rbac_chain_factory): - def __factory__() -> tuple[SignedDocument, RoleID]: - builder = generic_doc_builder( +def category_parameters_form_template_doc_factory(rbac_chain_factory, campaign_parameters_doc_factory): + def __factory__() -> SignedDocumentBase: + param: SignedDocumentBase = campaign_parameters_doc_factory() + + result = generic_doc_builder( rbac_chain_factory(), DOC_TYPE["category_parameters_form_template"], - [], - {} + [ + { "id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x" } + ], + { "type": "object" } ) resp = document.put( - data=builder["signed_doc"], - token=builder["auth_token"], + data=result.signed_doc, + token=result.auth_token, ) assert ( resp.status_code == 201 ), f"Failed to publish document: {resp.status_code} - {resp.text}" - return builder["doc_builder"], builder["role_id"] + return SignedDocumentBase(result.doc_builder.metadata, result.doc_builder.content) return __factory__ @pytest.fixture -def campaign_parameters_doc_factory(rbac_chain_factory): - def __factory__() -> tuple[SignedDocument, RoleID]: - role_id = RoleID.PROPOSER - template = proposal_form_template_doc_factory() - rbac_chain = rbac_chain_factory() - doc_id = uuid_v7.uuid_v7() - - metadata = { - "id": doc_id, - "ver": doc_id, - "type": DOC_TYPE["campaign_parameters"], - "content-type": "application/json", - "content-encoding": "br", - "parameters": [], - } - body = JSF(template).generate() - - doc_builder = SignedDocument(metadata, body) - (cat_id, sk_hex) = rbac_chain.cat_id_for_role(role_id) +def campaign_parameters_doc_factory(rbac_chain_factory, campaign_parameters_form_template_doc_factory, brand_parameters_doc_factory): + def __factory__() -> SignedDocumentBase: + template: SignedDocumentBase = campaign_parameters_form_template_doc_factory() + param: SignedDocumentBase = brand_parameters_doc_factory() + + result = generic_doc_builder( + rbac_chain_factory(), + DOC_TYPE["campaign_parameters"], + [ + { "id": template.metadata["id"], "ver": template.metadata["ver"], "cid": "0x" }, + { "id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x" } + ], + JSF(template.content).generate() + ) resp = document.put( - data=doc_builder.build_and_sign(cat_id, sk_hex), - token=rbac_chain.auth_token(), + data=result.signed_doc, + token=result.auth_token, + ) + assert ( + resp.status_code == 201 + ), f"Failed to publish document: {resp.status_code} - {resp.text}" + + return SignedDocumentBase(result.doc_builder.metadata, result.doc_builder.content) + + return __factory__ + + +@pytest.fixture +def campaign_parameters_form_template_doc_factory(rbac_chain_factory, brand_parameters_doc_factory): + def __factory__() -> SignedDocumentBase: + param: SignedDocumentBase = brand_parameters_doc_factory() + + result = generic_doc_builder( + rbac_chain_factory(), + DOC_TYPE["campaign_parameters_form_template"], + [ + { "id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x" } + ], + { "type": "object" } + ) + + resp = document.put( + data=result.signed_doc, + token=result.auth_token, ) assert ( resp.status_code == 201 ), f"Failed to publish document: {resp.status_code} - {resp.text}" - return doc_builder, role_id + return SignedDocumentBase(result.doc_builder.metadata, result.doc_builder.content) return __factory__ @pytest.fixture -def campaign_parameters_form_template_doc_factory(rbac_chain_factory): - None +def brand_parameters_doc_factory(rbac_chain_factory, brand_parameters_form_template_doc_factory): + def __factory__() -> SignedDocumentBase: + template: SignedDocumentBase = brand_parameters_form_template_doc_factory() + + result = generic_doc_builder( + rbac_chain_factory(), + DOC_TYPE["brand_parameters"], + [ + { "id": template.metadata["id"], "ver": template.metadata["ver"], "cid": "0x" } + ], + JSF(template.content).generate() + ) + + resp = document.put( + data=result.signed_doc, + token=result.auth_token, + ) + assert ( + resp.status_code == 201 + ), f"Failed to publish document: {resp.status_code} - {resp.text}" + + return SignedDocumentBase(result.doc_builder.metadata, result.doc_builder.content) + + return __factory__ + + +@pytest.fixture +def brand_parameters_form_template_doc_factory(rbac_chain_factory): + def __factory__() -> SignedDocumentBase: + result = generic_doc_builder( + rbac_chain_factory(), + DOC_TYPE["brand_parameters_form_template"], + [], + { "type": "object" } + ) + + resp = document.put( + data=result.signed_doc, + token=result.auth_token, + ) + assert ( + resp.status_code == 201 + ), f"Failed to publish document: {resp.status_code} - {resp.text}" + + return SignedDocumentBase(result.doc_builder.metadata, result.doc_builder.content) + + return __factory__ \ No newline at end of file From a3f3776d51c89b9d98ede5f435328767cbf11698 Mon Sep 17 00:00:00 2001 From: Apisit Ritruengroj Date: Thu, 20 Nov 2025 18:55:00 +0700 Subject: [PATCH 12/40] chore: fmt --- .../tests/api_tests/utils/signed_doc.py | 204 +++++++++++------- 1 file changed, 132 insertions(+), 72 deletions(-) diff --git a/catalyst-gateway/tests/api_tests/utils/signed_doc.py b/catalyst-gateway/tests/api_tests/utils/signed_doc.py index 0bdf0ad8a65..e2c3f5c4b2b 100644 --- a/catalyst-gateway/tests/api_tests/utils/signed_doc.py +++ b/catalyst-gateway/tests/api_tests/utils/signed_doc.py @@ -89,7 +89,14 @@ def build_and_sign( class DocBuilderReturns: - def __init__(self, doc_builder: SignedDocument, signed_doc: str, auth_token: str, cat_id: str, role_id: RoleID): + def __init__( + self, + doc_builder: SignedDocument, + signed_doc: str, + auth_token: str, + cat_id: str, + role_id: RoleID, + ): self.doc_builder = doc_builder self.signed_doc = signed_doc self.auth_token = auth_token @@ -97,7 +104,9 @@ def __init__(self, doc_builder: SignedDocument, signed_doc: str, auth_token: str self.role_id = role_id -def generic_doc_builder(rbac_chain: RBACChain, doc_type: str, parameters: list[dict], content: Any) -> DocBuilderReturns: +def generic_doc_builder( + rbac_chain: RBACChain, doc_type: str, parameters: list[dict], content: Any +) -> DocBuilderReturns: role_id = RoleID.PROPOSER doc_id = uuid_v7.uuid_v7() metadata = { @@ -129,8 +138,11 @@ def build_signed_doc( cat_id: str, mk_signed_doc_path, ) -> str: - with NamedTemporaryFile() as metadata_file, NamedTemporaryFile() as doc_content_file, NamedTemporaryFile() as signed_doc_file: - + with ( + NamedTemporaryFile() as metadata_file, + NamedTemporaryFile() as doc_content_file, + NamedTemporaryFile() as signed_doc_file, + ): json_str = json.dumps(metadata_json) metadata_file.write(json_str.encode(encoding="utf-8")) metadata_file.flush() @@ -163,7 +175,7 @@ def build_signed_doc( signed_doc_hex = signed_doc_file.read().hex() return signed_doc_hex - + # ------------------- # # Signed Docs Factory # @@ -178,8 +190,16 @@ class ProposalParameterType(IntEnum): # return a Proposal document which is already published to the cat-gateway and the corresponding RoleID @pytest.fixture -def proposal_doc_factory(rbac_chain_factory, proposal_form_template_doc_factory, category_parameters_doc_factory, campaign_parameters_doc_factory, brand_parameters_doc_factory): - def __factory__(parameter_type: ProposalParameterType) -> tuple[SignedDocument, RoleID]: +def proposal_doc_factory( + rbac_chain_factory, + proposal_form_template_doc_factory, + category_parameters_doc_factory, + campaign_parameters_doc_factory, + brand_parameters_doc_factory, +): + def __factory__( + parameter_type: ProposalParameterType, + ) -> tuple[SignedDocument, RoleID]: param: SignedDocumentBase if parameter_type == ProposalParameterType.CATEGORY: param = category_parameters_doc_factory() @@ -196,19 +216,23 @@ def __factory__(parameter_type: ProposalParameterType) -> tuple[SignedDocument, rbac_chain_factory(), DOC_TYPE["proposal"], [ - { "id": template.metadata["id"], "ver": template.metadata["ver"], "cid": "0x" }, - { "id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x" } + { + "id": template.metadata["id"], + "ver": template.metadata["ver"], + "cid": "0x", + }, + {"id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x"}, ], - JSF(template.content).generate() + JSF(template.content).generate(), ) - + resp = document.put( data=result.signed_doc, token=result.auth_token, ) - assert ( - resp.status_code == 201 - ), f"Failed to publish document: {resp.status_code} - {resp.text}" + assert resp.status_code == 201, ( + f"Failed to publish document: {resp.status_code} - {resp.text}" + ) return result.doc_builder, result.role_id @@ -219,20 +243,22 @@ def __factory__(parameter_type: ProposalParameterType) -> tuple[SignedDocument, def proposal_form_template_doc_factory(rbac_chain_factory): def __factory__() -> SignedDocumentBase: role_id = RoleID.PROPOSER - with open("./test_data/signed_docs/proposal_form_template.json", "r") as json_file: + with open( + "./test_data/signed_docs/proposal_form_template.json", "r" + ) as json_file: metadata, content = json.load(json_file) doc_builder = SignedDocument(metadata, content) rbac_chain = rbac_chain_factory() (cat_id, sk_hex) = rbac_chain.cat_id_for_role(role_id) - + resp = document.put( data=doc_builder.build_and_sign(cat_id, sk_hex), token=rbac_chain.auth_token(), ) - assert ( - resp.status_code == 201 - ), f"Failed to publish document: {resp.status_code} - {resp.text}" + assert resp.status_code == 201, ( + f"Failed to publish document: {resp.status_code} - {resp.text}" + ) return SignedDocumentBase(metadata, content) @@ -240,7 +266,11 @@ def __factory__() -> SignedDocumentBase: @pytest.fixture -def category_parameters_doc_factory(rbac_chain_factory, category_parameters_form_template_doc_factory, campaign_parameters_doc_factory): +def category_parameters_doc_factory( + rbac_chain_factory, + category_parameters_form_template_doc_factory, + campaign_parameters_doc_factory, +): def __factory__() -> SignedDocumentBase: template: SignedDocumentBase = category_parameters_form_template_doc_factory() param: SignedDocumentBase = campaign_parameters_doc_factory() @@ -249,54 +279,66 @@ def __factory__() -> SignedDocumentBase: rbac_chain_factory(), DOC_TYPE["category_parameters"], [ - { "id": template.metadata["id"], "ver": template.metadata["ver"], "cid": "0x" }, - { "id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x" } + { + "id": template.metadata["id"], + "ver": template.metadata["ver"], + "cid": "0x", + }, + {"id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x"}, ], - JSF(template.content).generate() + JSF(template.content).generate(), ) - + resp = document.put( data=result.signed_doc, token=result.auth_token, ) - assert ( - resp.status_code == 201 - ), f"Failed to publish document: {resp.status_code} - {resp.text}" + assert resp.status_code == 201, ( + f"Failed to publish document: {resp.status_code} - {resp.text}" + ) - return SignedDocumentBase(result.doc_builder.metadata, result.doc_builder.content) + return SignedDocumentBase( + result.doc_builder.metadata, result.doc_builder.content + ) return __factory__ @pytest.fixture -def category_parameters_form_template_doc_factory(rbac_chain_factory, campaign_parameters_doc_factory): +def category_parameters_form_template_doc_factory( + rbac_chain_factory, campaign_parameters_doc_factory +): def __factory__() -> SignedDocumentBase: param: SignedDocumentBase = campaign_parameters_doc_factory() result = generic_doc_builder( rbac_chain_factory(), DOC_TYPE["category_parameters_form_template"], - [ - { "id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x" } - ], - { "type": "object" } + [{"id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x"}], + {"type": "object"}, ) - + resp = document.put( data=result.signed_doc, token=result.auth_token, ) - assert ( - resp.status_code == 201 - ), f"Failed to publish document: {resp.status_code} - {resp.text}" + assert resp.status_code == 201, ( + f"Failed to publish document: {resp.status_code} - {resp.text}" + ) - return SignedDocumentBase(result.doc_builder.metadata, result.doc_builder.content) + return SignedDocumentBase( + result.doc_builder.metadata, result.doc_builder.content + ) return __factory__ @pytest.fixture -def campaign_parameters_doc_factory(rbac_chain_factory, campaign_parameters_form_template_doc_factory, brand_parameters_doc_factory): +def campaign_parameters_doc_factory( + rbac_chain_factory, + campaign_parameters_form_template_doc_factory, + brand_parameters_doc_factory, +): def __factory__() -> SignedDocumentBase: template: SignedDocumentBase = campaign_parameters_form_template_doc_factory() param: SignedDocumentBase = brand_parameters_doc_factory() @@ -305,54 +347,64 @@ def __factory__() -> SignedDocumentBase: rbac_chain_factory(), DOC_TYPE["campaign_parameters"], [ - { "id": template.metadata["id"], "ver": template.metadata["ver"], "cid": "0x" }, - { "id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x" } + { + "id": template.metadata["id"], + "ver": template.metadata["ver"], + "cid": "0x", + }, + {"id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x"}, ], - JSF(template.content).generate() + JSF(template.content).generate(), ) - + resp = document.put( data=result.signed_doc, token=result.auth_token, ) - assert ( - resp.status_code == 201 - ), f"Failed to publish document: {resp.status_code} - {resp.text}" + assert resp.status_code == 201, ( + f"Failed to publish document: {resp.status_code} - {resp.text}" + ) - return SignedDocumentBase(result.doc_builder.metadata, result.doc_builder.content) + return SignedDocumentBase( + result.doc_builder.metadata, result.doc_builder.content + ) return __factory__ @pytest.fixture -def campaign_parameters_form_template_doc_factory(rbac_chain_factory, brand_parameters_doc_factory): +def campaign_parameters_form_template_doc_factory( + rbac_chain_factory, brand_parameters_doc_factory +): def __factory__() -> SignedDocumentBase: param: SignedDocumentBase = brand_parameters_doc_factory() result = generic_doc_builder( rbac_chain_factory(), DOC_TYPE["campaign_parameters_form_template"], - [ - { "id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x" } - ], - { "type": "object" } + [{"id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x"}], + {"type": "object"}, ) - + resp = document.put( data=result.signed_doc, token=result.auth_token, ) - assert ( - resp.status_code == 201 - ), f"Failed to publish document: {resp.status_code} - {resp.text}" + assert resp.status_code == 201, ( + f"Failed to publish document: {resp.status_code} - {resp.text}" + ) - return SignedDocumentBase(result.doc_builder.metadata, result.doc_builder.content) + return SignedDocumentBase( + result.doc_builder.metadata, result.doc_builder.content + ) return __factory__ @pytest.fixture -def brand_parameters_doc_factory(rbac_chain_factory, brand_parameters_form_template_doc_factory): +def brand_parameters_doc_factory( + rbac_chain_factory, brand_parameters_form_template_doc_factory +): def __factory__() -> SignedDocumentBase: template: SignedDocumentBase = brand_parameters_form_template_doc_factory() @@ -360,20 +412,26 @@ def __factory__() -> SignedDocumentBase: rbac_chain_factory(), DOC_TYPE["brand_parameters"], [ - { "id": template.metadata["id"], "ver": template.metadata["ver"], "cid": "0x" } + { + "id": template.metadata["id"], + "ver": template.metadata["ver"], + "cid": "0x", + } ], - JSF(template.content).generate() + JSF(template.content).generate(), ) - + resp = document.put( data=result.signed_doc, token=result.auth_token, ) - assert ( - resp.status_code == 201 - ), f"Failed to publish document: {resp.status_code} - {resp.text}" + assert resp.status_code == 201, ( + f"Failed to publish document: {resp.status_code} - {resp.text}" + ) - return SignedDocumentBase(result.doc_builder.metadata, result.doc_builder.content) + return SignedDocumentBase( + result.doc_builder.metadata, result.doc_builder.content + ) return __factory__ @@ -385,17 +443,19 @@ def __factory__() -> SignedDocumentBase: rbac_chain_factory(), DOC_TYPE["brand_parameters_form_template"], [], - { "type": "object" } + {"type": "object"}, ) - + resp = document.put( data=result.signed_doc, token=result.auth_token, ) - assert ( - resp.status_code == 201 - ), f"Failed to publish document: {resp.status_code} - {resp.text}" + assert resp.status_code == 201, ( + f"Failed to publish document: {resp.status_code} - {resp.text}" + ) - return SignedDocumentBase(result.doc_builder.metadata, result.doc_builder.content) + return SignedDocumentBase( + result.doc_builder.metadata, result.doc_builder.content + ) - return __factory__ \ No newline at end of file + return __factory__ From ba9e6e1a0eb7f79979e719884762ba5f557f0537 Mon Sep 17 00:00:00 2001 From: Apisit Ritruengroj Date: Thu, 20 Nov 2025 18:59:03 +0700 Subject: [PATCH 13/40] chore: add jsf --- catalyst-gateway/tests/api_tests/pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/catalyst-gateway/tests/api_tests/pyproject.toml b/catalyst-gateway/tests/api_tests/pyproject.toml index 55108ef4eb9..b0585496301 100644 --- a/catalyst-gateway/tests/api_tests/pyproject.toml +++ b/catalyst-gateway/tests/api_tests/pyproject.toml @@ -25,6 +25,7 @@ certifi = "^2025.1.31" cbor2 = "^5.6.5" uuid = "^1.30" httpx = "^0.28.1" +jsf = "^0.11.2" [tool.poetry.group.dev.dependencies] pytest = "^8.0.0" From e8d7305f8c0b92a277725a5e6573778d716c31df Mon Sep 17 00:00:00 2001 From: Apisit Ritruengroj Date: Thu, 20 Nov 2025 19:00:08 +0700 Subject: [PATCH 14/40] chore: remove unused template --- .../proposal_comment_form_template.json | 62 ------------------- 1 file changed, 62 deletions(-) delete mode 100644 catalyst-gateway/tests/api_tests/test_data/signed_docs/proposal_comment_form_template.json diff --git a/catalyst-gateway/tests/api_tests/test_data/signed_docs/proposal_comment_form_template.json b/catalyst-gateway/tests/api_tests/test_data/signed_docs/proposal_comment_form_template.json deleted file mode 100644 index 7cb1ee9a841..00000000000 --- a/catalyst-gateway/tests/api_tests/test_data/signed_docs/proposal_comment_form_template.json +++ /dev/null @@ -1,62 +0,0 @@ -[ - { - "content-encoding": "br", - "content-type": "application/json", - "id": "0199802c-21b4-7b2c-aafd-0af557e8408c", - "parameters": [ - { - "cid": "0x", - "id": "0199802c-21b4-721f-aa1d-5123b006879e", - "ver": "0199802c-21b4-721f-aa1d-5123b006879e" - } - ], - "type": "0b8424d4-ebfd-46e3-9577-1775a69d290c", - "ver": "0199802c-21b4-7b2c-aafd-0af557e8408c" - }, - { - "$id": "https://cardano.org/schemas/catalyst/comments", - "$schema": "http://json-schema.org/draft-07/schema#", - "definitions": { - "multiLineTextEntry": { - "$comment": "UI - Multiline text entry without any markup or rich text capability.", - "pattern": "^[\\S\\s]+$", - "type": "string", - "x-note": "Enter multiple lines of plain text. You can use line breaks but no special formatting." - } - }, - "description": "Schema for comments on Catalyst proposals", - "maintainers": [ - { - "name": "Catalyst Team", - "url": "https://projectcatalyst.io/" - } - ], - "properties": { - "comment": { - "description": "The comments on the proposal", - "properties": { - "content": { - "$ref": "#/definitions/multiLineTextEntry", - "description": "The comment text content", - "maxLength": 2000, - "minLength": 1 - } - }, - "required": [ - "content" - ], - "type": "object" - } - }, - "required": [ - "comment" - ], - "title": "Proposal Comments Schema", - "type": "object", - "x-changelog": { - "2025-06-16": [ - "Updating maxLength for content text" - ] - } - } -] \ No newline at end of file From 6ac97a056b20793bcee5cb9b6e8647fa7c1638ae Mon Sep 17 00:00:00 2001 From: Apisit Ritruengroj Date: Thu, 20 Nov 2025 20:05:26 +0700 Subject: [PATCH 15/40] chore: once fixture --- .../tests/api_tests/utils/signed_doc.py | 311 ++++++++---------- 1 file changed, 139 insertions(+), 172 deletions(-) diff --git a/catalyst-gateway/tests/api_tests/utils/signed_doc.py b/catalyst-gateway/tests/api_tests/utils/signed_doc.py index e2c3f5c4b2b..e978be10c72 100644 --- a/catalyst-gateway/tests/api_tests/utils/signed_doc.py +++ b/catalyst-gateway/tests/api_tests/utils/signed_doc.py @@ -240,29 +240,24 @@ def __factory__( @pytest.fixture -def proposal_form_template_doc_factory(rbac_chain_factory): - def __factory__() -> SignedDocumentBase: - role_id = RoleID.PROPOSER - with open( - "./test_data/signed_docs/proposal_form_template.json", "r" - ) as json_file: - metadata, content = json.load(json_file) - - doc_builder = SignedDocument(metadata, content) - rbac_chain = rbac_chain_factory() - (cat_id, sk_hex) = rbac_chain.cat_id_for_role(role_id) +def proposal_form_template_doc_factory(rbac_chain_factory) -> SignedDocumentBase: + role_id = RoleID.PROPOSER + with open("./test_data/signed_docs/proposal_form_template.json", "r") as json_file: + metadata, content = json.load(json_file) - resp = document.put( - data=doc_builder.build_and_sign(cat_id, sk_hex), - token=rbac_chain.auth_token(), - ) - assert resp.status_code == 201, ( - f"Failed to publish document: {resp.status_code} - {resp.text}" - ) + doc_builder = SignedDocument(metadata, content) + rbac_chain = rbac_chain_factory() + (cat_id, sk_hex) = rbac_chain.cat_id_for_role(role_id) - return SignedDocumentBase(metadata, content) + resp = document.put( + data=doc_builder.build_and_sign(cat_id, sk_hex), + token=rbac_chain.auth_token(), + ) + assert resp.status_code == 201, ( + f"Failed to publish document: {resp.status_code} - {resp.text}" + ) - return __factory__ + return SignedDocumentBase(metadata, content) @pytest.fixture @@ -270,67 +265,57 @@ def category_parameters_doc_factory( rbac_chain_factory, category_parameters_form_template_doc_factory, campaign_parameters_doc_factory, -): - def __factory__() -> SignedDocumentBase: - template: SignedDocumentBase = category_parameters_form_template_doc_factory() - param: SignedDocumentBase = campaign_parameters_doc_factory() - - result = generic_doc_builder( - rbac_chain_factory(), - DOC_TYPE["category_parameters"], - [ - { - "id": template.metadata["id"], - "ver": template.metadata["ver"], - "cid": "0x", - }, - {"id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x"}, - ], - JSF(template.content).generate(), - ) - - resp = document.put( - data=result.signed_doc, - token=result.auth_token, - ) - assert resp.status_code == 201, ( - f"Failed to publish document: {resp.status_code} - {resp.text}" - ) +) -> SignedDocumentBase: + template: SignedDocumentBase = category_parameters_form_template_doc_factory() + param: SignedDocumentBase = campaign_parameters_doc_factory() + + result = generic_doc_builder( + rbac_chain_factory(), + DOC_TYPE["category_parameters"], + [ + { + "id": template.metadata["id"], + "ver": template.metadata["ver"], + "cid": "0x", + }, + {"id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x"}, + ], + JSF(template.content).generate(), + ) - return SignedDocumentBase( - result.doc_builder.metadata, result.doc_builder.content - ) + resp = document.put( + data=result.signed_doc, + token=result.auth_token, + ) + assert resp.status_code == 201, ( + f"Failed to publish document: {resp.status_code} - {resp.text}" + ) - return __factory__ + return SignedDocumentBase(result.doc_builder.metadata, result.doc_builder.content) @pytest.fixture def category_parameters_form_template_doc_factory( rbac_chain_factory, campaign_parameters_doc_factory -): - def __factory__() -> SignedDocumentBase: - param: SignedDocumentBase = campaign_parameters_doc_factory() - - result = generic_doc_builder( - rbac_chain_factory(), - DOC_TYPE["category_parameters_form_template"], - [{"id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x"}], - {"type": "object"}, - ) - - resp = document.put( - data=result.signed_doc, - token=result.auth_token, - ) - assert resp.status_code == 201, ( - f"Failed to publish document: {resp.status_code} - {resp.text}" - ) +) -> SignedDocumentBase: + param: SignedDocumentBase = campaign_parameters_doc_factory() + + result = generic_doc_builder( + rbac_chain_factory(), + DOC_TYPE["category_parameters_form_template"], + [{"id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x"}], + {"type": "object"}, + ) - return SignedDocumentBase( - result.doc_builder.metadata, result.doc_builder.content - ) + resp = document.put( + data=result.signed_doc, + token=result.auth_token, + ) + assert resp.status_code == 201, ( + f"Failed to publish document: {resp.status_code} - {resp.text}" + ) - return __factory__ + return SignedDocumentBase(result.doc_builder.metadata, result.doc_builder.content) @pytest.fixture @@ -338,124 +323,106 @@ def campaign_parameters_doc_factory( rbac_chain_factory, campaign_parameters_form_template_doc_factory, brand_parameters_doc_factory, -): - def __factory__() -> SignedDocumentBase: - template: SignedDocumentBase = campaign_parameters_form_template_doc_factory() - param: SignedDocumentBase = brand_parameters_doc_factory() - - result = generic_doc_builder( - rbac_chain_factory(), - DOC_TYPE["campaign_parameters"], - [ - { - "id": template.metadata["id"], - "ver": template.metadata["ver"], - "cid": "0x", - }, - {"id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x"}, - ], - JSF(template.content).generate(), - ) - - resp = document.put( - data=result.signed_doc, - token=result.auth_token, - ) - assert resp.status_code == 201, ( - f"Failed to publish document: {resp.status_code} - {resp.text}" - ) +) -> SignedDocumentBase: + template: SignedDocumentBase = campaign_parameters_form_template_doc_factory() + param: SignedDocumentBase = brand_parameters_doc_factory() + + result = generic_doc_builder( + rbac_chain_factory(), + DOC_TYPE["campaign_parameters"], + [ + { + "id": template.metadata["id"], + "ver": template.metadata["ver"], + "cid": "0x", + }, + {"id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x"}, + ], + JSF(template.content).generate(), + ) - return SignedDocumentBase( - result.doc_builder.metadata, result.doc_builder.content - ) + resp = document.put( + data=result.signed_doc, + token=result.auth_token, + ) + assert resp.status_code == 201, ( + f"Failed to publish document: {resp.status_code} - {resp.text}" + ) - return __factory__ + return SignedDocumentBase(result.doc_builder.metadata, result.doc_builder.content) @pytest.fixture def campaign_parameters_form_template_doc_factory( rbac_chain_factory, brand_parameters_doc_factory -): - def __factory__() -> SignedDocumentBase: - param: SignedDocumentBase = brand_parameters_doc_factory() - - result = generic_doc_builder( - rbac_chain_factory(), - DOC_TYPE["campaign_parameters_form_template"], - [{"id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x"}], - {"type": "object"}, - ) - - resp = document.put( - data=result.signed_doc, - token=result.auth_token, - ) - assert resp.status_code == 201, ( - f"Failed to publish document: {resp.status_code} - {resp.text}" - ) +) -> SignedDocumentBase: + param: SignedDocumentBase = brand_parameters_doc_factory() + + result = generic_doc_builder( + rbac_chain_factory(), + DOC_TYPE["campaign_parameters_form_template"], + [{"id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x"}], + {"type": "object"}, + ) - return SignedDocumentBase( - result.doc_builder.metadata, result.doc_builder.content - ) + resp = document.put( + data=result.signed_doc, + token=result.auth_token, + ) + assert resp.status_code == 201, ( + f"Failed to publish document: {resp.status_code} - {resp.text}" + ) - return __factory__ + return SignedDocumentBase(result.doc_builder.metadata, result.doc_builder.content) @pytest.fixture def brand_parameters_doc_factory( rbac_chain_factory, brand_parameters_form_template_doc_factory -): - def __factory__() -> SignedDocumentBase: - template: SignedDocumentBase = brand_parameters_form_template_doc_factory() - - result = generic_doc_builder( - rbac_chain_factory(), - DOC_TYPE["brand_parameters"], - [ - { - "id": template.metadata["id"], - "ver": template.metadata["ver"], - "cid": "0x", - } - ], - JSF(template.content).generate(), - ) - - resp = document.put( - data=result.signed_doc, - token=result.auth_token, - ) - assert resp.status_code == 201, ( - f"Failed to publish document: {resp.status_code} - {resp.text}" - ) +) -> SignedDocumentBase: + template: SignedDocumentBase = brand_parameters_form_template_doc_factory + + result = generic_doc_builder( + rbac_chain_factory(), + DOC_TYPE["brand_parameters"], + [ + { + "id": template.metadata["id"], + "ver": template.metadata["ver"], + "cid": "0x", + } + ], + JSF(template.content).generate(), + ) - return SignedDocumentBase( - result.doc_builder.metadata, result.doc_builder.content - ) + resp = document.put( + data=result.signed_doc, + token=result.auth_token, + ) + assert resp.status_code == 201, ( + f"Failed to publish document: {resp.status_code} - {resp.text}" + ) - return __factory__ + return SignedDocumentBase(result.doc_builder.metadata, result.doc_builder.content) @pytest.fixture -def brand_parameters_form_template_doc_factory(rbac_chain_factory): - def __factory__() -> SignedDocumentBase: - result = generic_doc_builder( - rbac_chain_factory(), - DOC_TYPE["brand_parameters_form_template"], - [], - {"type": "object"}, - ) - - resp = document.put( - data=result.signed_doc, - token=result.auth_token, - ) - assert resp.status_code == 201, ( - f"Failed to publish document: {resp.status_code} - {resp.text}" - ) +def brand_parameters_form_template_doc_factory( + rbac_chain_factory, +) -> SignedDocumentBase: + result = generic_doc_builder( + rbac_chain_factory(), + DOC_TYPE["brand_parameters_form_template"], + [], + {"type": "object"}, + ) - return SignedDocumentBase( - result.doc_builder.metadata, result.doc_builder.content - ) + resp = document.put( + data=result.signed_doc, + token=result.auth_token, + ) + assert resp.status_code == 201, ( + f"Failed to publish document: {resp.status_code} - {resp.text}" + ) - return __factory__ + return SignedDocumentBase(result.doc_builder.metadata, result.doc_builder.content) From 6dd00a5bab9ceea9027cc5c51d0baad99c2a0b9c Mon Sep 17 00:00:00 2001 From: Apisit Ritruengroj Date: Thu, 20 Nov 2025 20:18:37 +0700 Subject: [PATCH 16/40] fix: factory calling --- .../tests/api_tests/utils/signed_doc.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/catalyst-gateway/tests/api_tests/utils/signed_doc.py b/catalyst-gateway/tests/api_tests/utils/signed_doc.py index e978be10c72..b64245280c4 100644 --- a/catalyst-gateway/tests/api_tests/utils/signed_doc.py +++ b/catalyst-gateway/tests/api_tests/utils/signed_doc.py @@ -202,15 +202,15 @@ def __factory__( ) -> tuple[SignedDocument, RoleID]: param: SignedDocumentBase if parameter_type == ProposalParameterType.CATEGORY: - param = category_parameters_doc_factory() + param = category_parameters_doc_factory elif parameter_type == ProposalParameterType.CAMPAIGN: - param = campaign_parameters_doc_factory() + param = campaign_parameters_doc_factory elif parameter_type == ProposalParameterType.BRAND: - param = brand_parameters_doc_factory() + param = brand_parameters_doc_factory else: raise Exception("Invalid parameter type for proposal document") - template: SignedDocumentBase = proposal_form_template_doc_factory() + template: SignedDocumentBase = proposal_form_template_doc_factory result = generic_doc_builder( rbac_chain_factory(), @@ -266,8 +266,8 @@ def category_parameters_doc_factory( category_parameters_form_template_doc_factory, campaign_parameters_doc_factory, ) -> SignedDocumentBase: - template: SignedDocumentBase = category_parameters_form_template_doc_factory() - param: SignedDocumentBase = campaign_parameters_doc_factory() + template: SignedDocumentBase = category_parameters_form_template_doc_factory + param: SignedDocumentBase = campaign_parameters_doc_factory result = generic_doc_builder( rbac_chain_factory(), @@ -298,7 +298,7 @@ def category_parameters_doc_factory( def category_parameters_form_template_doc_factory( rbac_chain_factory, campaign_parameters_doc_factory ) -> SignedDocumentBase: - param: SignedDocumentBase = campaign_parameters_doc_factory() + param: SignedDocumentBase = campaign_parameters_doc_factory result = generic_doc_builder( rbac_chain_factory(), @@ -324,8 +324,8 @@ def campaign_parameters_doc_factory( campaign_parameters_form_template_doc_factory, brand_parameters_doc_factory, ) -> SignedDocumentBase: - template: SignedDocumentBase = campaign_parameters_form_template_doc_factory() - param: SignedDocumentBase = brand_parameters_doc_factory() + template: SignedDocumentBase = campaign_parameters_form_template_doc_factory + param: SignedDocumentBase = brand_parameters_doc_factory result = generic_doc_builder( rbac_chain_factory(), @@ -356,7 +356,7 @@ def campaign_parameters_doc_factory( def campaign_parameters_form_template_doc_factory( rbac_chain_factory, brand_parameters_doc_factory ) -> SignedDocumentBase: - param: SignedDocumentBase = brand_parameters_doc_factory() + param: SignedDocumentBase = brand_parameters_doc_factory result = generic_doc_builder( rbac_chain_factory(), From 0095d95baaefc3c11b81ebae583c1526883e3ac1 Mon Sep 17 00:00:00 2001 From: Apisit Ritruengroj Date: Thu, 20 Nov 2025 21:44:06 +0700 Subject: [PATCH 17/40] chore: update lockfile --- catalyst-gateway/tests/api_tests/poetry.lock | 828 +++++++++++++----- .../tests/api_tests/pyproject.toml | 4 +- 2 files changed, 601 insertions(+), 231 deletions(-) diff --git a/catalyst-gateway/tests/api_tests/poetry.lock b/catalyst-gateway/tests/api_tests/poetry.lock index 4d8ebc86912..34140f59b0e 100644 --- a/catalyst-gateway/tests/api_tests/poetry.lock +++ b/catalyst-gateway/tests/api_tests/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand. [[package]] name = "aioquic" @@ -326,29 +326,6 @@ files = [ tests = ["pytest (>=3.2.1,!=3.3.0)"] typecheck = ["mypy"] -[[package]] -name = "beautifulsoup4" -version = "4.14.2" -description = "Screen-scraping library" -optional = false -python-versions = ">=3.7.0" -groups = ["main"] -files = [ - {file = "beautifulsoup4-4.14.2-py3-none-any.whl", hash = "sha256:5ef6fa3a8cbece8488d66985560f97ed091e22bbc4e9c2338508a9d5de6d4515"}, - {file = "beautifulsoup4-4.14.2.tar.gz", hash = "sha256:2a98ab9f944a11acee9cc848508ec28d9228abfd522ef0fad6a02a72e0ded69e"}, -] - -[package.dependencies] -soupsieve = ">1.2" -typing-extensions = ">=4.0.0" - -[package.extras] -cchardet = ["cchardet"] -chardet = ["chardet"] -charset-normalizer = ["charset-normalizer"] -html5lib = ["html5lib"] -lxml = ["lxml"] - [[package]] name = "blinker" version = "1.9.0" @@ -513,14 +490,14 @@ files = [ [[package]] name = "cachetools" -version = "6.2.1" +version = "6.2.2" description = "Extensible memoizing collections and decorators" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "cachetools-6.2.1-py3-none-any.whl", hash = "sha256:09868944b6dde876dfd44e1d47e18484541eaf12f26f29b7af91b26cc892d701"}, - {file = "cachetools-6.2.1.tar.gz", hash = "sha256:3f391e4bd8f8bf0931169baf7456cc822705f4e2a31f840d218f445b9a854201"}, + {file = "cachetools-6.2.2-py3-none-any.whl", hash = "sha256:6c09c98183bf58560c97b2abfcedcbaf6a896a490f534b031b661d3723b45ace"}, + {file = "cachetools-6.2.2.tar.gz", hash = "sha256:8e6d266b25e539df852251cfd6f990b4bc3a141db73b939058d809ebd2590fc6"}, ] [[package]] @@ -601,14 +578,14 @@ files = [ [[package]] name = "certifi" -version = "2025.10.5" +version = "2025.11.12" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.7" groups = ["main", "dev"] files = [ - {file = "certifi-2025.10.5-py3-none-any.whl", hash = "sha256:0f212c2744a9bb6de0c56639a6f68afe01ecd92d91f14ae897c4fe7bbeeef0de"}, - {file = "certifi-2025.10.5.tar.gz", hash = "sha256:47c09d31ccf2acf0be3f701ea53595ee7e0b8fa08801c6624be771df09ae7b43"}, + {file = "certifi-2025.11.12-py3-none-any.whl", hash = "sha256:97de8790030bbd5c2d96b7ec782fc2f7820ef8dba6db909ccf95449f2d062d4b"}, + {file = "certifi-2025.11.12.tar.gz", hash = "sha256:d8ab5478f2ecd78af242878415affce761ca6bc54a22a27e026d7c25357c3316"}, ] [[package]] @@ -634,7 +611,6 @@ description = "Foreign Function Interface for Python calling C code." optional = false python-versions = ">=3.9" groups = ["main", "dev"] -markers = "platform_python_implementation != \"PyPy\"" files = [ {file = "cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44"}, {file = "cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49"}, @@ -721,6 +697,7 @@ files = [ {file = "cffi-2.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:b882b3df248017dba09d6b16defe9b5c407fe32fc7c65a9c69798e6175601be9"}, {file = "cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529"}, ] +markers = {main = "platform_python_implementation != \"PyPy\""} [package.dependencies] pycparser = {version = "*", markers = "implementation_name != \"PyPy\""} @@ -850,36 +827,19 @@ files = [ [[package]] name = "click" -version = "8.3.0" +version = "8.3.1" description = "Composable command line interface toolkit" optional = false python-versions = ">=3.10" groups = ["dev"] files = [ - {file = "click-8.3.0-py3-none-any.whl", hash = "sha256:9b9f285302c6e3064f4330c05f05b81945b2a39544279343e6e7c5f27a9baddc"}, - {file = "click-8.3.0.tar.gz", hash = "sha256:e7b8232224eba16f4ebe410c25ced9f7875cb5f3263ffc93cc3e8da705e229c4"}, + {file = "click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6"}, + {file = "click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a"}, ] [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} -[[package]] -name = "cloudscraper" -version = "1.2.71" -description = "A Python module to bypass Cloudflare's anti-bot page." -optional = false -python-versions = "*" -groups = ["main"] -files = [ - {file = "cloudscraper-1.2.71-py2.py3-none-any.whl", hash = "sha256:76f50ca529ed2279e220837befdec892626f9511708e200d48d5bb76ded679b0"}, - {file = "cloudscraper-1.2.71.tar.gz", hash = "sha256:429c6e8aa6916d5bad5c8a5eac50f3ea53c9ac22616f6cb21b18dcc71517d0d3"}, -] - -[package.dependencies] -pyparsing = ">=2.4.7" -requests = ">=2.9.2" -requests-toolbelt = ">=0.9.1" - [[package]] name = "colorama" version = "0.4.6" @@ -932,104 +892,104 @@ ecdsa = "*" [[package]] name = "coverage" -version = "7.11.3" +version = "7.12.0" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.10" groups = ["dev"] files = [ - {file = "coverage-7.11.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0c986537abca9b064510f3fd104ba33e98d3036608c7f2f5537f869bc10e1ee5"}, - {file = "coverage-7.11.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:28c5251b3ab1d23e66f1130ca0c419747edfbcb4690de19467cd616861507af7"}, - {file = "coverage-7.11.3-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:4f2bb4ee8dd40f9b2a80bb4adb2aecece9480ba1fa60d9382e8c8e0bd558e2eb"}, - {file = "coverage-7.11.3-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:e5f4bfac975a2138215a38bda599ef00162e4143541cf7dd186da10a7f8e69f1"}, - {file = "coverage-7.11.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8f4cbfff5cf01fa07464439a8510affc9df281535f41a1f5312fbd2b59b4ab5c"}, - {file = "coverage-7.11.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:31663572f20bf3406d7ac00d6981c7bbbcec302539d26b5ac596ca499664de31"}, - {file = "coverage-7.11.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:9799bd6a910961cb666196b8583ed0ee125fa225c6fdee2cbf00232b861f29d2"}, - {file = "coverage-7.11.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:097acc18bedf2c6e3144eaf09b5f6034926c3c9bb9e10574ffd0942717232507"}, - {file = "coverage-7.11.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:6f033dec603eea88204589175782290a038b436105a8f3637a81c4359df27832"}, - {file = "coverage-7.11.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:dd9ca2d44ed8018c90efb72f237a2a140325a4c3339971364d758e78b175f58e"}, - {file = "coverage-7.11.3-cp310-cp310-win32.whl", hash = "sha256:900580bc99c145e2561ea91a2d207e639171870d8a18756eb57db944a017d4bb"}, - {file = "coverage-7.11.3-cp310-cp310-win_amd64.whl", hash = "sha256:c8be5bfcdc7832011b2652db29ed7672ce9d353dd19bce5272ca33dbcf60aaa8"}, - {file = "coverage-7.11.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:200bb89fd2a8a07780eafcdff6463104dec459f3c838d980455cfa84f5e5e6e1"}, - {file = "coverage-7.11.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8d264402fc179776d43e557e1ca4a7d953020d3ee95f7ec19cc2c9d769277f06"}, - {file = "coverage-7.11.3-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:385977d94fc155f8731c895accdfcc3dd0d9dd9ef90d102969df95d3c637ab80"}, - {file = "coverage-7.11.3-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0542ddf6107adbd2592f29da9f59f5d9cff7947b5bb4f734805085c327dcffaa"}, - {file = "coverage-7.11.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d60bf4d7f886989ddf80e121a7f4d140d9eac91f1d2385ce8eb6bda93d563297"}, - {file = "coverage-7.11.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c0a3b6e32457535df0d41d2d895da46434706dd85dbaf53fbc0d3bd7d914b362"}, - {file = "coverage-7.11.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:876a3ee7fd2613eb79602e4cdb39deb6b28c186e76124c3f29e580099ec21a87"}, - {file = "coverage-7.11.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:a730cd0824e8083989f304e97b3f884189efb48e2151e07f57e9e138ab104200"}, - {file = "coverage-7.11.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:b5cd111d3ab7390be0c07ad839235d5ad54d2ca497b5f5db86896098a77180a4"}, - {file = "coverage-7.11.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:074e6a5cd38e06671580b4d872c1a67955d4e69639e4b04e87fc03b494c1f060"}, - {file = "coverage-7.11.3-cp311-cp311-win32.whl", hash = "sha256:86d27d2dd7c7c5a44710565933c7dc9cd70e65ef97142e260d16d555667deef7"}, - {file = "coverage-7.11.3-cp311-cp311-win_amd64.whl", hash = "sha256:ca90ef33a152205fb6f2f0c1f3e55c50df4ef049bb0940ebba666edd4cdebc55"}, - {file = "coverage-7.11.3-cp311-cp311-win_arm64.whl", hash = "sha256:56f909a40d68947ef726ce6a34eb38f0ed241ffbe55c5007c64e616663bcbafc"}, - {file = "coverage-7.11.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5b771b59ac0dfb7f139f70c85b42717ef400a6790abb6475ebac1ecee8de782f"}, - {file = "coverage-7.11.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:603c4414125fc9ae9000f17912dcfd3d3eb677d4e360b85206539240c96ea76e"}, - {file = "coverage-7.11.3-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:77ffb3b7704eb7b9b3298a01fe4509cef70117a52d50bcba29cffc5f53dd326a"}, - {file = "coverage-7.11.3-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:4d4ca49f5ba432b0755ebb0fc3a56be944a19a16bb33802264bbc7311622c0d1"}, - {file = "coverage-7.11.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:05fd3fb6edff0c98874d752013588836f458261e5eba587afe4c547bba544afd"}, - {file = "coverage-7.11.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0e920567f8c3a3ce68ae5a42cf7c2dc4bb6cc389f18bff2235dd8c03fa405de5"}, - {file = "coverage-7.11.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4bec8c7160688bd5a34e65c82984b25409563134d63285d8943d0599efbc448e"}, - {file = "coverage-7.11.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:adb9b7b42c802bd8cb3927de8c1c26368ce50c8fdaa83a9d8551384d77537044"}, - {file = "coverage-7.11.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:c8f563b245b4ddb591e99f28e3cd140b85f114b38b7f95b2e42542f0603eb7d7"}, - {file = "coverage-7.11.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e2a96fdc7643c9517a317553aca13b5cae9bad9a5f32f4654ce247ae4d321405"}, - {file = "coverage-7.11.3-cp312-cp312-win32.whl", hash = "sha256:e8feeb5e8705835f0622af0fe7ff8d5cb388948454647086494d6c41ec142c2e"}, - {file = "coverage-7.11.3-cp312-cp312-win_amd64.whl", hash = "sha256:abb903ffe46bd319d99979cdba350ae7016759bb69f47882242f7b93f3356055"}, - {file = "coverage-7.11.3-cp312-cp312-win_arm64.whl", hash = "sha256:1451464fd855d9bd000c19b71bb7dafea9ab815741fb0bd9e813d9b671462d6f"}, - {file = "coverage-7.11.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:84b892e968164b7a0498ddc5746cdf4e985700b902128421bb5cec1080a6ee36"}, - {file = "coverage-7.11.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f761dbcf45e9416ec4698e1a7649248005f0064ce3523a47402d1bff4af2779e"}, - {file = "coverage-7.11.3-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:1410bac9e98afd9623f53876fae7d8a5db9f5a0ac1c9e7c5188463cb4b3212e2"}, - {file = "coverage-7.11.3-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:004cdcea3457c0ea3233622cd3464c1e32ebba9b41578421097402bee6461b63"}, - {file = "coverage-7.11.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8f067ada2c333609b52835ca4d4868645d3b63ac04fb2b9a658c55bba7f667d3"}, - {file = "coverage-7.11.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:07bc7745c945a6d95676953e86ba7cebb9f11de7773951c387f4c07dc76d03f5"}, - {file = "coverage-7.11.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:8bba7e4743e37484ae17d5c3b8eb1ce78b564cb91b7ace2e2182b25f0f764cb5"}, - {file = "coverage-7.11.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:fbffc22d80d86fbe456af9abb17f7a7766e7b2101f7edaacc3535501691563f7"}, - {file = "coverage-7.11.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:0dba4da36730e384669e05b765a2c49f39514dd3012fcc0398dd66fba8d746d5"}, - {file = "coverage-7.11.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ae12fe90b00b71a71b69f513773310782ce01d5f58d2ceb2b7c595ab9d222094"}, - {file = "coverage-7.11.3-cp313-cp313-win32.whl", hash = "sha256:12d821de7408292530b0d241468b698bce18dd12ecaf45316149f53877885f8c"}, - {file = "coverage-7.11.3-cp313-cp313-win_amd64.whl", hash = "sha256:6bb599052a974bb6cedfa114f9778fedfad66854107cf81397ec87cb9b8fbcf2"}, - {file = "coverage-7.11.3-cp313-cp313-win_arm64.whl", hash = "sha256:bb9d7efdb063903b3fdf77caec7b77c3066885068bdc0d44bc1b0c171033f944"}, - {file = "coverage-7.11.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:fb58da65e3339b3dbe266b607bb936efb983d86b00b03eb04c4ad5b442c58428"}, - {file = "coverage-7.11.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:8d16bbe566e16a71d123cd66382c1315fcd520c7573652a8074a8fe281b38c6a"}, - {file = "coverage-7.11.3-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a8258f10059b5ac837232c589a350a2df4a96406d6d5f2a09ec587cbdd539655"}, - {file = "coverage-7.11.3-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:4c5627429f7fbff4f4131cfdd6abd530734ef7761116811a707b88b7e205afd7"}, - {file = "coverage-7.11.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:465695268414e149bab754c54b0c45c8ceda73dd4a5c3ba255500da13984b16d"}, - {file = "coverage-7.11.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4ebcddfcdfb4c614233cff6e9a3967a09484114a8b2e4f2c7a62dc83676ba13f"}, - {file = "coverage-7.11.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:13b2066303a1c1833c654d2af0455bb009b6e1727b3883c9964bc5c2f643c1d0"}, - {file = "coverage-7.11.3-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:d8750dd20362a1b80e3cf84f58013d4672f89663aee457ea59336df50fab6739"}, - {file = "coverage-7.11.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:ab6212e62ea0e1006531a2234e209607f360d98d18d532c2fa8e403c1afbdd71"}, - {file = "coverage-7.11.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a6b17c2b5e0b9bb7702449200f93e2d04cb04b1414c41424c08aa1e5d352da76"}, - {file = "coverage-7.11.3-cp313-cp313t-win32.whl", hash = "sha256:426559f105f644b69290ea414e154a0d320c3ad8a2bb75e62884731f69cf8e2c"}, - {file = "coverage-7.11.3-cp313-cp313t-win_amd64.whl", hash = "sha256:90a96fcd824564eae6137ec2563bd061d49a32944858d4bdbae5c00fb10e76ac"}, - {file = "coverage-7.11.3-cp313-cp313t-win_arm64.whl", hash = "sha256:1e33d0bebf895c7a0905fcfaff2b07ab900885fc78bba2a12291a2cfbab014cc"}, - {file = "coverage-7.11.3-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fdc5255eb4815babcdf236fa1a806ccb546724c8a9b129fd1ea4a5448a0bf07c"}, - {file = "coverage-7.11.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:fe3425dc6021f906c6325d3c415e048e7cdb955505a94f1eb774dafc779ba203"}, - {file = "coverage-7.11.3-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:4ca5f876bf41b24378ee67c41d688155f0e54cdc720de8ef9ad6544005899240"}, - {file = "coverage-7.11.3-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9061a3e3c92b27fd8036dafa26f25d95695b6aa2e4514ab16a254f297e664f83"}, - {file = "coverage-7.11.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:abcea3b5f0dc44e1d01c27090bc32ce6ffb7aa665f884f1890710454113ea902"}, - {file = "coverage-7.11.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:68c4eb92997dbaaf839ea13527be463178ac0ddd37a7ac636b8bc11a51af2428"}, - {file = "coverage-7.11.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:149eccc85d48c8f06547534068c41d69a1a35322deaa4d69ba1561e2e9127e75"}, - {file = "coverage-7.11.3-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:08c0bcf932e47795c49f0406054824b9d45671362dfc4269e0bc6e4bff010704"}, - {file = "coverage-7.11.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:39764c6167c82d68a2d8c97c33dba45ec0ad9172570860e12191416f4f8e6e1b"}, - {file = "coverage-7.11.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:3224c7baf34e923ffc78cb45e793925539d640d42c96646db62dbd61bbcfa131"}, - {file = "coverage-7.11.3-cp314-cp314-win32.whl", hash = "sha256:c713c1c528284d636cd37723b0b4c35c11190da6f932794e145fc40f8210a14a"}, - {file = "coverage-7.11.3-cp314-cp314-win_amd64.whl", hash = "sha256:c381a252317f63ca0179d2c7918e83b99a4ff3101e1b24849b999a00f9cd4f86"}, - {file = "coverage-7.11.3-cp314-cp314-win_arm64.whl", hash = "sha256:3e33a968672be1394eded257ec10d4acbb9af2ae263ba05a99ff901bb863557e"}, - {file = "coverage-7.11.3-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:f9c96a29c6d65bd36a91f5634fef800212dff69dacdb44345c4c9783943ab0df"}, - {file = "coverage-7.11.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2ec27a7a991d229213c8070d31e3ecf44d005d96a9edc30c78eaeafaa421c001"}, - {file = "coverage-7.11.3-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:72c8b494bd20ae1c58528b97c4a67d5cfeafcb3845c73542875ecd43924296de"}, - {file = "coverage-7.11.3-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:60ca149a446da255d56c2a7a813b51a80d9497a62250532598d249b3cdb1a926"}, - {file = "coverage-7.11.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eb5069074db19a534de3859c43eec78e962d6d119f637c41c8e028c5ab3f59dd"}, - {file = "coverage-7.11.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac5d5329c9c942bbe6295f4251b135d860ed9f86acd912d418dce186de7c19ac"}, - {file = "coverage-7.11.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e22539b676fafba17f0a90ac725f029a309eb6e483f364c86dcadee060429d46"}, - {file = "coverage-7.11.3-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:2376e8a9c889016f25472c452389e98bc6e54a19570b107e27cde9d47f387b64"}, - {file = "coverage-7.11.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:4234914b8c67238a3c4af2bba648dc716aa029ca44d01f3d51536d44ac16854f"}, - {file = "coverage-7.11.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f0b4101e2b3c6c352ff1f70b3a6fcc7c17c1ab1a91ccb7a33013cb0782af9820"}, - {file = "coverage-7.11.3-cp314-cp314t-win32.whl", hash = "sha256:305716afb19133762e8cf62745c46c4853ad6f9eeba54a593e373289e24ea237"}, - {file = "coverage-7.11.3-cp314-cp314t-win_amd64.whl", hash = "sha256:9245bd392572b9f799261c4c9e7216bafc9405537d0f4ce3ad93afe081a12dc9"}, - {file = "coverage-7.11.3-cp314-cp314t-win_arm64.whl", hash = "sha256:9a1d577c20b4334e5e814c3d5fe07fa4a8c3ae42a601945e8d7940bab811d0bd"}, - {file = "coverage-7.11.3-py3-none-any.whl", hash = "sha256:351511ae28e2509c8d8cae5311577ea7dd511ab8e746ffc8814a0896c3d33fbe"}, - {file = "coverage-7.11.3.tar.gz", hash = "sha256:0f59387f5e6edbbffec2281affb71cdc85e0776c1745150a3ab9b6c1d016106b"}, + {file = "coverage-7.12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:32b75c2ba3f324ee37af3ccee5b30458038c50b349ad9b88cee85096132a575b"}, + {file = "coverage-7.12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cb2a1b6ab9fe833714a483a915de350abc624a37149649297624c8d57add089c"}, + {file = "coverage-7.12.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5734b5d913c3755e72f70bf6cc37a0518d4f4745cde760c5d8e12005e62f9832"}, + {file = "coverage-7.12.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b527a08cdf15753279b7afb2339a12073620b761d79b81cbe2cdebdb43d90daa"}, + {file = "coverage-7.12.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9bb44c889fb68004e94cab71f6a021ec83eac9aeabdbb5a5a88821ec46e1da73"}, + {file = "coverage-7.12.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4b59b501455535e2e5dde5881739897967b272ba25988c89145c12d772810ccb"}, + {file = "coverage-7.12.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d8842f17095b9868a05837b7b1b73495293091bed870e099521ada176aa3e00e"}, + {file = "coverage-7.12.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:c5a6f20bf48b8866095c6820641e7ffbe23f2ac84a2efc218d91235e404c7777"}, + {file = "coverage-7.12.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:5f3738279524e988d9da2893f307c2093815c623f8d05a8f79e3eff3a7a9e553"}, + {file = "coverage-7.12.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e0d68c1f7eabbc8abe582d11fa393ea483caf4f44b0af86881174769f185c94d"}, + {file = "coverage-7.12.0-cp310-cp310-win32.whl", hash = "sha256:7670d860e18b1e3ee5930b17a7d55ae6287ec6e55d9799982aa103a2cc1fa2ef"}, + {file = "coverage-7.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:f999813dddeb2a56aab5841e687b68169da0d3f6fc78ccf50952fa2463746022"}, + {file = "coverage-7.12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:aa124a3683d2af98bd9d9c2bfa7a5076ca7e5ab09fdb96b81fa7d89376ae928f"}, + {file = "coverage-7.12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d93fbf446c31c0140208dcd07c5d882029832e8ed7891a39d6d44bd65f2316c3"}, + {file = "coverage-7.12.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:52ca620260bd8cd6027317bdd8b8ba929be1d741764ee765b42c4d79a408601e"}, + {file = "coverage-7.12.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f3433ffd541380f3a0e423cff0f4926d55b0cc8c1d160fdc3be24a4c03aa65f7"}, + {file = "coverage-7.12.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f7bbb321d4adc9f65e402c677cd1c8e4c2d0105d3ce285b51b4d87f1d5db5245"}, + {file = "coverage-7.12.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:22a7aade354a72dff3b59c577bfd18d6945c61f97393bc5fb7bd293a4237024b"}, + {file = "coverage-7.12.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3ff651dcd36d2fea66877cd4a82de478004c59b849945446acb5baf9379a1b64"}, + {file = "coverage-7.12.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:31b8b2e38391a56e3cea39d22a23faaa7c3fc911751756ef6d2621d2a9daf742"}, + {file = "coverage-7.12.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:297bc2da28440f5ae51c845a47c8175a4db0553a53827886e4fb25c66633000c"}, + {file = "coverage-7.12.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6ff7651cc01a246908eac162a6a86fc0dbab6de1ad165dfb9a1e2ec660b44984"}, + {file = "coverage-7.12.0-cp311-cp311-win32.whl", hash = "sha256:313672140638b6ddb2c6455ddeda41c6a0b208298034544cfca138978c6baed6"}, + {file = "coverage-7.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:a1783ed5bd0d5938d4435014626568dc7f93e3cb99bc59188cc18857c47aa3c4"}, + {file = "coverage-7.12.0-cp311-cp311-win_arm64.whl", hash = "sha256:4648158fd8dd9381b5847622df1c90ff314efbfc1df4550092ab6013c238a5fc"}, + {file = "coverage-7.12.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:29644c928772c78512b48e14156b81255000dcfd4817574ff69def189bcb3647"}, + {file = "coverage-7.12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8638cbb002eaa5d7c8d04da667813ce1067080b9a91099801a0053086e52b736"}, + {file = "coverage-7.12.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:083631eeff5eb9992c923e14b810a179798bb598e6a0dd60586819fc23be6e60"}, + {file = "coverage-7.12.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:99d5415c73ca12d558e07776bd957c4222c687b9f1d26fa0e1b57e3598bdcde8"}, + {file = "coverage-7.12.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e949ebf60c717c3df63adb4a1a366c096c8d7fd8472608cd09359e1bd48ef59f"}, + {file = "coverage-7.12.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6d907ddccbca819afa2cd014bc69983b146cca2735a0b1e6259b2a6c10be1e70"}, + {file = "coverage-7.12.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b1518ecbad4e6173f4c6e6c4a46e49555ea5679bf3feda5edb1b935c7c44e8a0"}, + {file = "coverage-7.12.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:51777647a749abdf6f6fd8c7cffab12de68ab93aab15efc72fbbb83036c2a068"}, + {file = "coverage-7.12.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:42435d46d6461a3b305cdfcad7cdd3248787771f53fe18305548cba474e6523b"}, + {file = "coverage-7.12.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5bcead88c8423e1855e64b8057d0544e33e4080b95b240c2a355334bb7ced937"}, + {file = "coverage-7.12.0-cp312-cp312-win32.whl", hash = "sha256:dcbb630ab034e86d2a0f79aefd2be07e583202f41e037602d438c80044957baa"}, + {file = "coverage-7.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:2fd8354ed5d69775ac42986a691fbf68b4084278710cee9d7c3eaa0c28fa982a"}, + {file = "coverage-7.12.0-cp312-cp312-win_arm64.whl", hash = "sha256:737c3814903be30695b2de20d22bcc5428fdae305c61ba44cdc8b3252984c49c"}, + {file = "coverage-7.12.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:47324fffca8d8eae7e185b5bb20c14645f23350f870c1649003618ea91a78941"}, + {file = "coverage-7.12.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ccf3b2ede91decd2fb53ec73c1f949c3e034129d1e0b07798ff1d02ea0c8fa4a"}, + {file = "coverage-7.12.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:b365adc70a6936c6b0582dc38746b33b2454148c02349345412c6e743efb646d"}, + {file = "coverage-7.12.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bc13baf85cd8a4cfcf4a35c7bc9d795837ad809775f782f697bf630b7e200211"}, + {file = "coverage-7.12.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:099d11698385d572ceafb3288a5b80fe1fc58bf665b3f9d362389de488361d3d"}, + {file = "coverage-7.12.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:473dc45d69694069adb7680c405fb1e81f60b2aff42c81e2f2c3feaf544d878c"}, + {file = "coverage-7.12.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:583f9adbefd278e9de33c33d6846aa8f5d164fa49b47144180a0e037f0688bb9"}, + {file = "coverage-7.12.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b2089cc445f2dc0af6f801f0d1355c025b76c24481935303cf1af28f636688f0"}, + {file = "coverage-7.12.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:950411f1eb5d579999c5f66c62a40961f126fc71e5e14419f004471957b51508"}, + {file = "coverage-7.12.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b1aab7302a87bafebfe76b12af681b56ff446dc6f32ed178ff9c092ca776e6bc"}, + {file = "coverage-7.12.0-cp313-cp313-win32.whl", hash = "sha256:d7e0d0303c13b54db495eb636bc2465b2fb8475d4c8bcec8fe4b5ca454dfbae8"}, + {file = "coverage-7.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:ce61969812d6a98a981d147d9ac583a36ac7db7766f2e64a9d4d059c2fe29d07"}, + {file = "coverage-7.12.0-cp313-cp313-win_arm64.whl", hash = "sha256:bcec6f47e4cb8a4c2dc91ce507f6eefc6a1b10f58df32cdc61dff65455031dfc"}, + {file = "coverage-7.12.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:459443346509476170d553035e4a3eed7b860f4fe5242f02de1010501956ce87"}, + {file = "coverage-7.12.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:04a79245ab2b7a61688958f7a855275997134bc84f4a03bc240cf64ff132abf6"}, + {file = "coverage-7.12.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:09a86acaaa8455f13d6a99221d9654df249b33937b4e212b4e5a822065f12aa7"}, + {file = "coverage-7.12.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:907e0df1b71ba77463687a74149c6122c3f6aac56c2510a5d906b2f368208560"}, + {file = "coverage-7.12.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9b57e2d0ddd5f0582bae5437c04ee71c46cd908e7bc5d4d0391f9a41e812dd12"}, + {file = "coverage-7.12.0-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:58c1c6aa677f3a1411fe6fb28ec3a942e4f665df036a3608816e0847fad23296"}, + {file = "coverage-7.12.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4c589361263ab2953e3c4cd2a94db94c4ad4a8e572776ecfbad2389c626e4507"}, + {file = "coverage-7.12.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:91b810a163ccad2e43b1faa11d70d3cf4b6f3d83f9fd5f2df82a32d47b648e0d"}, + {file = "coverage-7.12.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:40c867af715f22592e0d0fb533a33a71ec9e0f73a6945f722a0c85c8c1cbe3a2"}, + {file = "coverage-7.12.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:68b0d0a2d84f333de875666259dadf28cc67858bc8fd8b3f1eae84d3c2bec455"}, + {file = "coverage-7.12.0-cp313-cp313t-win32.whl", hash = "sha256:73f9e7fbd51a221818fd11b7090eaa835a353ddd59c236c57b2199486b116c6d"}, + {file = "coverage-7.12.0-cp313-cp313t-win_amd64.whl", hash = "sha256:24cff9d1f5743f67db7ba46ff284018a6e9aeb649b67aa1e70c396aa1b7cb23c"}, + {file = "coverage-7.12.0-cp313-cp313t-win_arm64.whl", hash = "sha256:c87395744f5c77c866d0f5a43d97cc39e17c7f1cb0115e54a2fe67ca75c5d14d"}, + {file = "coverage-7.12.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:a1c59b7dc169809a88b21a936eccf71c3895a78f5592051b1af8f4d59c2b4f92"}, + {file = "coverage-7.12.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8787b0f982e020adb732b9f051f3e49dd5054cebbc3f3432061278512a2b1360"}, + {file = "coverage-7.12.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5ea5a9f7dc8877455b13dd1effd3202e0bca72f6f3ab09f9036b1bcf728f69ac"}, + {file = "coverage-7.12.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fdba9f15849534594f60b47c9a30bc70409b54947319a7c4fd0e8e3d8d2f355d"}, + {file = "coverage-7.12.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a00594770eb715854fb1c57e0dea08cce6720cfbc531accdb9850d7c7770396c"}, + {file = "coverage-7.12.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5560c7e0d82b42eb1951e4f68f071f8017c824ebfd5a6ebe42c60ac16c6c2434"}, + {file = "coverage-7.12.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d6c2e26b481c9159c2773a37947a9718cfdc58893029cdfb177531793e375cfc"}, + {file = "coverage-7.12.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:6e1a8c066dabcde56d5d9fed6a66bc19a2883a3fe051f0c397a41fc42aedd4cc"}, + {file = "coverage-7.12.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:f7ba9da4726e446d8dd8aae5a6cd872511184a5d861de80a86ef970b5dacce3e"}, + {file = "coverage-7.12.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e0f483ab4f749039894abaf80c2f9e7ed77bbf3c737517fb88c8e8e305896a17"}, + {file = "coverage-7.12.0-cp314-cp314-win32.whl", hash = "sha256:76336c19a9ef4a94b2f8dc79f8ac2da3f193f625bb5d6f51a328cd19bfc19933"}, + {file = "coverage-7.12.0-cp314-cp314-win_amd64.whl", hash = "sha256:7c1059b600aec6ef090721f8f633f60ed70afaffe8ecab85b59df748f24b31fe"}, + {file = "coverage-7.12.0-cp314-cp314-win_arm64.whl", hash = "sha256:172cf3a34bfef42611963e2b661302a8931f44df31629e5b1050567d6b90287d"}, + {file = "coverage-7.12.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:aa7d48520a32cb21c7a9b31f81799e8eaec7239db36c3b670be0fa2403828d1d"}, + {file = "coverage-7.12.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:90d58ac63bc85e0fb919f14d09d6caa63f35a5512a2205284b7816cafd21bb03"}, + {file = "coverage-7.12.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ca8ecfa283764fdda3eae1bdb6afe58bf78c2c3ec2b2edcb05a671f0bba7b3f9"}, + {file = "coverage-7.12.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:874fe69a0785d96bd066059cd4368022cebbec1a8958f224f0016979183916e6"}, + {file = "coverage-7.12.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5b3c889c0b8b283a24d721a9eabc8ccafcfc3aebf167e4cd0d0e23bf8ec4e339"}, + {file = "coverage-7.12.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8bb5b894b3ec09dcd6d3743229dc7f2c42ef7787dc40596ae04c0edda487371e"}, + {file = "coverage-7.12.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:79a44421cd5fba96aa57b5e3b5a4d3274c449d4c622e8f76882d76635501fd13"}, + {file = "coverage-7.12.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:33baadc0efd5c7294f436a632566ccc1f72c867f82833eb59820ee37dc811c6f"}, + {file = "coverage-7.12.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:c406a71f544800ef7e9e0000af706b88465f3573ae8b8de37e5f96c59f689ad1"}, + {file = "coverage-7.12.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e71bba6a40883b00c6d571599b4627f50c360b3d0d02bfc658168936be74027b"}, + {file = "coverage-7.12.0-cp314-cp314t-win32.whl", hash = "sha256:9157a5e233c40ce6613dead4c131a006adfda70e557b6856b97aceed01b0e27a"}, + {file = "coverage-7.12.0-cp314-cp314t-win_amd64.whl", hash = "sha256:e84da3a0fd233aeec797b981c51af1cabac74f9bd67be42458365b30d11b5291"}, + {file = "coverage-7.12.0-cp314-cp314t-win_arm64.whl", hash = "sha256:01d24af36fedda51c2b1aca56e4330a3710f83b02a5ff3743a6b015ffa7c9384"}, + {file = "coverage-7.12.0-py3-none-any.whl", hash = "sha256:159d50c0b12e060b15ed3d39f87ed43d4f7f7ad40b8a534f4dd331adbb51104a"}, + {file = "coverage-7.12.0.tar.gz", hash = "sha256:fc11e0a4e372cb5f282f16ef90d4a585034050ccda536451901abfb19a57f40c"}, ] [package.extras] @@ -1166,6 +1126,21 @@ files = [ {file = "ECPy-1.2.5.tar.gz", hash = "sha256:9635cffb9b6ecf7fd7f72aea1665829ac74a1d272006d0057d45a621aae20228"}, ] +[[package]] +name = "faker" +version = "38.2.0" +description = "Faker is a Python package that generates fake data for you." +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "faker-38.2.0-py3-none-any.whl", hash = "sha256:35fe4a0a79dee0dc4103a6083ee9224941e7d3594811a50e3969e547b0d2ee65"}, + {file = "faker-38.2.0.tar.gz", hash = "sha256:20672803db9c7cb97f9b56c18c54b915b6f1d8991f63d1d673642dc43f5ce7ab"}, +] + +[package.dependencies] +tzdata = "*" + [[package]] name = "flask" version = "3.1.2" @@ -1192,51 +1167,103 @@ dotenv = ["python-dotenv"] [[package]] name = "frozendict" -version = "2.4.6" +version = "2.4.7" description = "A simple immutable dictionary" optional = false python-versions = ">=3.6" groups = ["main"] files = [ - {file = "frozendict-2.4.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c3a05c0a50cab96b4bb0ea25aa752efbfceed5ccb24c007612bc63e51299336f"}, - {file = "frozendict-2.4.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f5b94d5b07c00986f9e37a38dd83c13f5fe3bf3f1ccc8e88edea8fe15d6cd88c"}, - {file = "frozendict-2.4.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4c789fd70879ccb6289a603cdebdc4953e7e5dea047d30c1b180529b28257b5"}, - {file = "frozendict-2.4.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da6a10164c8a50b34b9ab508a9420df38f4edf286b9ca7b7df8a91767baecb34"}, - {file = "frozendict-2.4.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:9a8a43036754a941601635ea9c788ebd7a7efbed2becba01b54a887b41b175b9"}, - {file = "frozendict-2.4.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c9905dcf7aa659e6a11b8051114c9fa76dfde3a6e50e6dc129d5aece75b449a2"}, - {file = "frozendict-2.4.6-cp310-cp310-win_amd64.whl", hash = "sha256:323f1b674a2cc18f86ab81698e22aba8145d7a755e0ac2cccf142ee2db58620d"}, - {file = "frozendict-2.4.6-cp310-cp310-win_arm64.whl", hash = "sha256:eabd21d8e5db0c58b60d26b4bb9839cac13132e88277e1376970172a85ee04b3"}, - {file = "frozendict-2.4.6-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:eddabeb769fab1e122d3a6872982c78179b5bcc909fdc769f3cf1964f55a6d20"}, - {file = "frozendict-2.4.6-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:377a65be0a700188fc21e669c07de60f4f6d35fae8071c292b7df04776a1c27b"}, - {file = "frozendict-2.4.6-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce1e9217b85eec6ba9560d520d5089c82dbb15f977906eb345d81459723dd7e3"}, - {file = "frozendict-2.4.6-cp36-cp36m-musllinux_1_2_aarch64.whl", hash = "sha256:7291abacf51798d5ffe632771a69c14fb423ab98d63c4ccd1aa382619afe2f89"}, - {file = "frozendict-2.4.6-cp36-cp36m-musllinux_1_2_x86_64.whl", hash = "sha256:e72fb86e48811957d66ffb3e95580af7b1af1e6fbd760ad63d7bd79b2c9a07f8"}, - {file = "frozendict-2.4.6-cp36-cp36m-win_amd64.whl", hash = "sha256:622301b1c29c4f9bba633667d592a3a2b093cb408ba3ce578b8901ace3931ef3"}, - {file = "frozendict-2.4.6-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a4e3737cb99ed03200cd303bdcd5514c9f34b29ee48f405c1184141bd68611c9"}, - {file = "frozendict-2.4.6-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:49ffaf09241bc1417daa19362a2241a4aa435f758fd4375c39ce9790443a39cd"}, - {file = "frozendict-2.4.6-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d69418479bfb834ba75b0e764f058af46ceee3d655deb6a0dd0c0c1a5e82f09"}, - {file = "frozendict-2.4.6-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:c131f10c4d3906866454c4e89b87a7e0027d533cce8f4652aa5255112c4d6677"}, - {file = "frozendict-2.4.6-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:fc67cbb3c96af7a798fab53d52589752c1673027e516b702ab355510ddf6bdff"}, - {file = "frozendict-2.4.6-cp37-cp37m-win_amd64.whl", hash = "sha256:7730f8ebe791d147a1586cbf6a42629351d4597773317002181b66a2da0d509e"}, - {file = "frozendict-2.4.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:807862e14b0e9665042458fde692c4431d660c4219b9bb240817f5b918182222"}, - {file = "frozendict-2.4.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9647c74efe3d845faa666d4853cfeabbaee403b53270cabfc635b321f770e6b8"}, - {file = "frozendict-2.4.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:665fad3f0f815aa41294e561d98dbedba4b483b3968e7e8cab7d728d64b96e33"}, - {file = "frozendict-2.4.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f42e6b75254ea2afe428ad6d095b62f95a7ae6d4f8272f0bd44a25dddd20f67"}, - {file = "frozendict-2.4.6-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:02331541611f3897f260900a1815b63389654951126e6e65545e529b63c08361"}, - {file = "frozendict-2.4.6-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:18d50a2598350b89189da9150058191f55057581e40533e470db46c942373acf"}, - {file = "frozendict-2.4.6-cp38-cp38-win_amd64.whl", hash = "sha256:1b4a3f8f6dd51bee74a50995c39b5a606b612847862203dd5483b9cd91b0d36a"}, - {file = "frozendict-2.4.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a76cee5c4be2a5d1ff063188232fffcce05dde6fd5edd6afe7b75b247526490e"}, - {file = "frozendict-2.4.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ba5ef7328706db857a2bdb2c2a17b4cd37c32a19c017cff1bb7eeebc86b0f411"}, - {file = "frozendict-2.4.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:669237c571856be575eca28a69e92a3d18f8490511eff184937283dc6093bd67"}, - {file = "frozendict-2.4.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0aaa11e7c472150efe65adbcd6c17ac0f586896096ab3963775e1c5c58ac0098"}, - {file = "frozendict-2.4.6-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:b8f2829048f29fe115da4a60409be2130e69402e29029339663fac39c90e6e2b"}, - {file = "frozendict-2.4.6-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:94321e646cc39bebc66954a31edd1847d3a2a3483cf52ff051cd0996e7db07db"}, - {file = "frozendict-2.4.6-cp39-cp39-win_amd64.whl", hash = "sha256:74b6b26c15dddfefddeb89813e455b00ebf78d0a3662b89506b4d55c6445a9f4"}, - {file = "frozendict-2.4.6-cp39-cp39-win_arm64.whl", hash = "sha256:7088102345d1606450bd1801a61139bbaa2cb0d805b9b692f8d81918ea835da6"}, - {file = "frozendict-2.4.6-py311-none-any.whl", hash = "sha256:d065db6a44db2e2375c23eac816f1a022feb2fa98cbb50df44a9e83700accbea"}, - {file = "frozendict-2.4.6-py312-none-any.whl", hash = "sha256:49344abe90fb75f0f9fdefe6d4ef6d4894e640fadab71f11009d52ad97f370b9"}, - {file = "frozendict-2.4.6-py313-none-any.whl", hash = "sha256:7134a2bb95d4a16556bb5f2b9736dceb6ea848fa5b6f3f6c2d6dba93b44b4757"}, - {file = "frozendict-2.4.6.tar.gz", hash = "sha256:df7cd16470fbd26fc4969a208efadc46319334eb97def1ddf48919b351192b8e"}, + {file = "frozendict-2.4.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:bd37c087a538944652363cfd77fb7abe8100cc1f48afea0b88b38bf0f469c3d2"}, + {file = "frozendict-2.4.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2b96f224a5431889f04b2bc99c0e9abe285679464273ead83d7d7f2a15907d35"}, + {file = "frozendict-2.4.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5c1781f28c4bbb177644b3cb6d5cf7da59be374b02d91cdde68d1d5ef32e046b"}, + {file = "frozendict-2.4.7-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:8a06f6c3d3b8d487226fdde93f621e04a54faecc5bf5d9b16497b8f9ead0ac3e"}, + {file = "frozendict-2.4.7-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b809d1c861436a75b2b015dbfd94f6154fa4e7cb0a70e389df1d5f6246b21d1e"}, + {file = "frozendict-2.4.7-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:75eefdf257a84ea73d553eb80d0abbff0af4c9df62529e4600fd3f96ff17eeb3"}, + {file = "frozendict-2.4.7-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a4d2b27d8156922c9739dd2ff4f3934716e17cfd1cf6fb61aa17af7d378555e9"}, + {file = "frozendict-2.4.7-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2ebd953c41408acfb8041ff9e6c3519c09988fb7e007df7ab6b56e229029d788"}, + {file = "frozendict-2.4.7-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c64d34b802912ee6d107936e970b90750385a1fdfd38d310098b2918ba4cbf2"}, + {file = "frozendict-2.4.7-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:294a7d7d51dd979021a8691b46aedf9bd4a594ce3ed33a4bdf0a712d6929d712"}, + {file = "frozendict-2.4.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f65d1b90e9ddc791ea82ef91a9ae0ab27ef6c0cfa88fadfa0e5ca5a22f8fa22f"}, + {file = "frozendict-2.4.7-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:82d5272d08451bcef6fb6235a0a04cf1816b6b6815cec76be5ace1de17e0c1a4"}, + {file = "frozendict-2.4.7-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:5943c3f683d3f32036f6ca975e920e383d85add1857eee547742de9c1f283716"}, + {file = "frozendict-2.4.7-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:88c6bea948da03087035bb9ca9625305d70e084aa33f11e17048cb7dda4ca293"}, + {file = "frozendict-2.4.7-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:ffd1a9f9babec9119712e76a39397d8aa0d72ef8c4ccad917c6175d7e7f81b74"}, + {file = "frozendict-2.4.7-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:0ff6f57854cc8aa8b30947ec005f9246d96e795a78b21441614e85d39b708822"}, + {file = "frozendict-2.4.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d774df483c12d6cba896eb9a1337bbc5ad3f564eb18cfaaee3e95fb4402f2a86"}, + {file = "frozendict-2.4.7-cp310-cp310-win32.whl", hash = "sha256:a10d38fa300f6bef230fae1fdb4bc98706b78c8a3a2f3140fde748469ef3cfe8"}, + {file = "frozendict-2.4.7-cp310-cp310-win_amd64.whl", hash = "sha256:dd518f300e5eb6a8827bee380f2e1a31c01dc0af069b13abdecd4e5769bd8a97"}, + {file = "frozendict-2.4.7-cp310-cp310-win_arm64.whl", hash = "sha256:3842cfc2d69df5b9978f2e881b7678a282dbdd6846b11b5159f910bc633cbe4f"}, + {file = "frozendict-2.4.7-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:735be62d757e1e7e496ccb6401efe82b473faa653e95eec0826cd7819a29a34c"}, + {file = "frozendict-2.4.7-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fff8584e3bbdc5c1713cd016fbf4b88babfffd4e5e89b39020f2a208dd24c900"}, + {file = "frozendict-2.4.7-cp36-cp36m-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:91a06ee46b3e3ef3b237046b914c0c905eab9fdfeac677e9b51473b482e24c28"}, + {file = "frozendict-2.4.7-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fd7ba56cf6340c732ecb78787c4e9600c4bd01372af7313ded21037126d33ec6"}, + {file = "frozendict-2.4.7-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d1b4426457757c30ad86b57cdbcc0adaa328399f1ec3d231a0a2ce7447248987"}, + {file = "frozendict-2.4.7-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b22d337c76b765cb7961d4ee47fe29f89e30921eb47bf856b14dc7641f4df3e5"}, + {file = "frozendict-2.4.7-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57134ef5df1dd32229c148c75a7b89245dbdb89966a155d6dfd4bda653e8c7af"}, + {file = "frozendict-2.4.7-cp36-cp36m-musllinux_1_2_aarch64.whl", hash = "sha256:c89617a784e1c24a31f5aa4809402f8072a26b64ddbc437897f6391ff69b0ee9"}, + {file = "frozendict-2.4.7-cp36-cp36m-musllinux_1_2_armv7l.whl", hash = "sha256:176dd384dfe1d0d79449e05f67764c57c6f0f3095378bf00deb33165d5d2df5b"}, + {file = "frozendict-2.4.7-cp36-cp36m-musllinux_1_2_i686.whl", hash = "sha256:b1a94e8935c69ae30043b465af496f447950f2c03660aee8657074084faae0b3"}, + {file = "frozendict-2.4.7-cp36-cp36m-musllinux_1_2_ppc64le.whl", hash = "sha256:c570649ceccfa5e11ad9351e9009dc484c315a51a56aa02ced07ae97644bb7aa"}, + {file = "frozendict-2.4.7-cp36-cp36m-musllinux_1_2_s390x.whl", hash = "sha256:e0d450c9d444befe2668bf9386ac2945a2f38152248d58f6b3feea63db59ba08"}, + {file = "frozendict-2.4.7-cp36-cp36m-musllinux_1_2_x86_64.whl", hash = "sha256:7469912c1a04102457871ff675aebe600dbb7e79a6450a166cc8079b88f6ca79"}, + {file = "frozendict-2.4.7-cp36-cp36m-win32.whl", hash = "sha256:2808bab8e21887a8c106cca5f6f0ab5bda7ee81e159409a10f53d57542ccd99c"}, + {file = "frozendict-2.4.7-cp36-cp36m-win_amd64.whl", hash = "sha256:ca17ac727ffeeba6c46f5a88e0284a7cb1520fb03127645fcdd7041080adf849"}, + {file = "frozendict-2.4.7-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8ef11dd996208c5a96eab0683f7a17cb4b992948464d2498520efd75a10a2aac"}, + {file = "frozendict-2.4.7-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b960e700dc95faca7dd6919d0dce183ef89bfe01554d323cf5de7331a2e80f83"}, + {file = "frozendict-2.4.7-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:fc43257a06e6117da6a8a0779243b974cdb9205fed82e32eb669f6746c75d27d"}, + {file = "frozendict-2.4.7-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0ece525da7d0aa3eb56c3e479f30612028d545081c15450d67d771a303ee7d4c"}, + {file = "frozendict-2.4.7-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7ddffe7c0b3be414f88185e212758989c65b497315781290eb029e2c1e1fd64e"}, + {file = "frozendict-2.4.7-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05dd27415f913cd11649009f53d97eb565ce7b76787d7869c4733738c10e8d27"}, + {file = "frozendict-2.4.7-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0664092614d2b9d0aa404731f33ad5459a54fe8dab9d1fd45aa714fa6de4d0ef"}, + {file = "frozendict-2.4.7-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:830d181781bb263c9fa430b81f82c867546f5dcb368e73931c8591f533a04afb"}, + {file = "frozendict-2.4.7-cp37-cp37m-musllinux_1_2_armv7l.whl", hash = "sha256:c93827e0854393cd904b927ceb529afc17776706f5b9e45c7eaf6a40b3fc7b25"}, + {file = "frozendict-2.4.7-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:6d30dbba6eb1497c695f3108c2c292807e7a237c67a1b9ff92c04e89969d22d1"}, + {file = "frozendict-2.4.7-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:ec846bde66b75d68518c7b24a0a46d09db0aee5a6aefd2209d9901faf6e9df21"}, + {file = "frozendict-2.4.7-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:1df8e22f7d24172c08434b10911f3971434bb5a59b4d1b0078ae33a623625294"}, + {file = "frozendict-2.4.7-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:39abe54264ae69a0b2e00fabdb5118604f36a5b927d33e7532cd594c5142ebf4"}, + {file = "frozendict-2.4.7-cp37-cp37m-win32.whl", hash = "sha256:d10c2ea7c90ba204cd053167ba214d0cdd00f3184c7b8d117a56d7fd2b0c6553"}, + {file = "frozendict-2.4.7-cp37-cp37m-win_amd64.whl", hash = "sha256:346a53640f15c1640a3503f60ba99df39e4ab174979f10db4304bbb378df5cbd"}, + {file = "frozendict-2.4.7-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:cc520f3f4af14f456143a534d554175dbc0f0636ffd653e63675cd591862a9d9"}, + {file = "frozendict-2.4.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7fd0d0bd3a79e009dddbf5fedfd927ad495c218cd7b13a112d28a37e2079725c"}, + {file = "frozendict-2.4.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a404857e48d85a517bb5b974d740f8c4fccb25d8df98885f3a2a4d950870b845"}, + {file = "frozendict-2.4.7-cp38-cp38-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f42e2c25d3eee4ea3da88466f38ed0dce8c622a1a9d92572e5ee53b7a6bb9ef1"}, + {file = "frozendict-2.4.7-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a1a083e9ee7a1904e545a6307c7db1dd76200077520fcbf7a98d886f81b57dd7"}, + {file = "frozendict-2.4.7-cp38-cp38-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:f556ea05d9c5f6dae50d57ce6234e4ab1fbf4551dd0d52b4fed6ef537d9f3d3c"}, + {file = "frozendict-2.4.7-cp38-cp38-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:739ee81e574f33b46f1e6d9312f3ec2c549bdd574a4ebb6bf106775c9d85ca7b"}, + {file = "frozendict-2.4.7-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:48ab42b01952bc11543577de9fe5d9ca7c41b35dda36326a07fb47d84b3d5f22"}, + {file = "frozendict-2.4.7-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:34233deb8d09e798e874a6ac00b054d2e842164d982ebd43eb91b9f0a6a34876"}, + {file = "frozendict-2.4.7-cp38-cp38-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:76bd99f3508cb2ec87976f2e3fe7d92fb373a661cacffb863013d15e4cfaf0eb"}, + {file = "frozendict-2.4.7-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:a265e95e7087f44b88a6d78a63ea95a2ca0eb0a21ab4f76047f4c164a8beb413"}, + {file = "frozendict-2.4.7-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:1662f1b72b4f4a2ffdfdc4981ece275ca11f90244208ac1f1fc2c17fc9c9437a"}, + {file = "frozendict-2.4.7-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:2e5d2c30f4a3fea83a14b0a5722f21c10de5c755ab5637c70de5eb60886d58cd"}, + {file = "frozendict-2.4.7-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:2cf0a665bf2f1ce69d3cd8b6d3574b1d32ae00981a16fa1d255d2da8a2e44b7c"}, + {file = "frozendict-2.4.7-cp38-cp38-musllinux_1_2_riscv64.whl", hash = "sha256:708382875c3cfe91be625dddcba03dee2dfdadbad2c431568a8c7f2f2af0bbee"}, + {file = "frozendict-2.4.7-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:7fe194f37052a8f45a1a8507e36229e28b79f3d21542ae55ea6a18c6a444f625"}, + {file = "frozendict-2.4.7-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:d8930877a2dd40461968d9238d95c754e51b33ce7d2a45500f88ffeed5cb7202"}, + {file = "frozendict-2.4.7-cp38-cp38-win32.whl", hash = "sha256:6991469a889ee8a108fe5ed1b044447c7b7a07da9067e93c59cbfac8c1d625cf"}, + {file = "frozendict-2.4.7-cp38-cp38-win_amd64.whl", hash = "sha256:ebae8f4a07372acfc3963fc8d68070cdaab70272c3dd836f057ebbe9b7d38643"}, + {file = "frozendict-2.4.7-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:1c521ad3d747aa475e9040e231f5f1847c04423bae5571c010a9d969e6983c40"}, + {file = "frozendict-2.4.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:70e655c3aa5f893807830f549a7275031a181dbebeaf74c461b51adc755d9335"}, + {file = "frozendict-2.4.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:11d35075f979c96f528d74ccbf89322a7ef8211977dd566bc384985ebce689be"}, + {file = "frozendict-2.4.7-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d4d7ec24d3bfcfac3baf4dffd7fcea3fa8474b087ce32696232132064aa062cf"}, + {file = "frozendict-2.4.7-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5694417864875ca959932e3b98e2b7d5d27c75177bf510939d0da583712ddf58"}, + {file = "frozendict-2.4.7-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:57a754671c5746e11140363aa2f4e7a75c8607de6e85a2bf89dcd1daf51885a7"}, + {file = "frozendict-2.4.7-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:313e0e1d8b22b317aa1f7dd48aec8cbb0416ddd625addf7648a69148fcb9ccff"}, + {file = "frozendict-2.4.7-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:176a66094428b9fd66270927b9787e3b8b1c9505ef92723c7b0ef1923dbe3c4a"}, + {file = "frozendict-2.4.7-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de1fff2683d8af01299ec01eb21a24b6097ce92015fc1fbefa977cecf076a3fc"}, + {file = "frozendict-2.4.7-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:115a822ecd754574e11205e0880e9d61258d960863d6fd1b90883aa800f6d3b3"}, + {file = "frozendict-2.4.7-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:de8d2c98777ba266f5466e211778d4e3bd00635a207c54f6f7511d8613b86dd3"}, + {file = "frozendict-2.4.7-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:1e307be0e1f26cbc9593f6bdad5238a1408a50f39f63c9c39eb93c7de5926767"}, + {file = "frozendict-2.4.7-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:78a55f320ca924545494ce153df02d4349156cd95dc4603c1f0e80c42c889249"}, + {file = "frozendict-2.4.7-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:e89492dfcc4c27a718f8b5a4c8df1a2dec6c689718cccd70cb2ceba69ab8c642"}, + {file = "frozendict-2.4.7-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:1e801d62e35df24be2c6f7f43c114058712efa79a8549c289437754dad0207a3"}, + {file = "frozendict-2.4.7-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:3ed9e2f3547a59f4ef5c233614c6faa6221d33004cb615ae1c07ffc551cfe178"}, + {file = "frozendict-2.4.7-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:ad0448ed5569f0a9b9b010af9fb5b6d9bdc0b4b877a3ddb188396c4742e62284"}, + {file = "frozendict-2.4.7-cp39-cp39-win32.whl", hash = "sha256:eab9ef8a9268042e819de03079b984eb0894f05a7b63c4e5319b1cf1ef362ba7"}, + {file = "frozendict-2.4.7-cp39-cp39-win_amd64.whl", hash = "sha256:8dfe2f4840b043436ee5bdd07b0fa5daecedf086e6957e7df050a56ab6db078d"}, + {file = "frozendict-2.4.7-cp39-cp39-win_arm64.whl", hash = "sha256:cc2085926872a1b26deda4b81b2254d2e5d2cb2c4d7b327abe4c820b7c93f40b"}, + {file = "frozendict-2.4.7-py3-none-any.whl", hash = "sha256:972af65924ea25cf5b4d9326d549e69a9a4918d8a76a9d3a7cd174d98b237550"}, + {file = "frozendict-2.4.7.tar.gz", hash = "sha256:e478fb2a1391a56c8a6e10cc97c4a9002b410ecd1ac28c18d780661762e271bd"}, ] [[package]] @@ -1550,6 +1577,66 @@ MarkupSafe = ">=2.0" [package.extras] i18n = ["Babel (>=2.7)"] +[[package]] +name = "jsf" +version = "0.11.2" +description = "Creates fake JSON files from a JSON schema" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "jsf-0.11.2-py3-none-any.whl", hash = "sha256:b4472c8c2d776eb3e0bb08368caa6ae0ead7ea78b20653facc07b6d93768612c"}, + {file = "jsf-0.11.2.tar.gz", hash = "sha256:07055b363281d38ce871a9256a00587d8472802c5108721a7fe5884465104b5d"}, +] + +[package.dependencies] +faker = ">=15.3.4" +jsonschema = ">=4.17.3" +pydantic = ">=2.0.0" +rstr = ">=3.2.0" +smart-open = {version = ">=6.3.0", extras = ["http"]} +typing-extensions = ">=4.9.0" + +[package.extras] +cli = ["typer (>=0.7.0)"] + +[[package]] +name = "jsonschema" +version = "4.25.1" +description = "An implementation of JSON Schema validation for Python" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "jsonschema-4.25.1-py3-none-any.whl", hash = "sha256:3fba0169e345c7175110351d456342c364814cfcf3b964ba4587f22915230a63"}, + {file = "jsonschema-4.25.1.tar.gz", hash = "sha256:e4a9655ce0da0c0b67a085847e00a3a51449e1157f4f75e9fb5aa545e122eb85"}, +] + +[package.dependencies] +attrs = ">=22.2.0" +jsonschema-specifications = ">=2023.03.6" +referencing = ">=0.28.4" +rpds-py = ">=0.7.1" + +[package.extras] +format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] +format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "rfc3987-syntax (>=1.1.0)", "uri-template", "webcolors (>=24.6.0)"] + +[[package]] +name = "jsonschema-specifications" +version = "2025.9.1" +description = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe"}, + {file = "jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d"}, +] + +[package.dependencies] +referencing = ">=0.31.0" + [[package]] name = "kaitaistruct" version = "0.11" @@ -2169,11 +2256,11 @@ description = "C parser in Python" optional = false python-versions = ">=3.8" groups = ["main", "dev"] -markers = "platform_python_implementation != \"PyPy\" and implementation_name != \"PyPy\"" files = [ {file = "pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934"}, {file = "pycparser-2.23.tar.gz", hash = "sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2"}, ] +markers = {main = "platform_python_implementation != \"PyPy\" and implementation_name != \"PyPy\"", dev = "implementation_name != \"PyPy\""} [[package]] name = "pydantic" @@ -2454,7 +2541,7 @@ version = "3.2.5" description = "pyparsing - Classes and methods to define and execute parsing grammars" optional = false python-versions = ">=3.9" -groups = ["main", "dev"] +groups = ["dev"] files = [ {file = "pyparsing-3.2.5-py3-none-any.whl", hash = "sha256:e38a4f02064cf41fe6593d328d0512495ad1f3d8a91c4f73fc401b3079a59a5e"}, {file = "pyparsing-3.2.5.tar.gz", hash = "sha256:2df8d5b7b2802ef88e8d016a2eb9c7aeaa923529cd251ed0fe4608275d4105b6"}, @@ -2615,6 +2702,22 @@ files = [ {file = "pywin32-311-cp39-cp39-win_arm64.whl", hash = "sha256:62ea666235135fee79bb154e695f3ff67370afefd71bd7fea7512fc70ef31e3d"}, ] +[[package]] +name = "referencing" +version = "0.37.0" +description = "JSON Referencing + Python" +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231"}, + {file = "referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8"}, +] + +[package.dependencies] +attrs = ">=22.2.0" +rpds-py = ">=0.7.0" + [[package]] name = "requests" version = "2.32.5" @@ -2638,20 +2741,142 @@ socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] -name = "requests-toolbelt" -version = "1.0.0" -description = "A utility belt for advanced users of python-requests" +name = "rpds-py" +version = "0.29.0" +description = "Python bindings to Rust's persistent data structures (rpds)" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "rpds_py-0.29.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:4ae4b88c6617e1b9e5038ab3fccd7bac0842fdda2b703117b2aa99bc85379113"}, + {file = "rpds_py-0.29.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7d9128ec9d8cecda6f044001fde4fb71ea7c24325336612ef8179091eb9596b9"}, + {file = "rpds_py-0.29.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d37812c3da8e06f2bb35b3cf10e4a7b68e776a706c13058997238762b4e07f4f"}, + {file = "rpds_py-0.29.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:66786c3fb1d8de416a7fa8e1cb1ec6ba0a745b2b0eee42f9b7daa26f1a495545"}, + {file = "rpds_py-0.29.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b58f5c77f1af888b5fd1876c9a0d9858f6f88a39c9dd7c073a88e57e577da66d"}, + {file = "rpds_py-0.29.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:799156ef1f3529ed82c36eb012b5d7a4cf4b6ef556dd7cc192148991d07206ae"}, + {file = "rpds_py-0.29.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:453783477aa4f2d9104c4b59b08c871431647cb7af51b549bbf2d9eb9c827756"}, + {file = "rpds_py-0.29.0-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:24a7231493e3c4a4b30138b50cca089a598e52c34cf60b2f35cebf62f274fdea"}, + {file = "rpds_py-0.29.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7033c1010b1f57bb44d8067e8c25aa6fa2e944dbf46ccc8c92b25043839c3fd2"}, + {file = "rpds_py-0.29.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0248b19405422573621172ab8e3a1f29141362d13d9f72bafa2e28ea0cdca5a2"}, + {file = "rpds_py-0.29.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:f9f436aee28d13b9ad2c764fc273e0457e37c2e61529a07b928346b219fcde3b"}, + {file = "rpds_py-0.29.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:24a16cb7163933906c62c272de20ea3c228e4542c8c45c1d7dc2b9913e17369a"}, + {file = "rpds_py-0.29.0-cp310-cp310-win32.whl", hash = "sha256:1a409b0310a566bfd1be82119891fefbdce615ccc8aa558aff7835c27988cbef"}, + {file = "rpds_py-0.29.0-cp310-cp310-win_amd64.whl", hash = "sha256:c5523b0009e7c3c1263471b69d8da1c7d41b3ecb4cb62ef72be206b92040a950"}, + {file = "rpds_py-0.29.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:9b9c764a11fd637e0322a488560533112837f5334ffeb48b1be20f6d98a7b437"}, + {file = "rpds_py-0.29.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3fd2164d73812026ce970d44c3ebd51e019d2a26a4425a5dcbdfa93a34abc383"}, + {file = "rpds_py-0.29.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a097b7f7f7274164566ae90a221fd725363c0e9d243e2e9ed43d195ccc5495c"}, + {file = "rpds_py-0.29.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7cdc0490374e31cedefefaa1520d5fe38e82fde8748cbc926e7284574c714d6b"}, + {file = "rpds_py-0.29.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:89ca2e673ddd5bde9b386da9a0aac0cab0e76f40c8f0aaf0d6311b6bbf2aa311"}, + {file = "rpds_py-0.29.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a5d9da3ff5af1ca1249b1adb8ef0573b94c76e6ae880ba1852f033bf429d4588"}, + {file = "rpds_py-0.29.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8238d1d310283e87376c12f658b61e1ee23a14c0e54c7c0ce953efdbdc72deed"}, + {file = "rpds_py-0.29.0-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:2d6fb2ad1c36f91c4646989811e84b1ea5e0c3cf9690b826b6e32b7965853a63"}, + {file = "rpds_py-0.29.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:534dc9df211387547267ccdb42253aa30527482acb38dd9b21c5c115d66a96d2"}, + {file = "rpds_py-0.29.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d456e64724a075441e4ed648d7f154dc62e9aabff29bcdf723d0c00e9e1d352f"}, + {file = "rpds_py-0.29.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:a738f2da2f565989401bd6fd0b15990a4d1523c6d7fe83f300b7e7d17212feca"}, + {file = "rpds_py-0.29.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a110e14508fd26fd2e472bb541f37c209409876ba601cf57e739e87d8a53cf95"}, + {file = "rpds_py-0.29.0-cp311-cp311-win32.whl", hash = "sha256:923248a56dd8d158389a28934f6f69ebf89f218ef96a6b216a9be6861804d3f4"}, + {file = "rpds_py-0.29.0-cp311-cp311-win_amd64.whl", hash = "sha256:539eb77eb043afcc45314d1be09ea6d6cafb3addc73e0547c171c6d636957f60"}, + {file = "rpds_py-0.29.0-cp311-cp311-win_arm64.whl", hash = "sha256:bdb67151ea81fcf02d8f494703fb728d4d34d24556cbff5f417d74f6f5792e7c"}, + {file = "rpds_py-0.29.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a0891cfd8db43e085c0ab93ab7e9b0c8fee84780d436d3b266b113e51e79f954"}, + {file = "rpds_py-0.29.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3897924d3f9a0361472d884051f9a2460358f9a45b1d85a39a158d2f8f1ad71c"}, + {file = "rpds_py-0.29.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a21deb8e0d1571508c6491ce5ea5e25669b1dd4adf1c9d64b6314842f708b5d"}, + {file = "rpds_py-0.29.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9efe71687d6427737a0a2de9ca1c0a216510e6cd08925c44162be23ed7bed2d5"}, + {file = "rpds_py-0.29.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:40f65470919dc189c833e86b2c4bd21bd355f98436a2cef9e0a9a92aebc8e57e"}, + {file = "rpds_py-0.29.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:def48ff59f181130f1a2cb7c517d16328efac3ec03951cca40c1dc2049747e83"}, + {file = "rpds_py-0.29.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad7bd570be92695d89285a4b373006930715b78d96449f686af422debb4d3949"}, + {file = "rpds_py-0.29.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:5a572911cd053137bbff8e3a52d31c5d2dba51d3a67ad902629c70185f3f2181"}, + {file = "rpds_py-0.29.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d583d4403bcbf10cffc3ab5cee23d7643fcc960dff85973fd3c2d6c86e8dbb0c"}, + {file = "rpds_py-0.29.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:070befbb868f257d24c3bb350dbd6e2f645e83731f31264b19d7231dd5c396c7"}, + {file = "rpds_py-0.29.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:fc935f6b20b0c9f919a8ff024739174522abd331978f750a74bb68abd117bd19"}, + {file = "rpds_py-0.29.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8c5a8ecaa44ce2d8d9d20a68a2483a74c07f05d72e94a4dff88906c8807e77b0"}, + {file = "rpds_py-0.29.0-cp312-cp312-win32.whl", hash = "sha256:ba5e1aeaf8dd6d8f6caba1f5539cddda87d511331714b7b5fc908b6cfc3636b7"}, + {file = "rpds_py-0.29.0-cp312-cp312-win_amd64.whl", hash = "sha256:b5f6134faf54b3cb83375db0f113506f8b7770785be1f95a631e7e2892101977"}, + {file = "rpds_py-0.29.0-cp312-cp312-win_arm64.whl", hash = "sha256:b016eddf00dca7944721bf0cd85b6af7f6c4efaf83ee0b37c4133bd39757a8c7"}, + {file = "rpds_py-0.29.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1585648d0760b88292eecab5181f5651111a69d90eff35d6b78aa32998886a61"}, + {file = "rpds_py-0.29.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:521807963971a23996ddaf764c682b3e46459b3c58ccd79fefbe16718db43154"}, + {file = "rpds_py-0.29.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a8896986efaa243ab713c69e6491a4138410f0fe36f2f4c71e18bd5501e8014"}, + {file = "rpds_py-0.29.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1d24564a700ef41480a984c5ebed62b74e6ce5860429b98b1fede76049e953e6"}, + {file = "rpds_py-0.29.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e6596b93c010d386ae46c9fba9bfc9fc5965fa8228edeac51576299182c2e31c"}, + {file = "rpds_py-0.29.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5cc58aac218826d054c7da7f95821eba94125d88be673ff44267bb89d12a5866"}, + {file = "rpds_py-0.29.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de73e40ebc04dd5d9556f50180395322193a78ec247e637e741c1b954810f295"}, + {file = "rpds_py-0.29.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:295ce5ac7f0cf69a651ea75c8f76d02a31f98e5698e82a50a5f4d4982fbbae3b"}, + {file = "rpds_py-0.29.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1ea59b23ea931d494459c8338056fe7d93458c0bf3ecc061cd03916505369d55"}, + {file = "rpds_py-0.29.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f49d41559cebd608042fdcf54ba597a4a7555b49ad5c1c0c03e0af82692661cd"}, + {file = "rpds_py-0.29.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:05a2bd42768ea988294ca328206efbcc66e220d2d9b7836ee5712c07ad6340ea"}, + {file = "rpds_py-0.29.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:33ca7bdfedd83339ca55da3a5e1527ee5870d4b8369456b5777b197756f3ca22"}, + {file = "rpds_py-0.29.0-cp313-cp313-win32.whl", hash = "sha256:20c51ae86a0bb9accc9ad4e6cdeec58d5ebb7f1b09dd4466331fc65e1766aae7"}, + {file = "rpds_py-0.29.0-cp313-cp313-win_amd64.whl", hash = "sha256:6410e66f02803600edb0b1889541f4b5cc298a5ccda0ad789cc50ef23b54813e"}, + {file = "rpds_py-0.29.0-cp313-cp313-win_arm64.whl", hash = "sha256:56838e1cd9174dc23c5691ee29f1d1be9eab357f27efef6bded1328b23e1ced2"}, + {file = "rpds_py-0.29.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:37d94eadf764d16b9a04307f2ab1d7af6dc28774bbe0535c9323101e14877b4c"}, + {file = "rpds_py-0.29.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d472cf73efe5726a067dce63eebe8215b14beabea7c12606fd9994267b3cfe2b"}, + {file = "rpds_py-0.29.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:72fdfd5ff8992e4636621826371e3ac5f3e3b8323e9d0e48378e9c13c3dac9d0"}, + {file = "rpds_py-0.29.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2549d833abdf8275c901313b9e8ff8fba57e50f6a495035a2a4e30621a2f7cc4"}, + {file = "rpds_py-0.29.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4448dad428f28a6a767c3e3b80cde3446a22a0efbddaa2360f4bb4dc836d0688"}, + {file = "rpds_py-0.29.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:115f48170fd4296a33938d8c11f697f5f26e0472e43d28f35624764173a60e4d"}, + {file = "rpds_py-0.29.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e5bb73ffc029820f4348e9b66b3027493ae00bca6629129cd433fd7a76308ee"}, + {file = "rpds_py-0.29.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:b1581fcde18fcdf42ea2403a16a6b646f8eb1e58d7f90a0ce693da441f76942e"}, + {file = "rpds_py-0.29.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:16e9da2bda9eb17ea318b4c335ec9ac1818e88922cbe03a5743ea0da9ecf74fb"}, + {file = "rpds_py-0.29.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:28fd300326dd21198f311534bdb6d7e989dd09b3418b3a91d54a0f384c700967"}, + {file = "rpds_py-0.29.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2aba991e041d031c7939e1358f583ae405a7bf04804ca806b97a5c0e0af1ea5e"}, + {file = "rpds_py-0.29.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:7f437026dbbc3f08c99cc41a5b2570c6e1a1ddbe48ab19a9b814254128d4ea7a"}, + {file = "rpds_py-0.29.0-cp313-cp313t-win32.whl", hash = "sha256:6e97846e9800a5d0fe7be4d008f0c93d0feeb2700da7b1f7528dabafb31dfadb"}, + {file = "rpds_py-0.29.0-cp313-cp313t-win_amd64.whl", hash = "sha256:f49196aec7c4b406495f60e6f947ad71f317a765f956d74bbd83996b9edc0352"}, + {file = "rpds_py-0.29.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:394d27e4453d3b4d82bb85665dc1fcf4b0badc30fc84282defed71643b50e1a1"}, + {file = "rpds_py-0.29.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:55d827b2ae95425d3be9bc9a5838b6c29d664924f98146557f7715e331d06df8"}, + {file = "rpds_py-0.29.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc31a07ed352e5462d3ee1b22e89285f4ce97d5266f6d1169da1142e78045626"}, + {file = "rpds_py-0.29.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c4695dd224212f6105db7ea62197144230b808d6b2bba52238906a2762f1d1e7"}, + {file = "rpds_py-0.29.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcae1770b401167f8b9e1e3f566562e6966ffa9ce63639916248a9e25fa8a244"}, + {file = "rpds_py-0.29.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:90f30d15f45048448b8da21c41703b31c61119c06c216a1bf8c245812a0f0c17"}, + {file = "rpds_py-0.29.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:44a91e0ab77bdc0004b43261a4b8cd6d6b451e8d443754cfda830002b5745b32"}, + {file = "rpds_py-0.29.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:4aa195e5804d32c682e453b34474f411ca108e4291c6a0f824ebdc30a91c973c"}, + {file = "rpds_py-0.29.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7971bdb7bf4ee0f7e6f67fa4c7fbc6019d9850cc977d126904392d363f6f8318"}, + {file = "rpds_py-0.29.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8ae33ad9ce580c7a47452c3b3f7d8a9095ef6208e0a0c7e4e2384f9fc5bf8212"}, + {file = "rpds_py-0.29.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:c661132ab2fb4eeede2ef69670fd60da5235209874d001a98f1542f31f2a8a94"}, + {file = "rpds_py-0.29.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:bb78b3a0d31ac1bde132c67015a809948db751cb4e92cdb3f0b242e430b6ed0d"}, + {file = "rpds_py-0.29.0-cp314-cp314-win32.whl", hash = "sha256:f475f103488312e9bd4000bc890a95955a07b2d0b6e8884aef4be56132adbbf1"}, + {file = "rpds_py-0.29.0-cp314-cp314-win_amd64.whl", hash = "sha256:b9cf2359a4fca87cfb6801fae83a76aedf66ee1254a7a151f1341632acf67f1b"}, + {file = "rpds_py-0.29.0-cp314-cp314-win_arm64.whl", hash = "sha256:9ba8028597e824854f0f1733d8b964e914ae3003b22a10c2c664cb6927e0feb9"}, + {file = "rpds_py-0.29.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:e71136fd0612556b35c575dc2726ae04a1669e6a6c378f2240312cf5d1a2ab10"}, + {file = "rpds_py-0.29.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:76fe96632d53f3bf0ea31ede2f53bbe3540cc2736d4aec3b3801b0458499ef3a"}, + {file = "rpds_py-0.29.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9459a33f077130dbb2c7c3cea72ee9932271fb3126404ba2a2661e4fe9eb7b79"}, + {file = "rpds_py-0.29.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5c9546cfdd5d45e562cc0444b6dddc191e625c62e866bf567a2c69487c7ad28a"}, + {file = "rpds_py-0.29.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12597d11d97b8f7e376c88929a6e17acb980e234547c92992f9f7c058f1a7310"}, + {file = "rpds_py-0.29.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28de03cf48b8a9e6ec10318f2197b83946ed91e2891f651a109611be4106ac4b"}, + {file = "rpds_py-0.29.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd7951c964069039acc9d67a8ff1f0a7f34845ae180ca542b17dc1456b1f1808"}, + {file = "rpds_py-0.29.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:c07d107b7316088f1ac0177a7661ca0c6670d443f6fe72e836069025e6266761"}, + {file = "rpds_py-0.29.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1de2345af363d25696969befc0c1688a6cb5e8b1d32b515ef84fc245c6cddba3"}, + {file = "rpds_py-0.29.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:00e56b12d2199ca96068057e1ae7f9998ab6e99cda82431afafd32f3ec98cca9"}, + {file = "rpds_py-0.29.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:3919a3bbecee589300ed25000b6944174e07cd20db70552159207b3f4bbb45b8"}, + {file = "rpds_py-0.29.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e7fa2ccc312bbd91e43aa5e0869e46bc03278a3dddb8d58833150a18b0f0283a"}, + {file = "rpds_py-0.29.0-cp314-cp314t-win32.whl", hash = "sha256:97c817863ffc397f1e6a6e9d2d89fe5408c0a9922dac0329672fb0f35c867ea5"}, + {file = "rpds_py-0.29.0-cp314-cp314t-win_amd64.whl", hash = "sha256:2023473f444752f0f82a58dfcbee040d0a1b3d1b3c2ec40e884bd25db6d117d2"}, + {file = "rpds_py-0.29.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:acd82a9e39082dc5f4492d15a6b6c8599aa21db5c35aaf7d6889aea16502c07d"}, + {file = "rpds_py-0.29.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:715b67eac317bf1c7657508170a3e011a1ea6ccb1c9d5f296e20ba14196be6b3"}, + {file = "rpds_py-0.29.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3b1b87a237cb2dba4db18bcfaaa44ba4cd5936b91121b62292ff21df577fc43"}, + {file = "rpds_py-0.29.0-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1c3c3e8101bb06e337c88eb0c0ede3187131f19d97d43ea0e1c5407ea74c0cbf"}, + {file = "rpds_py-0.29.0-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2b8e54d6e61f3ecd3abe032065ce83ea63417a24f437e4a3d73d2f85ce7b7cfe"}, + {file = "rpds_py-0.29.0-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3fbd4e9aebf110473a420dea85a238b254cf8a15acb04b22a5a6b5ce8925b760"}, + {file = "rpds_py-0.29.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80fdf53d36e6c72819993e35d1ebeeb8e8fc688d0c6c2b391b55e335b3afba5a"}, + {file = "rpds_py-0.29.0-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:ea7173df5d86f625f8dde6d5929629ad811ed8decda3b60ae603903839ac9ac0"}, + {file = "rpds_py-0.29.0-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:76054d540061eda273274f3d13a21a4abdde90e13eaefdc205db37c05230efce"}, + {file = "rpds_py-0.29.0-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:9f84c549746a5be3bc7415830747a3a0312573afc9f95785eb35228bb17742ec"}, + {file = "rpds_py-0.29.0-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:0ea962671af5cb9a260489e311fa22b2e97103e3f9f0caaea6f81390af96a9ed"}, + {file = "rpds_py-0.29.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:f7728653900035fb7b8d06e1e5900545d8088efc9d5d4545782da7df03ec803f"}, + {file = "rpds_py-0.29.0.tar.gz", hash = "sha256:fe55fe686908f50154d1dc599232016e50c243b438c3b7432f24e2895b0e5359"}, +] + +[[package]] +name = "rstr" +version = "3.2.2" +description = "Generate random strings in Python" +optional = false +python-versions = ">=3.7" groups = ["main"] files = [ - {file = "requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6"}, - {file = "requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06"}, + {file = "rstr-3.2.2-py3-none-any.whl", hash = "sha256:f39195d38da1748331eeec52f1276e71eb6295e7949beea91a5e9af2340d7b3b"}, + {file = "rstr-3.2.2.tar.gz", hash = "sha256:c4a564d4dfb4472d931d145c43d1cf1ad78c24592142e7755b8866179eeac012"}, ] -[package.dependencies] -requests = ">=2.0.1,<3.0.0" - [[package]] name = "ruamel-yaml" version = "0.18.15" @@ -2670,31 +2895,31 @@ jinja2 = ["ruamel.yaml.jinja2 (>=0.2)"] [[package]] name = "ruff" -version = "0.14.4" +version = "0.14.5" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" groups = ["dev"] files = [ - {file = "ruff-0.14.4-py3-none-linux_armv6l.whl", hash = "sha256:e6604613ffbcf2297cd5dcba0e0ac9bd0c11dc026442dfbb614504e87c349518"}, - {file = "ruff-0.14.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:d99c0b52b6f0598acede45ee78288e5e9b4409d1ce7f661f0fa36d4cbeadf9a4"}, - {file = "ruff-0.14.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:9358d490ec030f1b51d048a7fd6ead418ed0826daf6149e95e30aa67c168af33"}, - {file = "ruff-0.14.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81b40d27924f1f02dfa827b9c0712a13c0e4b108421665322218fc38caf615c2"}, - {file = "ruff-0.14.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f5e649052a294fe00818650712083cddc6cc02744afaf37202c65df9ea52efa5"}, - {file = "ruff-0.14.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa082a8f878deeba955531f975881828fd6afd90dfa757c2b0808aadb437136e"}, - {file = "ruff-0.14.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:1043c6811c2419e39011890f14d0a30470f19d47d197c4858b2787dfa698f6c8"}, - {file = "ruff-0.14.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a9f3a936ac27fb7c2a93e4f4b943a662775879ac579a433291a6f69428722649"}, - {file = "ruff-0.14.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:95643ffd209ce78bc113266b88fba3d39e0461f0cbc8b55fb92505030fb4a850"}, - {file = "ruff-0.14.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:456daa2fa1021bc86ca857f43fe29d5d8b3f0e55e9f90c58c317c1dcc2afc7b5"}, - {file = "ruff-0.14.4-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:f911bba769e4a9f51af6e70037bb72b70b45a16db5ce73e1f72aefe6f6d62132"}, - {file = "ruff-0.14.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:76158a7369b3979fa878612c623a7e5430c18b2fd1c73b214945c2d06337db67"}, - {file = "ruff-0.14.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:f3b8f3b442d2b14c246e7aeca2e75915159e06a3540e2f4bed9f50d062d24469"}, - {file = "ruff-0.14.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:c62da9a06779deecf4d17ed04939ae8b31b517643b26370c3be1d26f3ef7dbde"}, - {file = "ruff-0.14.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5a443a83a1506c684e98acb8cb55abaf3ef725078be40237463dae4463366349"}, - {file = "ruff-0.14.4-py3-none-win32.whl", hash = "sha256:643b69cb63cd996f1fc7229da726d07ac307eae442dd8974dbc7cf22c1e18fff"}, - {file = "ruff-0.14.4-py3-none-win_amd64.whl", hash = "sha256:26673da283b96fe35fa0c939bf8411abec47111644aa9f7cfbd3c573fb125d2c"}, - {file = "ruff-0.14.4-py3-none-win_arm64.whl", hash = "sha256:dd09c292479596b0e6fec8cd95c65c3a6dc68e9ad17b8f2382130f87ff6a75bb"}, - {file = "ruff-0.14.4.tar.gz", hash = "sha256:f459a49fe1085a749f15414ca76f61595f1a2cc8778ed7c279b6ca2e1fd19df3"}, + {file = "ruff-0.14.5-py3-none-linux_armv6l.whl", hash = "sha256:f3b8248123b586de44a8018bcc9fefe31d23dda57a34e6f0e1e53bd51fd63594"}, + {file = "ruff-0.14.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:f7a75236570318c7a30edd7f5491945f0169de738d945ca8784500b517163a72"}, + {file = "ruff-0.14.5-py3-none-macosx_11_0_arm64.whl", hash = "sha256:6d146132d1ee115f8802356a2dc9a634dbf58184c51bff21f313e8cd1c74899a"}, + {file = "ruff-0.14.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2380596653dcd20b057794d55681571a257a42327da8894b93bbd6111aa801f"}, + {file = "ruff-0.14.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2d1fa985a42b1f075a098fa1ab9d472b712bdb17ad87a8ec86e45e7fa6273e68"}, + {file = "ruff-0.14.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88f0770d42b7fa02bbefddde15d235ca3aa24e2f0137388cc15b2dcbb1f7c7a7"}, + {file = "ruff-0.14.5-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:3676cb02b9061fee7294661071c4709fa21419ea9176087cb77e64410926eb78"}, + {file = "ruff-0.14.5-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b595bedf6bc9cab647c4a173a61acf4f1ac5f2b545203ba82f30fcb10b0318fb"}, + {file = "ruff-0.14.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f55382725ad0bdb2e8ee2babcbbfb16f124f5a59496a2f6a46f1d9d99d93e6e2"}, + {file = "ruff-0.14.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7497d19dce23976bdaca24345ae131a1d38dcfe1b0850ad8e9e6e4fa321a6e19"}, + {file = "ruff-0.14.5-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:410e781f1122d6be4f446981dd479470af86537fb0b8857f27a6e872f65a38e4"}, + {file = "ruff-0.14.5-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:c01be527ef4c91a6d55e53b337bfe2c0f82af024cc1a33c44792d6844e2331e1"}, + {file = "ruff-0.14.5-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:f66e9bb762e68d66e48550b59c74314168ebb46199886c5c5aa0b0fbcc81b151"}, + {file = "ruff-0.14.5-py3-none-musllinux_1_2_i686.whl", hash = "sha256:d93be8f1fa01022337f1f8f3bcaa7ffee2d0b03f00922c45c2207954f351f465"}, + {file = "ruff-0.14.5-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:c135d4b681f7401fe0e7312017e41aba9b3160861105726b76cfa14bc25aa367"}, + {file = "ruff-0.14.5-py3-none-win32.whl", hash = "sha256:c83642e6fccfb6dea8b785eb9f456800dcd6a63f362238af5fc0c83d027dd08b"}, + {file = "ruff-0.14.5-py3-none-win_amd64.whl", hash = "sha256:9d55d7af7166f143c94eae1db3312f9ea8f95a4defef1979ed516dbb38c27621"}, + {file = "ruff-0.14.5-py3-none-win_arm64.whl", hash = "sha256:4b700459d4649e2594b31f20a9de33bc7c19976d4746d8d0798ad959621d64a4"}, + {file = "ruff-0.14.5.tar.gz", hash = "sha256:8d3b48d7d8aad423d3137af7ab6c8b1e38e4de104800f0d596990f6ada1a9fc1"}, ] [[package]] @@ -2755,6 +2980,33 @@ files = [ {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, ] +[[package]] +name = "smart-open" +version = "7.5.0" +description = "Utils for streaming large files (S3, HDFS, GCS, SFTP, Azure Blob Storage, gzip, bz2, zst...)" +optional = false +python-versions = "<4.0,>=3.9" +groups = ["main"] +files = [ + {file = "smart_open-7.5.0-py3-none-any.whl", hash = "sha256:87e695c5148bbb988f15cec00971602765874163be85acb1c9fb8abc012e6599"}, + {file = "smart_open-7.5.0.tar.gz", hash = "sha256:f394b143851d8091011832ac8113ea4aba6b92e6c35f6e677ddaaccb169d7cb9"}, +] + +[package.dependencies] +requests = {version = "*", optional = true, markers = "extra == \"http\""} +wrapt = "*" + +[package.extras] +all = ["smart_open[azure,gcs,http,s3,ssh,webhdfs,zst]"] +azure = ["azure-common", "azure-core", "azure-storage-blob"] +gcs = ["google-api-core (<2.28) ; python_version < \"3.10\"", "google-cloud-storage (>=2.6.0)"] +http = ["requests"] +s3 = ["boto3 (>=1.9.17)"] +ssh = ["paramiko"] +test = ["awscli", "flake8", "moto[server]", "numpy", "pyopenssl", "pytest", "pytest-rerunfailures", "pytest-timeout", "pytest-xdist[psutil]", "pytest_benchmark", "responses", "smart_open[all]"] +webhdfs = ["requests"] +zst = ["backports.zstd (>=1.0.0) ; python_version < \"3.14\""] + [[package]] name = "sniffio" version = "1.3.1" @@ -2779,18 +3031,6 @@ files = [ {file = "sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88"}, ] -[[package]] -name = "soupsieve" -version = "2.8" -description = "A modern CSS selector implementation for Beautiful Soup." -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "soupsieve-2.8-py3-none-any.whl", hash = "sha256:0cc76456a30e20f5d7f2e14a98a4ae2ee4e5abdc7c5ea0aafe795f344bc7984c"}, - {file = "soupsieve-2.8.tar.gz", hash = "sha256:e2dd4a40a628cb5f28f6d4b0db8800b8f581b65bb380b97de22ba5ca8d72572f"}, -] - [[package]] name = "tornado" version = "6.5.2" @@ -2855,6 +3095,18 @@ files = [ [package.dependencies] typing-extensions = ">=4.12.0" +[[package]] +name = "tzdata" +version = "2025.2" +description = "Provider of IANA time zone data" +optional = false +python-versions = ">=2" +groups = ["main"] +files = [ + {file = "tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8"}, + {file = "tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9"}, +] + [[package]] name = "urllib3" version = "2.5.0" @@ -3051,6 +3303,126 @@ files = [ [package.extras] dev = ["black (>=19.3b0) ; python_version >= \"3.6\"", "pytest (>=4.6.2)"] +[[package]] +name = "wrapt" +version = "2.0.1" +description = "Module for decorators, wrappers and monkey patching." +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "wrapt-2.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:64b103acdaa53b7caf409e8d45d39a8442fe6dcfec6ba3f3d141e0cc2b5b4dbd"}, + {file = "wrapt-2.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:91bcc576260a274b169c3098e9a3519fb01f2989f6d3d386ef9cbf8653de1374"}, + {file = "wrapt-2.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ab594f346517010050126fcd822697b25a7031d815bb4fbc238ccbe568216489"}, + {file = "wrapt-2.0.1-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:36982b26f190f4d737f04a492a68accbfc6fa042c3f42326fdfbb6c5b7a20a31"}, + {file = "wrapt-2.0.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:23097ed8bc4c93b7bf36fa2113c6c733c976316ce0ee2c816f64ca06102034ef"}, + {file = "wrapt-2.0.1-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8bacfe6e001749a3b64db47bcf0341da757c95959f592823a93931a422395013"}, + {file = "wrapt-2.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8ec3303e8a81932171f455f792f8df500fc1a09f20069e5c16bd7049ab4e8e38"}, + {file = "wrapt-2.0.1-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:3f373a4ab5dbc528a94334f9fe444395b23c2f5332adab9ff4ea82f5a9e33bc1"}, + {file = "wrapt-2.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f49027b0b9503bf6c8cdc297ca55006b80c2f5dd36cecc72c6835ab6e10e8a25"}, + {file = "wrapt-2.0.1-cp310-cp310-win32.whl", hash = "sha256:8330b42d769965e96e01fa14034b28a2a7600fbf7e8f0cc90ebb36d492c993e4"}, + {file = "wrapt-2.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:1218573502a8235bb8a7ecaed12736213b22dcde9feab115fa2989d42b5ded45"}, + {file = "wrapt-2.0.1-cp310-cp310-win_arm64.whl", hash = "sha256:eda8e4ecd662d48c28bb86be9e837c13e45c58b8300e43ba3c9b4fa9900302f7"}, + {file = "wrapt-2.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0e17283f533a0d24d6e5429a7d11f250a58d28b4ae5186f8f47853e3e70d2590"}, + {file = "wrapt-2.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:85df8d92158cb8f3965aecc27cf821461bb5f40b450b03facc5d9f0d4d6ddec6"}, + {file = "wrapt-2.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c1be685ac7700c966b8610ccc63c3187a72e33cab53526a27b2a285a662cd4f7"}, + {file = "wrapt-2.0.1-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:df0b6d3b95932809c5b3fecc18fda0f1e07452d05e2662a0b35548985f256e28"}, + {file = "wrapt-2.0.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4da7384b0e5d4cae05c97cd6f94faaf78cc8b0f791fc63af43436d98c4ab37bb"}, + {file = "wrapt-2.0.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ec65a78fbd9d6f083a15d7613b2800d5663dbb6bb96003899c834beaa68b242c"}, + {file = "wrapt-2.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7de3cc939be0e1174969f943f3b44e0d79b6f9a82198133a5b7fc6cc92882f16"}, + {file = "wrapt-2.0.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:fb1a5b72cbd751813adc02ef01ada0b0d05d3dcbc32976ce189a1279d80ad4a2"}, + {file = "wrapt-2.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3fa272ca34332581e00bf7773e993d4f632594eb2d1b0b162a9038df0fd971dd"}, + {file = "wrapt-2.0.1-cp311-cp311-win32.whl", hash = "sha256:fc007fdf480c77301ab1afdbb6ab22a5deee8885f3b1ed7afcb7e5e84a0e27be"}, + {file = "wrapt-2.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:47434236c396d04875180171ee1f3815ca1eada05e24a1ee99546320d54d1d1b"}, + {file = "wrapt-2.0.1-cp311-cp311-win_arm64.whl", hash = "sha256:837e31620e06b16030b1d126ed78e9383815cbac914693f54926d816d35d8edf"}, + {file = "wrapt-2.0.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1fdbb34da15450f2b1d735a0e969c24bdb8d8924892380126e2a293d9902078c"}, + {file = "wrapt-2.0.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3d32794fe940b7000f0519904e247f902f0149edbe6316c710a8562fb6738841"}, + {file = "wrapt-2.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:386fb54d9cd903ee0012c09291336469eb7b244f7183d40dc3e86a16a4bace62"}, + {file = "wrapt-2.0.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7b219cb2182f230676308cdcacd428fa837987b89e4b7c5c9025088b8a6c9faf"}, + {file = "wrapt-2.0.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:641e94e789b5f6b4822bb8d8ebbdfc10f4e4eae7756d648b717d980f657a9eb9"}, + {file = "wrapt-2.0.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fe21b118b9f58859b5ebaa4b130dee18669df4bd111daad082b7beb8799ad16b"}, + {file = "wrapt-2.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:17fb85fa4abc26a5184d93b3efd2dcc14deb4b09edcdb3535a536ad34f0b4dba"}, + {file = "wrapt-2.0.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:b89ef9223d665ab255ae42cc282d27d69704d94be0deffc8b9d919179a609684"}, + {file = "wrapt-2.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a453257f19c31b31ba593c30d997d6e5be39e3b5ad9148c2af5a7314061c63eb"}, + {file = "wrapt-2.0.1-cp312-cp312-win32.whl", hash = "sha256:3e271346f01e9c8b1130a6a3b0e11908049fe5be2d365a5f402778049147e7e9"}, + {file = "wrapt-2.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:2da620b31a90cdefa9cd0c2b661882329e2e19d1d7b9b920189956b76c564d75"}, + {file = "wrapt-2.0.1-cp312-cp312-win_arm64.whl", hash = "sha256:aea9c7224c302bc8bfc892b908537f56c430802560e827b75ecbde81b604598b"}, + {file = "wrapt-2.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:47b0f8bafe90f7736151f61482c583c86b0693d80f075a58701dd1549b0010a9"}, + {file = "wrapt-2.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:cbeb0971e13b4bd81d34169ed57a6dda017328d1a22b62fda45e1d21dd06148f"}, + {file = "wrapt-2.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:eb7cffe572ad0a141a7886a1d2efa5bef0bf7fe021deeea76b3ab334d2c38218"}, + {file = "wrapt-2.0.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c8d60527d1ecfc131426b10d93ab5d53e08a09c5fa0175f6b21b3252080c70a9"}, + {file = "wrapt-2.0.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c654eafb01afac55246053d67a4b9a984a3567c3808bb7df2f8de1c1caba2e1c"}, + {file = "wrapt-2.0.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:98d873ed6c8b4ee2418f7afce666751854d6d03e3c0ec2a399bb039cd2ae89db"}, + {file = "wrapt-2.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c9e850f5b7fc67af856ff054c71690d54fa940c3ef74209ad9f935b4f66a0233"}, + {file = "wrapt-2.0.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:e505629359cb5f751e16e30cf3f91a1d3ddb4552480c205947da415d597f7ac2"}, + {file = "wrapt-2.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2879af909312d0baf35f08edeea918ee3af7ab57c37fe47cb6a373c9f2749c7b"}, + {file = "wrapt-2.0.1-cp313-cp313-win32.whl", hash = "sha256:d67956c676be5a24102c7407a71f4126d30de2a569a1c7871c9f3cabc94225d7"}, + {file = "wrapt-2.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:9ca66b38dd642bf90c59b6738af8070747b610115a39af2498535f62b5cdc1c3"}, + {file = "wrapt-2.0.1-cp313-cp313-win_arm64.whl", hash = "sha256:5a4939eae35db6b6cec8e7aa0e833dcca0acad8231672c26c2a9ab7a0f8ac9c8"}, + {file = "wrapt-2.0.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:a52f93d95c8d38fed0669da2ebdb0b0376e895d84596a976c15a9eb45e3eccb3"}, + {file = "wrapt-2.0.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4e54bbf554ee29fcceee24fa41c4d091398b911da6e7f5d7bffda963c9aed2e1"}, + {file = "wrapt-2.0.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:908f8c6c71557f4deaa280f55d0728c3bca0960e8c3dd5ceeeafb3c19942719d"}, + {file = "wrapt-2.0.1-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:e2f84e9af2060e3904a32cea9bb6db23ce3f91cfd90c6b426757cf7cc01c45c7"}, + {file = "wrapt-2.0.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e3612dc06b436968dfb9142c62e5dfa9eb5924f91120b3c8ff501ad878f90eb3"}, + {file = "wrapt-2.0.1-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6d2d947d266d99a1477cd005b23cbd09465276e302515e122df56bb9511aca1b"}, + {file = "wrapt-2.0.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:7d539241e87b650cbc4c3ac9f32c8d1ac8a54e510f6dca3f6ab60dcfd48c9b10"}, + {file = "wrapt-2.0.1-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:4811e15d88ee62dbf5c77f2c3ff3932b1e3ac92323ba3912f51fc4016ce81ecf"}, + {file = "wrapt-2.0.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c1c91405fcf1d501fa5d55df21e58ea49e6b879ae829f1039faaf7e5e509b41e"}, + {file = "wrapt-2.0.1-cp313-cp313t-win32.whl", hash = "sha256:e76e3f91f864e89db8b8d2a8311d57df93f01ad6bb1e9b9976d1f2e83e18315c"}, + {file = "wrapt-2.0.1-cp313-cp313t-win_amd64.whl", hash = "sha256:83ce30937f0ba0d28818807b303a412440c4b63e39d3d8fc036a94764b728c92"}, + {file = "wrapt-2.0.1-cp313-cp313t-win_arm64.whl", hash = "sha256:4b55cacc57e1dc2d0991dbe74c6419ffd415fb66474a02335cb10efd1aa3f84f"}, + {file = "wrapt-2.0.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:5e53b428f65ece6d9dad23cb87e64506392b720a0b45076c05354d27a13351a1"}, + {file = "wrapt-2.0.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:ad3ee9d0f254851c71780966eb417ef8e72117155cff04821ab9b60549694a55"}, + {file = "wrapt-2.0.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:d7b822c61ed04ee6ad64bc90d13368ad6eb094db54883b5dde2182f67a7f22c0"}, + {file = "wrapt-2.0.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7164a55f5e83a9a0b031d3ffab4d4e36bbec42e7025db560f225489fa929e509"}, + {file = "wrapt-2.0.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e60690ba71a57424c8d9ff28f8d006b7ad7772c22a4af432188572cd7fa004a1"}, + {file = "wrapt-2.0.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3cd1a4bd9a7a619922a8557e1318232e7269b5fb69d4ba97b04d20450a6bf970"}, + {file = "wrapt-2.0.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b4c2e3d777e38e913b8ce3a6257af72fb608f86a1df471cb1d4339755d0a807c"}, + {file = "wrapt-2.0.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:3d366aa598d69416b5afedf1faa539fac40c1d80a42f6b236c88c73a3c8f2d41"}, + {file = "wrapt-2.0.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c235095d6d090aa903f1db61f892fffb779c1eaeb2a50e566b52001f7a0f66ed"}, + {file = "wrapt-2.0.1-cp314-cp314-win32.whl", hash = "sha256:bfb5539005259f8127ea9c885bdc231978c06b7a980e63a8a61c8c4c979719d0"}, + {file = "wrapt-2.0.1-cp314-cp314-win_amd64.whl", hash = "sha256:4ae879acc449caa9ed43fc36ba08392b9412ee67941748d31d94e3cedb36628c"}, + {file = "wrapt-2.0.1-cp314-cp314-win_arm64.whl", hash = "sha256:8639b843c9efd84675f1e100ed9e99538ebea7297b62c4b45a7042edb84db03e"}, + {file = "wrapt-2.0.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:9219a1d946a9b32bb23ccae66bdb61e35c62773ce7ca6509ceea70f344656b7b"}, + {file = "wrapt-2.0.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:fa4184e74197af3adad3c889a1af95b53bb0466bced92ea99a0c014e48323eec"}, + {file = "wrapt-2.0.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c5ef2f2b8a53b7caee2f797ef166a390fef73979b15778a4a153e4b5fedce8fa"}, + {file = "wrapt-2.0.1-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:e042d653a4745be832d5aa190ff80ee4f02c34b21f4b785745eceacd0907b815"}, + {file = "wrapt-2.0.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2afa23318136709c4b23d87d543b425c399887b4057936cd20386d5b1422b6fa"}, + {file = "wrapt-2.0.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6c72328f668cf4c503ffcf9434c2b71fdd624345ced7941bc6693e61bbe36bef"}, + {file = "wrapt-2.0.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:3793ac154afb0e5b45d1233cb94d354ef7a983708cc3bb12563853b1d8d53747"}, + {file = "wrapt-2.0.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:fec0d993ecba3991645b4857837277469c8cc4c554a7e24d064d1ca291cfb81f"}, + {file = "wrapt-2.0.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:949520bccc1fa227274da7d03bf238be15389cd94e32e4297b92337df9b7a349"}, + {file = "wrapt-2.0.1-cp314-cp314t-win32.whl", hash = "sha256:be9e84e91d6497ba62594158d3d31ec0486c60055c49179edc51ee43d095f79c"}, + {file = "wrapt-2.0.1-cp314-cp314t-win_amd64.whl", hash = "sha256:61c4956171c7434634401db448371277d07032a81cc21c599c22953374781395"}, + {file = "wrapt-2.0.1-cp314-cp314t-win_arm64.whl", hash = "sha256:35cdbd478607036fee40273be8ed54a451f5f23121bd9d4be515158f9498f7ad"}, + {file = "wrapt-2.0.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:90897ea1cf0679763b62e79657958cd54eae5659f6360fc7d2ccc6f906342183"}, + {file = "wrapt-2.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:50844efc8cdf63b2d90cd3d62d4947a28311e6266ce5235a219d21b195b4ec2c"}, + {file = "wrapt-2.0.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:49989061a9977a8cbd6d20f2efa813f24bf657c6990a42967019ce779a878dbf"}, + {file = "wrapt-2.0.1-cp38-cp38-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:09c7476ab884b74dce081ad9bfd07fe5822d8600abade571cb1f66d5fc915af6"}, + {file = "wrapt-2.0.1-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d1a8a09a004ef100e614beec82862d11fc17d601092c3599afd22b1f36e4137e"}, + {file = "wrapt-2.0.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:89a82053b193837bf93c0f8a57ded6e4b6d88033a499dadff5067e912c2a41e9"}, + {file = "wrapt-2.0.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:f26f8e2ca19564e2e1fdbb6a0e47f36e0efbab1acc31e15471fad88f828c75f6"}, + {file = "wrapt-2.0.1-cp38-cp38-win32.whl", hash = "sha256:115cae4beed3542e37866469a8a1f2b9ec549b4463572b000611e9946b86e6f6"}, + {file = "wrapt-2.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:c4012a2bd37059d04f8209916aa771dfb564cccb86079072bdcd48a308b6a5c5"}, + {file = "wrapt-2.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:68424221a2dc00d634b54f92441914929c5ffb1c30b3b837343978343a3512a3"}, + {file = "wrapt-2.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6bd1a18f5a797fe740cb3d7a0e853a8ce6461cc62023b630caec80171a6b8097"}, + {file = "wrapt-2.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fb3a86e703868561c5cad155a15c36c716e1ab513b7065bd2ac8ed353c503333"}, + {file = "wrapt-2.0.1-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5dc1b852337c6792aa111ca8becff5bacf576bf4a0255b0f05eb749da6a1643e"}, + {file = "wrapt-2.0.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c046781d422f0830de6329fa4b16796096f28a92c8aef3850674442cdcb87b7f"}, + {file = "wrapt-2.0.1-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f73f9f7a0ebd0db139253d27e5fc8d2866ceaeef19c30ab5d69dcbe35e1a6981"}, + {file = "wrapt-2.0.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:b667189cf8efe008f55bbda321890bef628a67ab4147ebf90d182f2dadc78790"}, + {file = "wrapt-2.0.1-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:a9a83618c4f0757557c077ef71d708ddd9847ed66b7cc63416632af70d3e2308"}, + {file = "wrapt-2.0.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1e9b121e9aeb15df416c2c960b8255a49d44b4038016ee17af03975992d03931"}, + {file = "wrapt-2.0.1-cp39-cp39-win32.whl", hash = "sha256:1f186e26ea0a55f809f232e92cc8556a0977e00183c3ebda039a807a42be1494"}, + {file = "wrapt-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:bf4cb76f36be5de950ce13e22e7fdf462b35b04665a12b64f3ac5c1bbbcf3728"}, + {file = "wrapt-2.0.1-cp39-cp39-win_arm64.whl", hash = "sha256:d6cc985b9c8b235bd933990cdbf0f891f8e010b65a3911f7a55179cd7b0fc57b"}, + {file = "wrapt-2.0.1-py3-none-any.whl", hash = "sha256:4d2ce1bf1a48c5277d7969259232b57645aae5686dba1eaeade39442277afbca"}, + {file = "wrapt-2.0.1.tar.gz", hash = "sha256:9c9c635e78497cacb81e84f8b11b23e0aacac7a136e73b8e5b2109a1d9fc468f"}, +] + +[package.extras] +dev = ["pytest", "setuptools"] + [[package]] name = "wsproto" version = "1.2.0" @@ -3181,4 +3553,4 @@ cffi = ["cffi (>=1.17,<2.0) ; platform_python_implementation != \"PyPy\" and pyt [metadata] lock-version = "2.1" python-versions = "^3.14" -content-hash = "b2392eb7778873783180126e2e26b849bcc02cf948a7244b5b16240dfdd69f34" +content-hash = "a608b28c4a5f08ea116de52c2a9bd9a838847c79ade7b445efde0abf678c3671" \ No newline at end of file diff --git a/catalyst-gateway/tests/api_tests/pyproject.toml b/catalyst-gateway/tests/api_tests/pyproject.toml index b0585496301..06f56e3488c 100644 --- a/catalyst-gateway/tests/api_tests/pyproject.toml +++ b/catalyst-gateway/tests/api_tests/pyproject.toml @@ -1,4 +1,4 @@ -# cspell: words bitcoinlib addopts beautifulsoup cloudscraper certifi +# cspell: words bitcoinlib addopts certifi [tool.poetry] name = "api_tests" @@ -19,8 +19,6 @@ asyncpg = "^0.30.0" requests = "^2.32.3" python-bitcoinlib = "^0.12.2" pycardano = "^0.17.0" -beautifulsoup4 = "^4.13.4" -cloudscraper = "^1.2.71" certifi = "^2025.1.31" cbor2 = "^5.6.5" uuid = "^1.30" From e79bce21c7ff7ecefa1374e331342b36582f51c5 Mon Sep 17 00:00:00 2001 From: Apisit Ritruengroj Date: Fri, 21 Nov 2025 17:32:08 +0700 Subject: [PATCH 18/40] fix: comments --- .../tests/api_tests/utils/signed_doc.py | 90 +++++++++++-------- 1 file changed, 54 insertions(+), 36 deletions(-) diff --git a/catalyst-gateway/tests/api_tests/utils/signed_doc.py b/catalyst-gateway/tests/api_tests/utils/signed_doc.py index b64245280c4..926dda9a9e5 100644 --- a/catalyst-gateway/tests/api_tests/utils/signed_doc.py +++ b/catalyst-gateway/tests/api_tests/utils/signed_doc.py @@ -105,19 +105,27 @@ def __init__( def generic_doc_builder( - rbac_chain: RBACChain, doc_type: str, parameters: list[dict], content: Any + rbac_chain: RBACChain, + doc_type: str, + templates: list[dict] | None, + parameters: list[dict] | None, + content: Any, ) -> DocBuilderReturns: role_id = RoleID.PROPOSER doc_id = uuid_v7.uuid_v7() - metadata = { + metadata: dict[str, Any] = { "content-encoding": "br", "content-type": "application/json", "id": doc_id, "ver": doc_id, "type": doc_type, - "parameters": parameters, } + if templates is not None: + metadata["templates"] = templates + if parameters is not None: + metadata["parameters"] = parameters + doc_builder = SignedDocument(metadata, content) (cat_id, sk_hex) = rbac_chain.cat_id_for_role(role_id) @@ -192,25 +200,25 @@ class ProposalParameterType(IntEnum): @pytest.fixture def proposal_doc_factory( rbac_chain_factory, - proposal_form_template_doc_factory, - category_parameters_doc_factory, - campaign_parameters_doc_factory, - brand_parameters_doc_factory, + proposal_form_template_doc, + category_parameters_doc, + campaign_parameters_doc, + brand_parameters_doc, ): def __factory__( parameter_type: ProposalParameterType, ) -> tuple[SignedDocument, RoleID]: param: SignedDocumentBase if parameter_type == ProposalParameterType.CATEGORY: - param = category_parameters_doc_factory + param = category_parameters_doc elif parameter_type == ProposalParameterType.CAMPAIGN: - param = campaign_parameters_doc_factory + param = campaign_parameters_doc elif parameter_type == ProposalParameterType.BRAND: - param = brand_parameters_doc_factory + param = brand_parameters_doc else: raise Exception("Invalid parameter type for proposal document") - template: SignedDocumentBase = proposal_form_template_doc_factory + template: SignedDocumentBase = proposal_form_template_doc result = generic_doc_builder( rbac_chain_factory(), @@ -220,7 +228,9 @@ def __factory__( "id": template.metadata["id"], "ver": template.metadata["ver"], "cid": "0x", - }, + } + ], + [ {"id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x"}, ], JSF(template.content).generate(), @@ -240,7 +250,7 @@ def __factory__( @pytest.fixture -def proposal_form_template_doc_factory(rbac_chain_factory) -> SignedDocumentBase: +def proposal_form_template_doc(rbac_chain_factory) -> SignedDocumentBase: role_id = RoleID.PROPOSER with open("./test_data/signed_docs/proposal_form_template.json", "r") as json_file: metadata, content = json.load(json_file) @@ -261,13 +271,13 @@ def proposal_form_template_doc_factory(rbac_chain_factory) -> SignedDocumentBase @pytest.fixture -def category_parameters_doc_factory( +def category_parameters_doc( rbac_chain_factory, - category_parameters_form_template_doc_factory, - campaign_parameters_doc_factory, + category_parameters_form_template_doc, + campaign_parameters_doc, ) -> SignedDocumentBase: - template: SignedDocumentBase = category_parameters_form_template_doc_factory - param: SignedDocumentBase = campaign_parameters_doc_factory + template: SignedDocumentBase = category_parameters_form_template_doc + param: SignedDocumentBase = campaign_parameters_doc result = generic_doc_builder( rbac_chain_factory(), @@ -277,7 +287,9 @@ def category_parameters_doc_factory( "id": template.metadata["id"], "ver": template.metadata["ver"], "cid": "0x", - }, + } + ], + [ {"id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x"}, ], JSF(template.content).generate(), @@ -295,14 +307,15 @@ def category_parameters_doc_factory( @pytest.fixture -def category_parameters_form_template_doc_factory( - rbac_chain_factory, campaign_parameters_doc_factory +def category_parameters_form_template_doc( + rbac_chain_factory, campaign_parameters_doc ) -> SignedDocumentBase: - param: SignedDocumentBase = campaign_parameters_doc_factory + param: SignedDocumentBase = campaign_parameters_doc result = generic_doc_builder( rbac_chain_factory(), DOC_TYPE["category_parameters_form_template"], + None, [{"id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x"}], {"type": "object"}, ) @@ -319,13 +332,13 @@ def category_parameters_form_template_doc_factory( @pytest.fixture -def campaign_parameters_doc_factory( +def campaign_parameters_doc( rbac_chain_factory, - campaign_parameters_form_template_doc_factory, - brand_parameters_doc_factory, + campaign_parameters_form_template_doc, + brand_parameters_doc, ) -> SignedDocumentBase: - template: SignedDocumentBase = campaign_parameters_form_template_doc_factory - param: SignedDocumentBase = brand_parameters_doc_factory + template: SignedDocumentBase = campaign_parameters_form_template_doc + param: SignedDocumentBase = brand_parameters_doc result = generic_doc_builder( rbac_chain_factory(), @@ -335,7 +348,9 @@ def campaign_parameters_doc_factory( "id": template.metadata["id"], "ver": template.metadata["ver"], "cid": "0x", - }, + } + ], + [ {"id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x"}, ], JSF(template.content).generate(), @@ -353,14 +368,15 @@ def campaign_parameters_doc_factory( @pytest.fixture -def campaign_parameters_form_template_doc_factory( - rbac_chain_factory, brand_parameters_doc_factory +def campaign_parameters_form_template_doc( + rbac_chain_factory, brand_parameters_doc ) -> SignedDocumentBase: - param: SignedDocumentBase = brand_parameters_doc_factory + param: SignedDocumentBase = brand_parameters_doc result = generic_doc_builder( rbac_chain_factory(), DOC_TYPE["campaign_parameters_form_template"], + None, [{"id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x"}], {"type": "object"}, ) @@ -377,10 +393,10 @@ def campaign_parameters_form_template_doc_factory( @pytest.fixture -def brand_parameters_doc_factory( - rbac_chain_factory, brand_parameters_form_template_doc_factory +def brand_parameters_doc( + rbac_chain_factory, brand_parameters_form_template_doc ) -> SignedDocumentBase: - template: SignedDocumentBase = brand_parameters_form_template_doc_factory + template: SignedDocumentBase = brand_parameters_form_template_doc result = generic_doc_builder( rbac_chain_factory(), @@ -392,6 +408,7 @@ def brand_parameters_doc_factory( "cid": "0x", } ], + None, JSF(template.content).generate(), ) @@ -407,13 +424,14 @@ def brand_parameters_doc_factory( @pytest.fixture -def brand_parameters_form_template_doc_factory( +def brand_parameters_form_template_doc( rbac_chain_factory, ) -> SignedDocumentBase: result = generic_doc_builder( rbac_chain_factory(), DOC_TYPE["brand_parameters_form_template"], - [], + None, + None, {"type": "object"}, ) From c92b49370f1c33d8c286acfbc1a6dddf47fd1c24 Mon Sep 17 00:00:00 2001 From: Apisit Ritruengroj Date: Mon, 24 Nov 2025 15:54:29 +0700 Subject: [PATCH 19/40] chore: move metadata --- .../tests/api_tests/utils/signed_doc.py | 71 +++++++++++++------ 1 file changed, 50 insertions(+), 21 deletions(-) diff --git a/catalyst-gateway/tests/api_tests/utils/signed_doc.py b/catalyst-gateway/tests/api_tests/utils/signed_doc.py index 926dda9a9e5..2415af5ad36 100644 --- a/catalyst-gateway/tests/api_tests/utils/signed_doc.py +++ b/catalyst-gateway/tests/api_tests/utils/signed_doc.py @@ -104,15 +104,13 @@ def __init__( self.role_id = role_id -def generic_doc_builder( - rbac_chain: RBACChain, +def create_metadata( doc_type: str, - templates: list[dict] | None, - parameters: list[dict] | None, - content: Any, -) -> DocBuilderReturns: - role_id = RoleID.PROPOSER + templates: list[Any] | None = None, + parameters: list[Any] | None = None, +) -> dict[str, Any]: doc_id = uuid_v7.uuid_v7() + metadata: dict[str, Any] = { "content-encoding": "br", "content-type": "application/json", @@ -126,6 +124,16 @@ def generic_doc_builder( if parameters is not None: metadata["parameters"] = parameters + return metadata + + +def generic_doc_builder( + rbac_chain: RBACChain, + metadata: dict[str, Any], + content: Any, +) -> DocBuilderReturns: + role_id = RoleID.PROPOSER + doc_builder = SignedDocument(metadata, content) (cat_id, sk_hex) = rbac_chain.cat_id_for_role(role_id) @@ -220,8 +228,7 @@ def __factory__( template: SignedDocumentBase = proposal_form_template_doc - result = generic_doc_builder( - rbac_chain_factory(), + metadata = create_metadata( DOC_TYPE["proposal"], [ { @@ -233,6 +240,10 @@ def __factory__( [ {"id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x"}, ], + ) + result = generic_doc_builder( + rbac_chain_factory(), + metadata, JSF(template.content).generate(), ) @@ -279,8 +290,7 @@ def category_parameters_doc( template: SignedDocumentBase = category_parameters_form_template_doc param: SignedDocumentBase = campaign_parameters_doc - result = generic_doc_builder( - rbac_chain_factory(), + metadata = create_metadata( DOC_TYPE["category_parameters"], [ { @@ -292,6 +302,10 @@ def category_parameters_doc( [ {"id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x"}, ], + ) + result = generic_doc_builder( + rbac_chain_factory(), + metadata, JSF(template.content).generate(), ) @@ -312,11 +326,14 @@ def category_parameters_form_template_doc( ) -> SignedDocumentBase: param: SignedDocumentBase = campaign_parameters_doc - result = generic_doc_builder( - rbac_chain_factory(), + metadata = create_metadata( DOC_TYPE["category_parameters_form_template"], None, [{"id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x"}], + ) + result = generic_doc_builder( + rbac_chain_factory(), + metadata, {"type": "object"}, ) @@ -340,8 +357,7 @@ def campaign_parameters_doc( template: SignedDocumentBase = campaign_parameters_form_template_doc param: SignedDocumentBase = brand_parameters_doc - result = generic_doc_builder( - rbac_chain_factory(), + metadata = create_metadata( DOC_TYPE["campaign_parameters"], [ { @@ -353,6 +369,10 @@ def campaign_parameters_doc( [ {"id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x"}, ], + ) + result = generic_doc_builder( + rbac_chain_factory(), + metadata, JSF(template.content).generate(), ) @@ -373,11 +393,14 @@ def campaign_parameters_form_template_doc( ) -> SignedDocumentBase: param: SignedDocumentBase = brand_parameters_doc - result = generic_doc_builder( - rbac_chain_factory(), + metadata = create_metadata( DOC_TYPE["campaign_parameters_form_template"], None, [{"id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x"}], + ) + result = generic_doc_builder( + rbac_chain_factory(), + metadata, {"type": "object"}, ) @@ -398,8 +421,7 @@ def brand_parameters_doc( ) -> SignedDocumentBase: template: SignedDocumentBase = brand_parameters_form_template_doc - result = generic_doc_builder( - rbac_chain_factory(), + metadata = create_metadata( DOC_TYPE["brand_parameters"], [ { @@ -409,6 +431,10 @@ def brand_parameters_doc( } ], None, + ) + result = generic_doc_builder( + rbac_chain_factory(), + metadata, JSF(template.content).generate(), ) @@ -427,11 +453,14 @@ def brand_parameters_doc( def brand_parameters_form_template_doc( rbac_chain_factory, ) -> SignedDocumentBase: - result = generic_doc_builder( - rbac_chain_factory(), + metadata = create_metadata( DOC_TYPE["brand_parameters_form_template"], None, None, + ) + result = generic_doc_builder( + rbac_chain_factory(), + metadata, {"type": "object"}, ) From 2450adf907ad869f8e843df6603977de297688f3 Mon Sep 17 00:00:00 2001 From: Apisit Ritruengroj Date: Mon, 24 Nov 2025 16:08:17 +0700 Subject: [PATCH 20/40] fix: type --- catalyst-gateway/tests/api_tests/utils/signed_doc.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/catalyst-gateway/tests/api_tests/utils/signed_doc.py b/catalyst-gateway/tests/api_tests/utils/signed_doc.py index 2415af5ad36..683fc866443 100644 --- a/catalyst-gateway/tests/api_tests/utils/signed_doc.py +++ b/catalyst-gateway/tests/api_tests/utils/signed_doc.py @@ -7,10 +7,9 @@ import json from api.v1 import document from utils import signed_doc, uuid_v7 -from utils.rbac_chain import rbac_chain_factory, RoleID +from utils.rbac_chain import rbac_chain_factory, RBACChain, RoleID from tempfile import NamedTemporaryFile from jsf import JSF -from rbac_chain import RBACChain from enum import IntEnum From e40ba7309163a419a240e05598319f4674d447e8 Mon Sep 17 00:00:00 2001 From: Apisit Ritruengroj Date: Tue, 25 Nov 2025 14:58:26 +0700 Subject: [PATCH 21/40] chore: reorder fixtures --- .../tests/api_tests/utils/signed_doc.py | 252 ++++++++---------- 1 file changed, 108 insertions(+), 144 deletions(-) diff --git a/catalyst-gateway/tests/api_tests/utils/signed_doc.py b/catalyst-gateway/tests/api_tests/utils/signed_doc.py index 683fc866443..ce38b452683 100644 --- a/catalyst-gateway/tests/api_tests/utils/signed_doc.py +++ b/catalyst-gateway/tests/api_tests/utils/signed_doc.py @@ -203,94 +203,40 @@ class ProposalParameterType(IntEnum): BRAND = 2 -# return a Proposal document which is already published to the cat-gateway and the corresponding RoleID @pytest.fixture -def proposal_doc_factory( +def brand_parameters_form_template_doc( rbac_chain_factory, - proposal_form_template_doc, - category_parameters_doc, - campaign_parameters_doc, - brand_parameters_doc, -): - def __factory__( - parameter_type: ProposalParameterType, - ) -> tuple[SignedDocument, RoleID]: - param: SignedDocumentBase - if parameter_type == ProposalParameterType.CATEGORY: - param = category_parameters_doc - elif parameter_type == ProposalParameterType.CAMPAIGN: - param = campaign_parameters_doc - elif parameter_type == ProposalParameterType.BRAND: - param = brand_parameters_doc - else: - raise Exception("Invalid parameter type for proposal document") - - template: SignedDocumentBase = proposal_form_template_doc - - metadata = create_metadata( - DOC_TYPE["proposal"], - [ - { - "id": template.metadata["id"], - "ver": template.metadata["ver"], - "cid": "0x", - } - ], - [ - {"id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x"}, - ], - ) - result = generic_doc_builder( - rbac_chain_factory(), - metadata, - JSF(template.content).generate(), - ) - - resp = document.put( - data=result.signed_doc, - token=result.auth_token, - ) - assert resp.status_code == 201, ( - f"Failed to publish document: {resp.status_code} - {resp.text}" - ) - - return result.doc_builder, result.role_id - - return __factory__ - - -@pytest.fixture -def proposal_form_template_doc(rbac_chain_factory) -> SignedDocumentBase: - role_id = RoleID.PROPOSER - with open("./test_data/signed_docs/proposal_form_template.json", "r") as json_file: - metadata, content = json.load(json_file) - - doc_builder = SignedDocument(metadata, content) - rbac_chain = rbac_chain_factory() - (cat_id, sk_hex) = rbac_chain.cat_id_for_role(role_id) +) -> SignedDocumentBase: + metadata = create_metadata( + DOC_TYPE["brand_parameters_form_template"], + None, + None, + ) + result = generic_doc_builder( + rbac_chain_factory(), + metadata, + {"type": "object"}, + ) resp = document.put( - data=doc_builder.build_and_sign(cat_id, sk_hex), - token=rbac_chain.auth_token(), + data=result.signed_doc, + token=result.auth_token, ) assert resp.status_code == 201, ( f"Failed to publish document: {resp.status_code} - {resp.text}" ) - return SignedDocumentBase(metadata, content) + return SignedDocumentBase(result.doc_builder.metadata, result.doc_builder.content) @pytest.fixture -def category_parameters_doc( - rbac_chain_factory, - category_parameters_form_template_doc, - campaign_parameters_doc, +def brand_parameters_doc( + rbac_chain_factory, brand_parameters_form_template_doc ) -> SignedDocumentBase: - template: SignedDocumentBase = category_parameters_form_template_doc - param: SignedDocumentBase = campaign_parameters_doc + template: SignedDocumentBase = brand_parameters_form_template_doc metadata = create_metadata( - DOC_TYPE["category_parameters"], + DOC_TYPE["brand_parameters"], [ { "id": template.metadata["id"], @@ -298,9 +244,7 @@ def category_parameters_doc( "cid": "0x", } ], - [ - {"id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x"}, - ], + None, ) result = generic_doc_builder( rbac_chain_factory(), @@ -312,21 +256,19 @@ def category_parameters_doc( data=result.signed_doc, token=result.auth_token, ) - assert resp.status_code == 201, ( - f"Failed to publish document: {resp.status_code} - {resp.text}" - ) + assert resp.status_code == 201 return SignedDocumentBase(result.doc_builder.metadata, result.doc_builder.content) @pytest.fixture -def category_parameters_form_template_doc( - rbac_chain_factory, campaign_parameters_doc +def campaign_parameters_form_template_doc( + rbac_chain_factory, brand_parameters_doc ) -> SignedDocumentBase: - param: SignedDocumentBase = campaign_parameters_doc + param: SignedDocumentBase = brand_parameters_doc metadata = create_metadata( - DOC_TYPE["category_parameters_form_template"], + DOC_TYPE["campaign_parameters_form_template"], None, [{"id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x"}], ) @@ -340,9 +282,7 @@ def category_parameters_form_template_doc( data=result.signed_doc, token=result.auth_token, ) - assert resp.status_code == 201, ( - f"Failed to publish document: {resp.status_code} - {resp.text}" - ) + assert resp.status_code == 201 return SignedDocumentBase(result.doc_builder.metadata, result.doc_builder.content) @@ -365,110 +305,134 @@ def campaign_parameters_doc( "cid": "0x", } ], - [ - {"id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x"}, - ], + [{"id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x"}], ) result = generic_doc_builder( - rbac_chain_factory(), - metadata, - JSF(template.content).generate(), + rbac_chain_factory(), metadata, JSF(template.content).generate() ) resp = document.put( data=result.signed_doc, token=result.auth_token, ) - assert resp.status_code == 201, ( - f"Failed to publish document: {resp.status_code} - {resp.text}" - ) + assert resp.status_code == 201 return SignedDocumentBase(result.doc_builder.metadata, result.doc_builder.content) @pytest.fixture -def campaign_parameters_form_template_doc( - rbac_chain_factory, brand_parameters_doc +def category_parameters_form_template_doc( + rbac_chain_factory, campaign_parameters_doc ) -> SignedDocumentBase: - param: SignedDocumentBase = brand_parameters_doc + param: SignedDocumentBase = campaign_parameters_doc metadata = create_metadata( - DOC_TYPE["campaign_parameters_form_template"], + DOC_TYPE["category_parameters_form_template"], None, [{"id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x"}], ) - result = generic_doc_builder( - rbac_chain_factory(), - metadata, - {"type": "object"}, - ) + result = generic_doc_builder(rbac_chain_factory(), metadata, {"type": "object"}) resp = document.put( data=result.signed_doc, token=result.auth_token, ) - assert resp.status_code == 201, ( - f"Failed to publish document: {resp.status_code} - {resp.text}" - ) + assert resp.status_code == 201 return SignedDocumentBase(result.doc_builder.metadata, result.doc_builder.content) @pytest.fixture -def brand_parameters_doc( - rbac_chain_factory, brand_parameters_form_template_doc +def category_parameters_doc( + rbac_chain_factory, + category_parameters_form_template_doc, + campaign_parameters_doc, ) -> SignedDocumentBase: - template: SignedDocumentBase = brand_parameters_form_template_doc + template = category_parameters_form_template_doc + param = campaign_parameters_doc metadata = create_metadata( - DOC_TYPE["brand_parameters"], - [ - { - "id": template.metadata["id"], - "ver": template.metadata["ver"], - "cid": "0x", - } - ], - None, + DOC_TYPE["category_parameters"], + [{"id": template.metadata["id"], "ver": template.metadata["ver"], "cid": "0x"}], + [{"id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x"}], ) result = generic_doc_builder( - rbac_chain_factory(), - metadata, - JSF(template.content).generate(), + rbac_chain_factory(), metadata, JSF(template.content).generate() ) resp = document.put( data=result.signed_doc, token=result.auth_token, ) - assert resp.status_code == 201, ( - f"Failed to publish document: {resp.status_code} - {resp.text}" - ) + assert resp.status_code == 201 return SignedDocumentBase(result.doc_builder.metadata, result.doc_builder.content) @pytest.fixture -def brand_parameters_form_template_doc( - rbac_chain_factory, -) -> SignedDocumentBase: - metadata = create_metadata( - DOC_TYPE["brand_parameters_form_template"], - None, - None, - ) - result = generic_doc_builder( - rbac_chain_factory(), - metadata, - {"type": "object"}, - ) +def proposal_form_template_doc(rbac_chain_factory) -> SignedDocumentBase: + role_id = RoleID.PROPOSER + with open("./test_data/signed_docs/proposal_form_template.json", "r") as json_file: + metadata, content = json.load(json_file) + + doc_builder = SignedDocument(metadata, content) + rbac_chain = rbac_chain_factory() + (cat_id, sk_hex) = rbac_chain.cat_id_for_role(role_id) resp = document.put( - data=result.signed_doc, - token=result.auth_token, - ) - assert resp.status_code == 201, ( - f"Failed to publish document: {resp.status_code} - {resp.text}" + data=doc_builder.build_and_sign(cat_id, sk_hex), + token=rbac_chain.auth_token(), ) + assert resp.status_code == 201 - return SignedDocumentBase(result.doc_builder.metadata, result.doc_builder.content) + return SignedDocumentBase(metadata, content) + + +# return a Proposal document which is already published to the cat-gateway and the corresponding RoleID +@pytest.fixture +def proposal_doc_factory( + rbac_chain_factory, + proposal_form_template_doc, + category_parameters_doc, + campaign_parameters_doc, + brand_parameters_doc, +): + def __factory__( + parameter_type: ProposalParameterType, + ) -> tuple[SignedDocument, RoleID]: + if parameter_type == ProposalParameterType.CATEGORY: + param = category_parameters_doc + elif parameter_type == ProposalParameterType.CAMPAIGN: + param = campaign_parameters_doc + elif parameter_type == ProposalParameterType.BRAND: + param = brand_parameters_doc + else: + raise Exception("Invalid parameter type for proposal document") + + template = proposal_form_template_doc + + metadata = create_metadata( + DOC_TYPE["proposal"], + [ + { + "id": template.metadata["id"], + "ver": template.metadata["ver"], + "cid": "0x", + } + ], + [{"id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x"}], + ) + + result = generic_doc_builder( + rbac_chain_factory(), metadata, JSF(template.content).generate() + ) + + resp = document.put( + data=result.signed_doc, + token=result.auth_token, + ) + assert resp.status_code == 201 + + return result.doc_builder, result.role_id + + return __factory__ From 6a983fc123fc9de02f8c3dc1055a4f9b3faab509 Mon Sep 17 00:00:00 2001 From: Apisit Ritruengroj Date: Tue, 25 Nov 2025 14:58:37 +0700 Subject: [PATCH 22/40] chore: reorder fixtures --- catalyst-gateway/tests/api_tests/utils/signed_doc.py | 1 - 1 file changed, 1 deletion(-) diff --git a/catalyst-gateway/tests/api_tests/utils/signed_doc.py b/catalyst-gateway/tests/api_tests/utils/signed_doc.py index ce38b452683..2086748d45d 100644 --- a/catalyst-gateway/tests/api_tests/utils/signed_doc.py +++ b/catalyst-gateway/tests/api_tests/utils/signed_doc.py @@ -422,7 +422,6 @@ def __factory__( ], [{"id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x"}], ) - result = generic_doc_builder( rbac_chain_factory(), metadata, JSF(template.content).generate() ) From 31ca03f8d9570fe65342f19ffacb194cef834340 Mon Sep 17 00:00:00 2001 From: Apisit Ritruengroj Date: Tue, 25 Nov 2025 16:01:18 +0700 Subject: [PATCH 23/40] fix: imports --- .../integration/signed_doc/test_signed_doc.py | 12 +- .../tests/api_tests/utils/signed_doc.py | 251 ++++++++++-------- 2 files changed, 155 insertions(+), 108 deletions(-) diff --git a/catalyst-gateway/tests/api_tests/integration/signed_doc/test_signed_doc.py b/catalyst-gateway/tests/api_tests/integration/signed_doc/test_signed_doc.py index 70f99d64541..90be2967117 100644 --- a/catalyst-gateway/tests/api_tests/integration/signed_doc/test_signed_doc.py +++ b/catalyst-gateway/tests/api_tests/integration/signed_doc/test_signed_doc.py @@ -3,7 +3,17 @@ from api.v1 import document as document_v1 from api.v2 import document as document_v2 from utils.rbac_chain import rbac_chain_factory -from utils.signed_doc import proposal_doc_factory, ProposalParameterType +from utils.signed_doc import ( + proposal_doc_factory, + proposal_form_template_doc, + category_parameters_doc, + category_parameters_form_template_doc, + campaign_parameters_doc, + campaign_parameters_form_template_doc, + brand_parameters_doc, + brand_parameters_form_template_doc, + ProposalParameterType +) @pytest.mark.preprod_indexing diff --git a/catalyst-gateway/tests/api_tests/utils/signed_doc.py b/catalyst-gateway/tests/api_tests/utils/signed_doc.py index 2086748d45d..683fc866443 100644 --- a/catalyst-gateway/tests/api_tests/utils/signed_doc.py +++ b/catalyst-gateway/tests/api_tests/utils/signed_doc.py @@ -203,40 +203,94 @@ class ProposalParameterType(IntEnum): BRAND = 2 +# return a Proposal document which is already published to the cat-gateway and the corresponding RoleID @pytest.fixture -def brand_parameters_form_template_doc( +def proposal_doc_factory( rbac_chain_factory, -) -> SignedDocumentBase: - metadata = create_metadata( - DOC_TYPE["brand_parameters_form_template"], - None, - None, - ) - result = generic_doc_builder( - rbac_chain_factory(), - metadata, - {"type": "object"}, - ) + proposal_form_template_doc, + category_parameters_doc, + campaign_parameters_doc, + brand_parameters_doc, +): + def __factory__( + parameter_type: ProposalParameterType, + ) -> tuple[SignedDocument, RoleID]: + param: SignedDocumentBase + if parameter_type == ProposalParameterType.CATEGORY: + param = category_parameters_doc + elif parameter_type == ProposalParameterType.CAMPAIGN: + param = campaign_parameters_doc + elif parameter_type == ProposalParameterType.BRAND: + param = brand_parameters_doc + else: + raise Exception("Invalid parameter type for proposal document") + + template: SignedDocumentBase = proposal_form_template_doc + + metadata = create_metadata( + DOC_TYPE["proposal"], + [ + { + "id": template.metadata["id"], + "ver": template.metadata["ver"], + "cid": "0x", + } + ], + [ + {"id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x"}, + ], + ) + result = generic_doc_builder( + rbac_chain_factory(), + metadata, + JSF(template.content).generate(), + ) + + resp = document.put( + data=result.signed_doc, + token=result.auth_token, + ) + assert resp.status_code == 201, ( + f"Failed to publish document: {resp.status_code} - {resp.text}" + ) + + return result.doc_builder, result.role_id + + return __factory__ + + +@pytest.fixture +def proposal_form_template_doc(rbac_chain_factory) -> SignedDocumentBase: + role_id = RoleID.PROPOSER + with open("./test_data/signed_docs/proposal_form_template.json", "r") as json_file: + metadata, content = json.load(json_file) + + doc_builder = SignedDocument(metadata, content) + rbac_chain = rbac_chain_factory() + (cat_id, sk_hex) = rbac_chain.cat_id_for_role(role_id) resp = document.put( - data=result.signed_doc, - token=result.auth_token, + data=doc_builder.build_and_sign(cat_id, sk_hex), + token=rbac_chain.auth_token(), ) assert resp.status_code == 201, ( f"Failed to publish document: {resp.status_code} - {resp.text}" ) - return SignedDocumentBase(result.doc_builder.metadata, result.doc_builder.content) + return SignedDocumentBase(metadata, content) @pytest.fixture -def brand_parameters_doc( - rbac_chain_factory, brand_parameters_form_template_doc +def category_parameters_doc( + rbac_chain_factory, + category_parameters_form_template_doc, + campaign_parameters_doc, ) -> SignedDocumentBase: - template: SignedDocumentBase = brand_parameters_form_template_doc + template: SignedDocumentBase = category_parameters_form_template_doc + param: SignedDocumentBase = campaign_parameters_doc metadata = create_metadata( - DOC_TYPE["brand_parameters"], + DOC_TYPE["category_parameters"], [ { "id": template.metadata["id"], @@ -244,7 +298,9 @@ def brand_parameters_doc( "cid": "0x", } ], - None, + [ + {"id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x"}, + ], ) result = generic_doc_builder( rbac_chain_factory(), @@ -256,19 +312,21 @@ def brand_parameters_doc( data=result.signed_doc, token=result.auth_token, ) - assert resp.status_code == 201 + assert resp.status_code == 201, ( + f"Failed to publish document: {resp.status_code} - {resp.text}" + ) return SignedDocumentBase(result.doc_builder.metadata, result.doc_builder.content) @pytest.fixture -def campaign_parameters_form_template_doc( - rbac_chain_factory, brand_parameters_doc +def category_parameters_form_template_doc( + rbac_chain_factory, campaign_parameters_doc ) -> SignedDocumentBase: - param: SignedDocumentBase = brand_parameters_doc + param: SignedDocumentBase = campaign_parameters_doc metadata = create_metadata( - DOC_TYPE["campaign_parameters_form_template"], + DOC_TYPE["category_parameters_form_template"], None, [{"id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x"}], ) @@ -282,7 +340,9 @@ def campaign_parameters_form_template_doc( data=result.signed_doc, token=result.auth_token, ) - assert resp.status_code == 201 + assert resp.status_code == 201, ( + f"Failed to publish document: {resp.status_code} - {resp.text}" + ) return SignedDocumentBase(result.doc_builder.metadata, result.doc_builder.content) @@ -305,133 +365,110 @@ def campaign_parameters_doc( "cid": "0x", } ], - [{"id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x"}], + [ + {"id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x"}, + ], ) result = generic_doc_builder( - rbac_chain_factory(), metadata, JSF(template.content).generate() + rbac_chain_factory(), + metadata, + JSF(template.content).generate(), ) resp = document.put( data=result.signed_doc, token=result.auth_token, ) - assert resp.status_code == 201 + assert resp.status_code == 201, ( + f"Failed to publish document: {resp.status_code} - {resp.text}" + ) return SignedDocumentBase(result.doc_builder.metadata, result.doc_builder.content) @pytest.fixture -def category_parameters_form_template_doc( - rbac_chain_factory, campaign_parameters_doc +def campaign_parameters_form_template_doc( + rbac_chain_factory, brand_parameters_doc ) -> SignedDocumentBase: - param: SignedDocumentBase = campaign_parameters_doc + param: SignedDocumentBase = brand_parameters_doc metadata = create_metadata( - DOC_TYPE["category_parameters_form_template"], + DOC_TYPE["campaign_parameters_form_template"], None, [{"id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x"}], ) - result = generic_doc_builder(rbac_chain_factory(), metadata, {"type": "object"}) + result = generic_doc_builder( + rbac_chain_factory(), + metadata, + {"type": "object"}, + ) resp = document.put( data=result.signed_doc, token=result.auth_token, ) - assert resp.status_code == 201 + assert resp.status_code == 201, ( + f"Failed to publish document: {resp.status_code} - {resp.text}" + ) return SignedDocumentBase(result.doc_builder.metadata, result.doc_builder.content) @pytest.fixture -def category_parameters_doc( - rbac_chain_factory, - category_parameters_form_template_doc, - campaign_parameters_doc, +def brand_parameters_doc( + rbac_chain_factory, brand_parameters_form_template_doc ) -> SignedDocumentBase: - template = category_parameters_form_template_doc - param = campaign_parameters_doc + template: SignedDocumentBase = brand_parameters_form_template_doc metadata = create_metadata( - DOC_TYPE["category_parameters"], - [{"id": template.metadata["id"], "ver": template.metadata["ver"], "cid": "0x"}], - [{"id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x"}], + DOC_TYPE["brand_parameters"], + [ + { + "id": template.metadata["id"], + "ver": template.metadata["ver"], + "cid": "0x", + } + ], + None, ) result = generic_doc_builder( - rbac_chain_factory(), metadata, JSF(template.content).generate() + rbac_chain_factory(), + metadata, + JSF(template.content).generate(), ) resp = document.put( data=result.signed_doc, token=result.auth_token, ) - assert resp.status_code == 201 + assert resp.status_code == 201, ( + f"Failed to publish document: {resp.status_code} - {resp.text}" + ) return SignedDocumentBase(result.doc_builder.metadata, result.doc_builder.content) @pytest.fixture -def proposal_form_template_doc(rbac_chain_factory) -> SignedDocumentBase: - role_id = RoleID.PROPOSER - with open("./test_data/signed_docs/proposal_form_template.json", "r") as json_file: - metadata, content = json.load(json_file) - - doc_builder = SignedDocument(metadata, content) - rbac_chain = rbac_chain_factory() - (cat_id, sk_hex) = rbac_chain.cat_id_for_role(role_id) +def brand_parameters_form_template_doc( + rbac_chain_factory, +) -> SignedDocumentBase: + metadata = create_metadata( + DOC_TYPE["brand_parameters_form_template"], + None, + None, + ) + result = generic_doc_builder( + rbac_chain_factory(), + metadata, + {"type": "object"}, + ) resp = document.put( - data=doc_builder.build_and_sign(cat_id, sk_hex), - token=rbac_chain.auth_token(), + data=result.signed_doc, + token=result.auth_token, + ) + assert resp.status_code == 201, ( + f"Failed to publish document: {resp.status_code} - {resp.text}" ) - assert resp.status_code == 201 - - return SignedDocumentBase(metadata, content) - - -# return a Proposal document which is already published to the cat-gateway and the corresponding RoleID -@pytest.fixture -def proposal_doc_factory( - rbac_chain_factory, - proposal_form_template_doc, - category_parameters_doc, - campaign_parameters_doc, - brand_parameters_doc, -): - def __factory__( - parameter_type: ProposalParameterType, - ) -> tuple[SignedDocument, RoleID]: - if parameter_type == ProposalParameterType.CATEGORY: - param = category_parameters_doc - elif parameter_type == ProposalParameterType.CAMPAIGN: - param = campaign_parameters_doc - elif parameter_type == ProposalParameterType.BRAND: - param = brand_parameters_doc - else: - raise Exception("Invalid parameter type for proposal document") - - template = proposal_form_template_doc - - metadata = create_metadata( - DOC_TYPE["proposal"], - [ - { - "id": template.metadata["id"], - "ver": template.metadata["ver"], - "cid": "0x", - } - ], - [{"id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x"}], - ) - result = generic_doc_builder( - rbac_chain_factory(), metadata, JSF(template.content).generate() - ) - - resp = document.put( - data=result.signed_doc, - token=result.auth_token, - ) - assert resp.status_code == 201 - - return result.doc_builder, result.role_id - return __factory__ + return SignedDocumentBase(result.doc_builder.metadata, result.doc_builder.content) From d97ef57227122906bcf19592a944b52e2c57c808 Mon Sep 17 00:00:00 2001 From: Apisit Ritruengroj Date: Tue, 25 Nov 2025 17:20:14 +0700 Subject: [PATCH 24/40] fix: proposal form template metadata --- .../integration/signed_doc/test_signed_doc.py | 2 +- .../tests/api_tests/utils/signed_doc.py | 67 ++++++++++++++----- 2 files changed, 51 insertions(+), 18 deletions(-) diff --git a/catalyst-gateway/tests/api_tests/integration/signed_doc/test_signed_doc.py b/catalyst-gateway/tests/api_tests/integration/signed_doc/test_signed_doc.py index 90be2967117..749cd59af55 100644 --- a/catalyst-gateway/tests/api_tests/integration/signed_doc/test_signed_doc.py +++ b/catalyst-gateway/tests/api_tests/integration/signed_doc/test_signed_doc.py @@ -5,7 +5,7 @@ from utils.rbac_chain import rbac_chain_factory from utils.signed_doc import ( proposal_doc_factory, - proposal_form_template_doc, + proposal_form_template_doc_factory, category_parameters_doc, category_parameters_form_template_doc, campaign_parameters_doc, diff --git a/catalyst-gateway/tests/api_tests/utils/signed_doc.py b/catalyst-gateway/tests/api_tests/utils/signed_doc.py index 683fc866443..8c97cfe5661 100644 --- a/catalyst-gateway/tests/api_tests/utils/signed_doc.py +++ b/catalyst-gateway/tests/api_tests/utils/signed_doc.py @@ -207,7 +207,7 @@ class ProposalParameterType(IntEnum): @pytest.fixture def proposal_doc_factory( rbac_chain_factory, - proposal_form_template_doc, + proposal_form_template_doc_factory, category_parameters_doc, campaign_parameters_doc, brand_parameters_doc, @@ -225,7 +225,9 @@ def __factory__( else: raise Exception("Invalid parameter type for proposal document") - template: SignedDocumentBase = proposal_form_template_doc + template: SignedDocumentBase = proposal_form_template_doc_factory( + parameter_type + ) metadata = create_metadata( DOC_TYPE["proposal"], @@ -260,24 +262,55 @@ def __factory__( @pytest.fixture -def proposal_form_template_doc(rbac_chain_factory) -> SignedDocumentBase: - role_id = RoleID.PROPOSER - with open("./test_data/signed_docs/proposal_form_template.json", "r") as json_file: - metadata, content = json.load(json_file) +def proposal_form_template_doc_factory( + rbac_chain_factory, + category_parameters_doc, + campaign_parameters_doc, + brand_parameters_doc, +): + def __factory__(parameter_type: ProposalParameterType): + param: SignedDocumentBase + if parameter_type == ProposalParameterType.CATEGORY: + param = category_parameters_doc + elif parameter_type == ProposalParameterType.CAMPAIGN: + param = campaign_parameters_doc + elif parameter_type == ProposalParameterType.BRAND: + param = brand_parameters_doc + else: + raise Exception( + "Invalid parameter type for proposal form template document" + ) - doc_builder = SignedDocument(metadata, content) - rbac_chain = rbac_chain_factory() - (cat_id, sk_hex) = rbac_chain.cat_id_for_role(role_id) + metadata = create_metadata( + DOC_TYPE["proposal_form_template"], + None, + [ + {"id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x"}, + ], + ) + with open( + "./test_data/signed_docs/proposal_form_template.json", "r" + ) as json_file: + _, content = json.load(json_file) + result = generic_doc_builder( + rbac_chain_factory(), + metadata, + content, + ) - resp = document.put( - data=doc_builder.build_and_sign(cat_id, sk_hex), - token=rbac_chain.auth_token(), - ) - assert resp.status_code == 201, ( - f"Failed to publish document: {resp.status_code} - {resp.text}" - ) + resp = document.put( + data=result.signed_doc, + token=result.auth_token, + ) + assert resp.status_code == 201, ( + f"Failed to publish document: {resp.status_code} - {resp.text}" + ) - return SignedDocumentBase(metadata, content) + return SignedDocumentBase( + result.doc_builder.metadata, result.doc_builder.content + ) + + return __factory__ @pytest.fixture From ed546e3069c6739f09bb0a33bcb20879513195ef Mon Sep 17 00:00:00 2001 From: Apisit Ritruengroj Date: Tue, 25 Nov 2025 17:25:59 +0700 Subject: [PATCH 25/40] fix: proposal form template id, ver, and params --- catalyst-gateway/tests/api_tests/utils/signed_doc.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/catalyst-gateway/tests/api_tests/utils/signed_doc.py b/catalyst-gateway/tests/api_tests/utils/signed_doc.py index 8c97cfe5661..2411058d6c8 100644 --- a/catalyst-gateway/tests/api_tests/utils/signed_doc.py +++ b/catalyst-gateway/tests/api_tests/utils/signed_doc.py @@ -281,17 +281,23 @@ def __factory__(parameter_type: ProposalParameterType): "Invalid parameter type for proposal form template document" ) - metadata = create_metadata( + tmp_metadata = create_metadata( DOC_TYPE["proposal_form_template"], None, [ {"id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x"}, ], ) + with open( "./test_data/signed_docs/proposal_form_template.json", "r" ) as json_file: - _, content = json.load(json_file) + metadata, content = json.load(json_file) + + metadata["id"] = tmp_metadata["id"] + metadata["ver"] = tmp_metadata["ver"] + metadata["templates"] = tmp_metadata["templates"] + result = generic_doc_builder( rbac_chain_factory(), metadata, From a74331ad7e59a84c124b76f07d128b4908f3888d Mon Sep 17 00:00:00 2001 From: Apisit Ritruengroj Date: Tue, 25 Nov 2025 17:26:57 +0700 Subject: [PATCH 26/40] fix: proposal form template id, ver, and params --- catalyst-gateway/tests/api_tests/utils/signed_doc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/catalyst-gateway/tests/api_tests/utils/signed_doc.py b/catalyst-gateway/tests/api_tests/utils/signed_doc.py index 2411058d6c8..5b2b185c451 100644 --- a/catalyst-gateway/tests/api_tests/utils/signed_doc.py +++ b/catalyst-gateway/tests/api_tests/utils/signed_doc.py @@ -296,7 +296,7 @@ def __factory__(parameter_type: ProposalParameterType): metadata["id"] = tmp_metadata["id"] metadata["ver"] = tmp_metadata["ver"] - metadata["templates"] = tmp_metadata["templates"] + metadata["parameters"] = tmp_metadata["parameters"] result = generic_doc_builder( rbac_chain_factory(), From 4a783800a687c64e3b459c7feb469f17cdec49bd Mon Sep 17 00:00:00 2001 From: Apisit Ritruengroj Date: Tue, 25 Nov 2025 20:48:27 +0700 Subject: [PATCH 27/40] feat: admin key setup initial --- catalyst-gateway/docker-compose.yml | 1 + .../tests/api_tests/utils/admin.py | 30 +++++++++++++++++++ .../tests/api_tests/utils/signed_doc.py | 13 +++++--- catalyst-gateway/tests/docker-compose.yml | 1 + 4 files changed, 41 insertions(+), 4 deletions(-) create mode 100644 catalyst-gateway/tests/api_tests/utils/admin.py diff --git a/catalyst-gateway/docker-compose.yml b/catalyst-gateway/docker-compose.yml index db30388135d..0c5b1ba808f 100644 --- a/catalyst-gateway/docker-compose.yml +++ b/catalyst-gateway/docker-compose.yml @@ -57,6 +57,7 @@ services: - CHAIN_NETWORK=Preprod - SIGNED_DOC_SK=0x6455585b5dcc565c8975bc136e215d6d4dd96540620f37783c564da3cb3686dd - INTERNAL_API_KEY=123 + - SIGNED_DOC_ADMIN_KEYS=admin.catalyst://preview.cardano/0KGVpkXVe5-h1RfIb08Mnc-xMZTzSX5VVnPDaHvhrqQ/0/10 # - LOG_LEVEL=error # - RUST_LOG="error,cat_gateway=debug,cardano_chain_follower=debug,mithril-client=debug" ports: diff --git a/catalyst-gateway/tests/api_tests/utils/admin.py b/catalyst-gateway/tests/api_tests/utils/admin.py new file mode 100644 index 00000000000..864772c30e1 --- /dev/null +++ b/catalyst-gateway/tests/api_tests/utils/admin.py @@ -0,0 +1,30 @@ +import pytest +import base64 +import json +from pycardano.crypto.bip32 import BIP32ED25519PrivateKey, BIP32ED25519PublicKey + + +class AdminKey: + sk_key: BIP32ED25519PrivateKey + + def __init__(self, hex_sk: str): + self.sk = BIP32ED25519PrivateKey(bytes.fromhex(hex_sk), bytes()) + + def pk(self) -> BIP32ED25519PublicKey: + return BIP32ED25519PublicKey.from_private_key(self.sk) + + def base64url_pk(self) -> str: + return ( + base64.urlsafe_b64encode(self.pk().public_key).rstrip(b"=").decode("ascii") + ) + + def cat_id(self) -> str: + return f"admin.catalyst://preview.cardano/{self.base64url_pk()}/0/10" + + +@pytest.fixture +def admin_cat_id() -> AdminKey: + with open("./test_data/rbac_regs/only_role_0.jsonc", "r") as json_file: + keys = json.load(json_file) + + return AdminKey(keys["0"][0]["sk"]) diff --git a/catalyst-gateway/tests/api_tests/utils/signed_doc.py b/catalyst-gateway/tests/api_tests/utils/signed_doc.py index 5b2b185c451..4781836d344 100644 --- a/catalyst-gateway/tests/api_tests/utils/signed_doc.py +++ b/catalyst-gateway/tests/api_tests/utils/signed_doc.py @@ -8,6 +8,7 @@ from api.v1 import document from utils import signed_doc, uuid_v7 from utils.rbac_chain import rbac_chain_factory, RBACChain, RoleID +from utils.admin import AdminKey, admin_cat_id from tempfile import NamedTemporaryFile from jsf import JSF from enum import IntEnum @@ -207,6 +208,7 @@ class ProposalParameterType(IntEnum): @pytest.fixture def proposal_doc_factory( rbac_chain_factory, + admin_cat_id, proposal_form_template_doc_factory, category_parameters_doc, campaign_parameters_doc, @@ -264,6 +266,7 @@ def __factory__( @pytest.fixture def proposal_form_template_doc_factory( rbac_chain_factory, + admin_cat_id, category_parameters_doc, campaign_parameters_doc, brand_parameters_doc, @@ -322,6 +325,7 @@ def __factory__(parameter_type: ProposalParameterType): @pytest.fixture def category_parameters_doc( rbac_chain_factory, + admin_cat_id, category_parameters_form_template_doc, campaign_parameters_doc, ) -> SignedDocumentBase: @@ -360,7 +364,7 @@ def category_parameters_doc( @pytest.fixture def category_parameters_form_template_doc( - rbac_chain_factory, campaign_parameters_doc + rbac_chain_factory, admin_cat_id, campaign_parameters_doc ) -> SignedDocumentBase: param: SignedDocumentBase = campaign_parameters_doc @@ -389,6 +393,7 @@ def category_parameters_form_template_doc( @pytest.fixture def campaign_parameters_doc( rbac_chain_factory, + admin_cat_id, campaign_parameters_form_template_doc, brand_parameters_doc, ) -> SignedDocumentBase: @@ -427,7 +432,7 @@ def campaign_parameters_doc( @pytest.fixture def campaign_parameters_form_template_doc( - rbac_chain_factory, brand_parameters_doc + rbac_chain_factory, admin_cat_id, brand_parameters_doc ) -> SignedDocumentBase: param: SignedDocumentBase = brand_parameters_doc @@ -455,7 +460,7 @@ def campaign_parameters_form_template_doc( @pytest.fixture def brand_parameters_doc( - rbac_chain_factory, brand_parameters_form_template_doc + rbac_chain_factory, admin_cat_id, brand_parameters_form_template_doc ) -> SignedDocumentBase: template: SignedDocumentBase = brand_parameters_form_template_doc @@ -489,7 +494,7 @@ def brand_parameters_doc( @pytest.fixture def brand_parameters_form_template_doc( - rbac_chain_factory, + rbac_chain_factory, admin_cat_id ) -> SignedDocumentBase: metadata = create_metadata( DOC_TYPE["brand_parameters_form_template"], diff --git a/catalyst-gateway/tests/docker-compose.yml b/catalyst-gateway/tests/docker-compose.yml index 222f4849249..4a16db52ba8 100644 --- a/catalyst-gateway/tests/docker-compose.yml +++ b/catalyst-gateway/tests/docker-compose.yml @@ -103,6 +103,7 @@ services: - YES_I_REALLY_WANT_TO_PANIC=panic attack - SERVICE_LIVE_COUNTER_THRESHOLD=100 - SERVICE_LIVE_TIMEOUT_INTERVAL=30 + - SIGNED_DOC_ADMIN_KEYS=admin.catalyst://preview.cardano/0KGVpkXVe5-h1RfIb08Mnc-xMZTzSX5VVnPDaHvhrqQ/0/10 # - LOG_LEVEL=error # - RUST_LOG="error,cat_gateway=debug,cardano_chain_follower=debug,mithril-client=debug" ports: From f6c03d6819d29ff1ba64c1f15e38c3d01e2df20e Mon Sep 17 00:00:00 2001 From: Apisit Ritruengroj Date: Tue, 25 Nov 2025 22:09:20 +0700 Subject: [PATCH 28/40] feat: admin auth key --- catalyst-gateway/docker-compose.yml | 2 +- .../integration/signed_doc/test_signed_doc.py | 1 + .../tests/api_tests/utils/admin.py | 17 +++- .../tests/api_tests/utils/signed_doc.py | 79 +++++++++++++------ catalyst-gateway/tests/docker-compose.yml | 2 +- 5 files changed, 69 insertions(+), 32 deletions(-) diff --git a/catalyst-gateway/docker-compose.yml b/catalyst-gateway/docker-compose.yml index 0c5b1ba808f..ccb9bed48ab 100644 --- a/catalyst-gateway/docker-compose.yml +++ b/catalyst-gateway/docker-compose.yml @@ -57,7 +57,7 @@ services: - CHAIN_NETWORK=Preprod - SIGNED_DOC_SK=0x6455585b5dcc565c8975bc136e215d6d4dd96540620f37783c564da3cb3686dd - INTERNAL_API_KEY=123 - - SIGNED_DOC_ADMIN_KEYS=admin.catalyst://preview.cardano/0KGVpkXVe5-h1RfIb08Mnc-xMZTzSX5VVnPDaHvhrqQ/0/10 + - SIGNED_DOC_ADMIN_KEYS=admin.catalyst://preprod.cardano/0KGVpkXVe5-h1RfIb08Mnc-xMZTzSX5VVnPDaHvhrqQ/0/10 # - LOG_LEVEL=error # - RUST_LOG="error,cat_gateway=debug,cardano_chain_follower=debug,mithril-client=debug" ports: diff --git a/catalyst-gateway/tests/api_tests/integration/signed_doc/test_signed_doc.py b/catalyst-gateway/tests/api_tests/integration/signed_doc/test_signed_doc.py index 749cd59af55..3dfc3aefc99 100644 --- a/catalyst-gateway/tests/api_tests/integration/signed_doc/test_signed_doc.py +++ b/catalyst-gateway/tests/api_tests/integration/signed_doc/test_signed_doc.py @@ -3,6 +3,7 @@ from api.v1 import document as document_v1 from api.v2 import document as document_v2 from utils.rbac_chain import rbac_chain_factory +from utils.admin import admin_key from utils.signed_doc import ( proposal_doc_factory, proposal_form_template_doc_factory, diff --git a/catalyst-gateway/tests/api_tests/utils/admin.py b/catalyst-gateway/tests/api_tests/utils/admin.py index 864772c30e1..5968a16feb5 100644 --- a/catalyst-gateway/tests/api_tests/utils/admin.py +++ b/catalyst-gateway/tests/api_tests/utils/admin.py @@ -5,25 +5,34 @@ class AdminKey: + sk_hex: str sk_key: BIP32ED25519PrivateKey - def __init__(self, hex_sk: str): - self.sk = BIP32ED25519PrivateKey(bytes.fromhex(hex_sk), bytes()) + def __init__(self, sk_hex: str): + self.sk_hex = sk_hex + + sk = bytes.fromhex(sk_hex)[:64] + chain_code = bytes.fromhex(sk_hex)[64:] + + self.sk = BIP32ED25519PrivateKey(sk, chain_code) def pk(self) -> BIP32ED25519PublicKey: return BIP32ED25519PublicKey.from_private_key(self.sk) + def pk_hex(self) -> str: + return self.pk().public_key.hex() + def base64url_pk(self) -> str: return ( base64.urlsafe_b64encode(self.pk().public_key).rstrip(b"=").decode("ascii") ) def cat_id(self) -> str: - return f"admin.catalyst://preview.cardano/{self.base64url_pk()}/0/10" + return f"admin.catalyst://preprod.cardano/{self.base64url_pk()}/0/10" @pytest.fixture -def admin_cat_id() -> AdminKey: +def admin_key() -> AdminKey: with open("./test_data/rbac_regs/only_role_0.jsonc", "r") as json_file: keys = json.load(json_file) diff --git a/catalyst-gateway/tests/api_tests/utils/signed_doc.py b/catalyst-gateway/tests/api_tests/utils/signed_doc.py index 4781836d344..f46e2e5c0a2 100644 --- a/catalyst-gateway/tests/api_tests/utils/signed_doc.py +++ b/catalyst-gateway/tests/api_tests/utils/signed_doc.py @@ -7,8 +7,13 @@ import json from api.v1 import document from utils import signed_doc, uuid_v7 -from utils.rbac_chain import rbac_chain_factory, RBACChain, RoleID -from utils.admin import AdminKey, admin_cat_id +from utils.rbac_chain import ( + rbac_chain_factory, + RBACChain, + RoleID, + generate_rbac_auth_token, +) +from utils.admin import AdminKey, admin_key from tempfile import NamedTemporaryFile from jsf import JSF from enum import IntEnum @@ -127,7 +132,7 @@ def create_metadata( return metadata -def generic_doc_builder( +def generic_doc_builder_with_rbac( rbac_chain: RBACChain, metadata: dict[str, Any], content: Any, @@ -146,6 +151,29 @@ def generic_doc_builder( ) +def generic_doc_builder_with_admin_key( + admin_key: AdminKey, + metadata: dict[str, Any], + content: Any, +) -> DocBuilderReturns: + role_id = RoleID.ROLE_0 + + doc_builder = SignedDocument(metadata, content) + cat_id = admin_key.cat_id() + sk_hex = admin_key.sk_hex + auth_token = generate_rbac_auth_token( + network="cardano", subnet="preprod", pk_hex=admin_key.pk_hex(), sk_hex=sk_hex + ) + + return DocBuilderReturns( + doc_builder=doc_builder, + signed_doc=doc_builder.build_and_sign(cat_id, sk_hex), + auth_token=auth_token, + cat_id=cat_id, + role_id=role_id, + ) + + def build_signed_doc( metadata_json: Dict[str, Any], doc_content_json: Dict[str, Any], @@ -208,7 +236,6 @@ class ProposalParameterType(IntEnum): @pytest.fixture def proposal_doc_factory( rbac_chain_factory, - admin_cat_id, proposal_form_template_doc_factory, category_parameters_doc, campaign_parameters_doc, @@ -244,7 +271,7 @@ def __factory__( {"id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x"}, ], ) - result = generic_doc_builder( + result = generic_doc_builder_with_rbac( rbac_chain_factory(), metadata, JSF(template.content).generate(), @@ -266,7 +293,7 @@ def __factory__( @pytest.fixture def proposal_form_template_doc_factory( rbac_chain_factory, - admin_cat_id, + admin_key, category_parameters_doc, campaign_parameters_doc, brand_parameters_doc, @@ -301,8 +328,8 @@ def __factory__(parameter_type: ProposalParameterType): metadata["ver"] = tmp_metadata["ver"] metadata["parameters"] = tmp_metadata["parameters"] - result = generic_doc_builder( - rbac_chain_factory(), + result = generic_doc_builder_with_admin_key( + admin_key, metadata, content, ) @@ -325,7 +352,7 @@ def __factory__(parameter_type: ProposalParameterType): @pytest.fixture def category_parameters_doc( rbac_chain_factory, - admin_cat_id, + admin_key, category_parameters_form_template_doc, campaign_parameters_doc, ) -> SignedDocumentBase: @@ -345,8 +372,8 @@ def category_parameters_doc( {"id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x"}, ], ) - result = generic_doc_builder( - rbac_chain_factory(), + result = generic_doc_builder_with_admin_key( + admin_key, metadata, JSF(template.content).generate(), ) @@ -364,7 +391,7 @@ def category_parameters_doc( @pytest.fixture def category_parameters_form_template_doc( - rbac_chain_factory, admin_cat_id, campaign_parameters_doc + rbac_chain_factory, admin_key, campaign_parameters_doc ) -> SignedDocumentBase: param: SignedDocumentBase = campaign_parameters_doc @@ -373,8 +400,8 @@ def category_parameters_form_template_doc( None, [{"id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x"}], ) - result = generic_doc_builder( - rbac_chain_factory(), + result = generic_doc_builder_with_admin_key( + admin_key, metadata, {"type": "object"}, ) @@ -393,7 +420,7 @@ def category_parameters_form_template_doc( @pytest.fixture def campaign_parameters_doc( rbac_chain_factory, - admin_cat_id, + admin_key, campaign_parameters_form_template_doc, brand_parameters_doc, ) -> SignedDocumentBase: @@ -413,8 +440,8 @@ def campaign_parameters_doc( {"id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x"}, ], ) - result = generic_doc_builder( - rbac_chain_factory(), + result = generic_doc_builder_with_admin_key( + admin_key, metadata, JSF(template.content).generate(), ) @@ -432,7 +459,7 @@ def campaign_parameters_doc( @pytest.fixture def campaign_parameters_form_template_doc( - rbac_chain_factory, admin_cat_id, brand_parameters_doc + rbac_chain_factory, admin_key, brand_parameters_doc ) -> SignedDocumentBase: param: SignedDocumentBase = brand_parameters_doc @@ -441,8 +468,8 @@ def campaign_parameters_form_template_doc( None, [{"id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x"}], ) - result = generic_doc_builder( - rbac_chain_factory(), + result = generic_doc_builder_with_admin_key( + admin_key, metadata, {"type": "object"}, ) @@ -460,7 +487,7 @@ def campaign_parameters_form_template_doc( @pytest.fixture def brand_parameters_doc( - rbac_chain_factory, admin_cat_id, brand_parameters_form_template_doc + rbac_chain_factory, admin_key, brand_parameters_form_template_doc ) -> SignedDocumentBase: template: SignedDocumentBase = brand_parameters_form_template_doc @@ -475,8 +502,8 @@ def brand_parameters_doc( ], None, ) - result = generic_doc_builder( - rbac_chain_factory(), + result = generic_doc_builder_with_admin_key( + admin_key, metadata, JSF(template.content).generate(), ) @@ -494,15 +521,15 @@ def brand_parameters_doc( @pytest.fixture def brand_parameters_form_template_doc( - rbac_chain_factory, admin_cat_id + rbac_chain_factory, admin_key ) -> SignedDocumentBase: metadata = create_metadata( DOC_TYPE["brand_parameters_form_template"], None, None, ) - result = generic_doc_builder( - rbac_chain_factory(), + result = generic_doc_builder_with_admin_key( + admin_key, metadata, {"type": "object"}, ) diff --git a/catalyst-gateway/tests/docker-compose.yml b/catalyst-gateway/tests/docker-compose.yml index 4a16db52ba8..b452235e725 100644 --- a/catalyst-gateway/tests/docker-compose.yml +++ b/catalyst-gateway/tests/docker-compose.yml @@ -103,7 +103,7 @@ services: - YES_I_REALLY_WANT_TO_PANIC=panic attack - SERVICE_LIVE_COUNTER_THRESHOLD=100 - SERVICE_LIVE_TIMEOUT_INTERVAL=30 - - SIGNED_DOC_ADMIN_KEYS=admin.catalyst://preview.cardano/0KGVpkXVe5-h1RfIb08Mnc-xMZTzSX5VVnPDaHvhrqQ/0/10 + - SIGNED_DOC_ADMIN_KEYS=admin.catalyst://preprod.cardano/0KGVpkXVe5-h1RfIb08Mnc-xMZTzSX5VVnPDaHvhrqQ/0/10 # - LOG_LEVEL=error # - RUST_LOG="error,cat_gateway=debug,cardano_chain_follower=debug,mithril-client=debug" ports: From 2b54cca865c4c1ac222e5e45be9e9aa95d949265 Mon Sep 17 00:00:00 2001 From: Apisit Ritruengroj Date: Wed, 26 Nov 2025 00:44:09 +0700 Subject: [PATCH 29/40] fix: cspell --- catalyst-gateway/docker-compose.yml | 2 ++ catalyst-gateway/tests/docker-compose.yml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/catalyst-gateway/docker-compose.yml b/catalyst-gateway/docker-compose.yml index ccb9bed48ab..45c8aaf6838 100644 --- a/catalyst-gateway/docker-compose.yml +++ b/catalyst-gateway/docker-compose.yml @@ -57,7 +57,9 @@ services: - CHAIN_NETWORK=Preprod - SIGNED_DOC_SK=0x6455585b5dcc565c8975bc136e215d6d4dd96540620f37783c564da3cb3686dd - INTERNAL_API_KEY=123 + # cspell: disable - SIGNED_DOC_ADMIN_KEYS=admin.catalyst://preprod.cardano/0KGVpkXVe5-h1RfIb08Mnc-xMZTzSX5VVnPDaHvhrqQ/0/10 + # cspell: enable # - LOG_LEVEL=error # - RUST_LOG="error,cat_gateway=debug,cardano_chain_follower=debug,mithril-client=debug" ports: diff --git a/catalyst-gateway/tests/docker-compose.yml b/catalyst-gateway/tests/docker-compose.yml index b452235e725..d3308908160 100644 --- a/catalyst-gateway/tests/docker-compose.yml +++ b/catalyst-gateway/tests/docker-compose.yml @@ -103,7 +103,9 @@ services: - YES_I_REALLY_WANT_TO_PANIC=panic attack - SERVICE_LIVE_COUNTER_THRESHOLD=100 - SERVICE_LIVE_TIMEOUT_INTERVAL=30 + # cspell: disable - SIGNED_DOC_ADMIN_KEYS=admin.catalyst://preprod.cardano/0KGVpkXVe5-h1RfIb08Mnc-xMZTzSX5VVnPDaHvhrqQ/0/10 + # cspell: enable # - LOG_LEVEL=error # - RUST_LOG="error,cat_gateway=debug,cardano_chain_follower=debug,mithril-client=debug" ports: From c576aa7de4bf2ee52c6d3a2d12eec5e68a0ed781 Mon Sep 17 00:00:00 2001 From: Apisit Ritruengroj Date: Wed, 26 Nov 2025 16:49:20 +0700 Subject: [PATCH 30/40] chore: minor builder clean up --- .../tests/api_tests/utils/admin.py | 13 +- .../tests/api_tests/utils/rbac_chain.py | 26 +-- .../tests/api_tests/utils/signed_doc.py | 165 ++++++------------ 3 files changed, 74 insertions(+), 130 deletions(-) diff --git a/catalyst-gateway/tests/api_tests/utils/admin.py b/catalyst-gateway/tests/api_tests/utils/admin.py index 5968a16feb5..39fff8a4601 100644 --- a/catalyst-gateway/tests/api_tests/utils/admin.py +++ b/catalyst-gateway/tests/api_tests/utils/admin.py @@ -2,6 +2,7 @@ import base64 import json from pycardano.crypto.bip32 import BIP32ED25519PrivateKey, BIP32ED25519PublicKey +from utils.rbac_chain import generate_rbac_auth_token class AdminKey: @@ -22,13 +23,21 @@ def pk(self) -> BIP32ED25519PublicKey: def pk_hex(self) -> str: return self.pk().public_key.hex() - def base64url_pk(self) -> str: + def pk_base64url(self) -> str: return ( base64.urlsafe_b64encode(self.pk().public_key).rstrip(b"=").decode("ascii") ) def cat_id(self) -> str: - return f"admin.catalyst://preprod.cardano/{self.base64url_pk()}/0/10" + return f"admin.catalyst://preprod.cardano/{self.pk_base64url()}/0/10" + + def auth_token(self) -> str: + return generate_rbac_auth_token( + network="cardano", + subnet="preprod", + pk_hex=self.pk_hex(), + sk_hex=self.sk_hex, + ) @pytest.fixture diff --git a/catalyst-gateway/tests/api_tests/utils/rbac_chain.py b/catalyst-gateway/tests/api_tests/utils/rbac_chain.py index cc8d2a12cc7..ad7d1d69173 100644 --- a/catalyst-gateway/tests/api_tests/utils/rbac_chain.py +++ b/catalyst-gateway/tests/api_tests/utils/rbac_chain.py @@ -34,11 +34,11 @@ def __init__(self, keys_map: dict, network: str, subnet: str): def auth_token( self, - cid: str = None, - sig: str = None, - username: str = None, + cid: str | None = None, + sig: str | None = None, + username: str | None = None, is_uri: bool = False, - nonce: str = None, + nonce: str | None = None, ) -> str: role_0_arr = self.keys_map[f"{RoleID.ROLE_0}"] return generate_rbac_auth_token( @@ -105,11 +105,11 @@ def generate_cat_id( network: str, pk_hex: str, is_uri: bool = True, - subnet: str = None, - role_id: str = None, - rotation: str = None, - username: str = None, - nonce: str = None, + subnet: str | None = None, + role_id: RoleID | None = None, + rotation: str | None = None, + username: str | None = None, + nonce: str | None = None, ) -> str: pk = bytes.fromhex(pk_hex)[:32] role0_pk_b64 = base64_url(pk) @@ -150,11 +150,11 @@ def generate_rbac_auth_token( subnet: str, pk_hex: str, sk_hex: str, - cid: str = None, - sig: str = None, - username: str = None, + cid: str | None = None, + sig: str | None = None, + username: str | None = None, is_uri: bool = False, - nonce: str = None, + nonce: str | None = None, ) -> str: pk = bytes.fromhex(pk_hex)[:32] sk = bytes.fromhex(sk_hex)[:64] diff --git a/catalyst-gateway/tests/api_tests/utils/signed_doc.py b/catalyst-gateway/tests/api_tests/utils/signed_doc.py index f46e2e5c0a2..763b22aa896 100644 --- a/catalyst-gateway/tests/api_tests/utils/signed_doc.py +++ b/catalyst-gateway/tests/api_tests/utils/signed_doc.py @@ -7,12 +7,7 @@ import json from api.v1 import document from utils import signed_doc, uuid_v7 -from utils.rbac_chain import ( - rbac_chain_factory, - RBACChain, - RoleID, - generate_rbac_auth_token, -) +from utils.rbac_chain import rbac_chain_factory, RoleID from utils.admin import AdminKey, admin_key from tempfile import NamedTemporaryFile from jsf import JSF @@ -132,48 +127,6 @@ def create_metadata( return metadata -def generic_doc_builder_with_rbac( - rbac_chain: RBACChain, - metadata: dict[str, Any], - content: Any, -) -> DocBuilderReturns: - role_id = RoleID.PROPOSER - - doc_builder = SignedDocument(metadata, content) - (cat_id, sk_hex) = rbac_chain.cat_id_for_role(role_id) - - return DocBuilderReturns( - doc_builder=doc_builder, - signed_doc=doc_builder.build_and_sign(cat_id, sk_hex), - auth_token=rbac_chain.auth_token(), - cat_id=cat_id, - role_id=role_id, - ) - - -def generic_doc_builder_with_admin_key( - admin_key: AdminKey, - metadata: dict[str, Any], - content: Any, -) -> DocBuilderReturns: - role_id = RoleID.ROLE_0 - - doc_builder = SignedDocument(metadata, content) - cat_id = admin_key.cat_id() - sk_hex = admin_key.sk_hex - auth_token = generate_rbac_auth_token( - network="cardano", subnet="preprod", pk_hex=admin_key.pk_hex(), sk_hex=sk_hex - ) - - return DocBuilderReturns( - doc_builder=doc_builder, - signed_doc=doc_builder.build_and_sign(cat_id, sk_hex), - auth_token=auth_token, - cat_id=cat_id, - role_id=role_id, - ) - - def build_signed_doc( metadata_json: Dict[str, Any], doc_content_json: Dict[str, Any], @@ -271,21 +224,21 @@ def __factory__( {"id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x"}, ], ) - result = generic_doc_builder_with_rbac( - rbac_chain_factory(), - metadata, - JSF(template.content).generate(), - ) + + role_id = RoleID.PROPOSER + rbac_chain = rbac_chain_factory() + doc_builder = SignedDocument(metadata, JSF(template.content).generate()) + (cat_id, sk_hex) = rbac_chain.cat_id_for_role(role_id) resp = document.put( - data=result.signed_doc, - token=result.auth_token, + data=doc_builder.build_and_sign(cat_id, sk_hex), + token=rbac_chain.auth_token(), ) assert resp.status_code == 201, ( f"Failed to publish document: {resp.status_code} - {resp.text}" ) - return result.doc_builder, result.role_id + return doc_builder, role_id return __factory__ @@ -328,23 +281,17 @@ def __factory__(parameter_type: ProposalParameterType): metadata["ver"] = tmp_metadata["ver"] metadata["parameters"] = tmp_metadata["parameters"] - result = generic_doc_builder_with_admin_key( - admin_key, - metadata, - content, - ) - resp = document.put( - data=result.signed_doc, - token=result.auth_token, + data=SignedDocument(metadata, content).build_and_sign( + admin_key.cat_id(), admin_key.sk_hex + ), + token=admin_key.auth_token(), ) assert resp.status_code == 201, ( f"Failed to publish document: {resp.status_code} - {resp.text}" ) - return SignedDocumentBase( - result.doc_builder.metadata, result.doc_builder.content - ) + return SignedDocumentBase(metadata, content) return __factory__ @@ -372,21 +319,19 @@ def category_parameters_doc( {"id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x"}, ], ) - result = generic_doc_builder_with_admin_key( - admin_key, - metadata, - JSF(template.content).generate(), - ) + content = JSF(template.content).generate() resp = document.put( - data=result.signed_doc, - token=result.auth_token, + data=SignedDocument(metadata, content).build_and_sign( + admin_key.cat_id(), admin_key.sk_hex + ), + token=admin_key.auth_token(), ) assert resp.status_code == 201, ( f"Failed to publish document: {resp.status_code} - {resp.text}" ) - return SignedDocumentBase(result.doc_builder.metadata, result.doc_builder.content) + return SignedDocumentBase(metadata, content) @pytest.fixture @@ -400,21 +345,19 @@ def category_parameters_form_template_doc( None, [{"id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x"}], ) - result = generic_doc_builder_with_admin_key( - admin_key, - metadata, - {"type": "object"}, - ) + content = {"type": "object"} resp = document.put( - data=result.signed_doc, - token=result.auth_token, + data=SignedDocument(metadata, content).build_and_sign( + admin_key.cat_id(), admin_key.sk_hex + ), + token=admin_key.auth_token(), ) assert resp.status_code == 201, ( f"Failed to publish document: {resp.status_code} - {resp.text}" ) - return SignedDocumentBase(result.doc_builder.metadata, result.doc_builder.content) + return SignedDocumentBase(metadata, content) @pytest.fixture @@ -440,21 +383,19 @@ def campaign_parameters_doc( {"id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x"}, ], ) - result = generic_doc_builder_with_admin_key( - admin_key, - metadata, - JSF(template.content).generate(), - ) + content = JSF(template.content).generate() resp = document.put( - data=result.signed_doc, - token=result.auth_token, + data=SignedDocument(metadata, content).build_and_sign( + admin_key.cat_id(), admin_key.sk_hex + ), + token=admin_key.auth_token(), ) assert resp.status_code == 201, ( f"Failed to publish document: {resp.status_code} - {resp.text}" ) - return SignedDocumentBase(result.doc_builder.metadata, result.doc_builder.content) + return SignedDocumentBase(metadata, content) @pytest.fixture @@ -468,21 +409,19 @@ def campaign_parameters_form_template_doc( None, [{"id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x"}], ) - result = generic_doc_builder_with_admin_key( - admin_key, - metadata, - {"type": "object"}, - ) + content = {"type": "object"} resp = document.put( - data=result.signed_doc, - token=result.auth_token, + data=SignedDocument(metadata, content).build_and_sign( + admin_key.cat_id(), admin_key.sk_hex + ), + token=admin_key.auth_token(), ) assert resp.status_code == 201, ( f"Failed to publish document: {resp.status_code} - {resp.text}" ) - return SignedDocumentBase(result.doc_builder.metadata, result.doc_builder.content) + return SignedDocumentBase(metadata, content) @pytest.fixture @@ -502,21 +441,19 @@ def brand_parameters_doc( ], None, ) - result = generic_doc_builder_with_admin_key( - admin_key, - metadata, - JSF(template.content).generate(), - ) + content = JSF(template.content).generate() resp = document.put( - data=result.signed_doc, - token=result.auth_token, + data=SignedDocument(metadata, content).build_and_sign( + admin_key.cat_id(), admin_key.sk_hex + ), + token=admin_key.auth_token(), ) assert resp.status_code == 201, ( f"Failed to publish document: {resp.status_code} - {resp.text}" ) - return SignedDocumentBase(result.doc_builder.metadata, result.doc_builder.content) + return SignedDocumentBase(metadata, content) @pytest.fixture @@ -528,18 +465,16 @@ def brand_parameters_form_template_doc( None, None, ) - result = generic_doc_builder_with_admin_key( - admin_key, - metadata, - {"type": "object"}, - ) + content = {"type": "object"} resp = document.put( - data=result.signed_doc, - token=result.auth_token, + data=SignedDocument(metadata, content).build_and_sign( + admin_key.cat_id(), admin_key.sk_hex + ), + token=admin_key.auth_token(), ) assert resp.status_code == 201, ( f"Failed to publish document: {resp.status_code} - {resp.text}" ) - return SignedDocumentBase(result.doc_builder.metadata, result.doc_builder.content) + return SignedDocumentBase(metadata, content) From 26d4d03e93ca0f08e4f347a41c13cc5f3cb9e530 Mon Sep 17 00:00:00 2001 From: Apisit Ritruengroj Date: Wed, 26 Nov 2025 17:01:35 +0700 Subject: [PATCH 31/40] chore: minor format --- .../tests/api_tests/utils/signed_doc.py | 56 +++++++++---------- 1 file changed, 25 insertions(+), 31 deletions(-) diff --git a/catalyst-gateway/tests/api_tests/utils/signed_doc.py b/catalyst-gateway/tests/api_tests/utils/signed_doc.py index 763b22aa896..fdc9844b34f 100644 --- a/catalyst-gateway/tests/api_tests/utils/signed_doc.py +++ b/catalyst-gateway/tests/api_tests/utils/signed_doc.py @@ -227,18 +227,18 @@ def __factory__( role_id = RoleID.PROPOSER rbac_chain = rbac_chain_factory() - doc_builder = SignedDocument(metadata, JSF(template.content).generate()) + doc = SignedDocument(metadata, JSF(template.content).generate()) (cat_id, sk_hex) = rbac_chain.cat_id_for_role(role_id) resp = document.put( - data=doc_builder.build_and_sign(cat_id, sk_hex), + data=doc.build_and_sign(cat_id, sk_hex), token=rbac_chain.auth_token(), ) assert resp.status_code == 201, ( f"Failed to publish document: {resp.status_code} - {resp.text}" ) - return doc_builder, role_id + return doc, role_id return __factory__ @@ -281,17 +281,17 @@ def __factory__(parameter_type: ProposalParameterType): metadata["ver"] = tmp_metadata["ver"] metadata["parameters"] = tmp_metadata["parameters"] + doc = SignedDocument(metadata, content) + resp = document.put( - data=SignedDocument(metadata, content).build_and_sign( - admin_key.cat_id(), admin_key.sk_hex - ), + data=doc.build_and_sign(admin_key.cat_id(), admin_key.sk_hex), token=admin_key.auth_token(), ) assert resp.status_code == 201, ( f"Failed to publish document: {resp.status_code} - {resp.text}" ) - return SignedDocumentBase(metadata, content) + return doc return __factory__ @@ -320,18 +320,17 @@ def category_parameters_doc( ], ) content = JSF(template.content).generate() + doc = SignedDocument(metadata, content) resp = document.put( - data=SignedDocument(metadata, content).build_and_sign( - admin_key.cat_id(), admin_key.sk_hex - ), + data=doc.build_and_sign(admin_key.cat_id(), admin_key.sk_hex), token=admin_key.auth_token(), ) assert resp.status_code == 201, ( f"Failed to publish document: {resp.status_code} - {resp.text}" ) - return SignedDocumentBase(metadata, content) + return doc @pytest.fixture @@ -346,18 +345,17 @@ def category_parameters_form_template_doc( [{"id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x"}], ) content = {"type": "object"} + doc = SignedDocument(metadata, content) resp = document.put( - data=SignedDocument(metadata, content).build_and_sign( - admin_key.cat_id(), admin_key.sk_hex - ), + data=doc.build_and_sign(admin_key.cat_id(), admin_key.sk_hex), token=admin_key.auth_token(), ) assert resp.status_code == 201, ( f"Failed to publish document: {resp.status_code} - {resp.text}" ) - return SignedDocumentBase(metadata, content) + return doc @pytest.fixture @@ -384,18 +382,17 @@ def campaign_parameters_doc( ], ) content = JSF(template.content).generate() + doc = SignedDocument(metadata, content) resp = document.put( - data=SignedDocument(metadata, content).build_and_sign( - admin_key.cat_id(), admin_key.sk_hex - ), + data=doc.build_and_sign(admin_key.cat_id(), admin_key.sk_hex), token=admin_key.auth_token(), ) assert resp.status_code == 201, ( f"Failed to publish document: {resp.status_code} - {resp.text}" ) - return SignedDocumentBase(metadata, content) + return doc @pytest.fixture @@ -410,18 +407,17 @@ def campaign_parameters_form_template_doc( [{"id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x"}], ) content = {"type": "object"} + doc = SignedDocument(metadata, content) resp = document.put( - data=SignedDocument(metadata, content).build_and_sign( - admin_key.cat_id(), admin_key.sk_hex - ), + data=doc.build_and_sign(admin_key.cat_id(), admin_key.sk_hex), token=admin_key.auth_token(), ) assert resp.status_code == 201, ( f"Failed to publish document: {resp.status_code} - {resp.text}" ) - return SignedDocumentBase(metadata, content) + return doc @pytest.fixture @@ -442,18 +438,17 @@ def brand_parameters_doc( None, ) content = JSF(template.content).generate() + doc = SignedDocument(metadata, content) resp = document.put( - data=SignedDocument(metadata, content).build_and_sign( - admin_key.cat_id(), admin_key.sk_hex - ), + data=doc.build_and_sign(admin_key.cat_id(), admin_key.sk_hex), token=admin_key.auth_token(), ) assert resp.status_code == 201, ( f"Failed to publish document: {resp.status_code} - {resp.text}" ) - return SignedDocumentBase(metadata, content) + return doc @pytest.fixture @@ -466,15 +461,14 @@ def brand_parameters_form_template_doc( None, ) content = {"type": "object"} + doc = SignedDocument(metadata, content) resp = document.put( - data=SignedDocument(metadata, content).build_and_sign( - admin_key.cat_id(), admin_key.sk_hex - ), + data=doc.build_and_sign(admin_key.cat_id(), admin_key.sk_hex), token=admin_key.auth_token(), ) assert resp.status_code == 201, ( f"Failed to publish document: {resp.status_code} - {resp.text}" ) - return SignedDocumentBase(metadata, content) + return doc From 56a69ec0b95a8d674161549f9c75ea5e10e2cebf Mon Sep 17 00:00:00 2001 From: Apisit Ritruengroj Date: Wed, 26 Nov 2025 17:02:35 +0700 Subject: [PATCH 32/40] chore: minor format --- catalyst-gateway/tests/api_tests/utils/signed_doc.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/catalyst-gateway/tests/api_tests/utils/signed_doc.py b/catalyst-gateway/tests/api_tests/utils/signed_doc.py index fdc9844b34f..86e8f59a873 100644 --- a/catalyst-gateway/tests/api_tests/utils/signed_doc.py +++ b/catalyst-gateway/tests/api_tests/utils/signed_doc.py @@ -224,10 +224,11 @@ def __factory__( {"id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x"}, ], ) + content = JSF(template.content).generate() role_id = RoleID.PROPOSER rbac_chain = rbac_chain_factory() - doc = SignedDocument(metadata, JSF(template.content).generate()) + doc = SignedDocument(metadata, content) (cat_id, sk_hex) = rbac_chain.cat_id_for_role(role_id) resp = document.put( From fcb222f2fdb0f08e2bafa842d8ba3edee4bfb7c3 Mon Sep 17 00:00:00 2001 From: Apisit Ritruengroj Date: Wed, 26 Nov 2025 17:05:53 +0700 Subject: [PATCH 33/40] chore: role id as param --- .../api_tests/integration/signed_doc/test_signed_doc.py | 8 +++++--- catalyst-gateway/tests/api_tests/utils/signed_doc.py | 6 +++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/catalyst-gateway/tests/api_tests/integration/signed_doc/test_signed_doc.py b/catalyst-gateway/tests/api_tests/integration/signed_doc/test_signed_doc.py index 3dfc3aefc99..3c43c94c252 100644 --- a/catalyst-gateway/tests/api_tests/integration/signed_doc/test_signed_doc.py +++ b/catalyst-gateway/tests/api_tests/integration/signed_doc/test_signed_doc.py @@ -2,7 +2,7 @@ from utils import uuid_v7 from api.v1 import document as document_v1 from api.v2 import document as document_v2 -from utils.rbac_chain import rbac_chain_factory +from utils.rbac_chain import rbac_chain_factory, RoleID from utils.admin import admin_key from utils.signed_doc import ( proposal_doc_factory, @@ -20,7 +20,8 @@ @pytest.mark.preprod_indexing def test_document_put_and_get_endpoints(proposal_doc_factory, rbac_chain_factory): rbac_chain = rbac_chain_factory() - (proposal_doc, role_id) = proposal_doc_factory(ProposalParameterType.CATEGORY) + role_id = RoleID.PROPOSER + proposal_doc = proposal_doc_factory(ProposalParameterType.CATEGORY, role_id) (cat_id, sk_hex) = rbac_chain.cat_id_for_role(role_id) proposal_doc_id = proposal_doc.metadata["id"] @@ -91,7 +92,8 @@ def test_document_index_endpoint( proposal_doc_factory, rbac_chain_factory, ): - (doc, role_id) = proposal_doc_factory(ProposalParameterType.CATEGORY) + role_id = RoleID.PROPOSER + doc = proposal_doc_factory(ProposalParameterType.CATEGORY, role_id) rbac_chain = rbac_chain_factory() (cat_id, sk_hex) = rbac_chain.cat_id_for_role(role_id) diff --git a/catalyst-gateway/tests/api_tests/utils/signed_doc.py b/catalyst-gateway/tests/api_tests/utils/signed_doc.py index 86e8f59a873..f0bf38996fa 100644 --- a/catalyst-gateway/tests/api_tests/utils/signed_doc.py +++ b/catalyst-gateway/tests/api_tests/utils/signed_doc.py @@ -196,7 +196,8 @@ def proposal_doc_factory( ): def __factory__( parameter_type: ProposalParameterType, - ) -> tuple[SignedDocument, RoleID]: + role_id: RoleID + ) -> SignedDocument: param: SignedDocumentBase if parameter_type == ProposalParameterType.CATEGORY: param = category_parameters_doc @@ -226,7 +227,6 @@ def __factory__( ) content = JSF(template.content).generate() - role_id = RoleID.PROPOSER rbac_chain = rbac_chain_factory() doc = SignedDocument(metadata, content) (cat_id, sk_hex) = rbac_chain.cat_id_for_role(role_id) @@ -239,7 +239,7 @@ def __factory__( f"Failed to publish document: {resp.status_code} - {resp.text}" ) - return doc, role_id + return doc return __factory__ From 10db12af2f743818698096313b9ae83cc8763f9d Mon Sep 17 00:00:00 2001 From: Apisit Ritruengroj Date: Wed, 26 Nov 2025 20:33:44 +0700 Subject: [PATCH 34/40] chore: minor admin --- catalyst-gateway/tests/api_tests/utils/admin.py | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/catalyst-gateway/tests/api_tests/utils/admin.py b/catalyst-gateway/tests/api_tests/utils/admin.py index 39fff8a4601..cc73ccd512e 100644 --- a/catalyst-gateway/tests/api_tests/utils/admin.py +++ b/catalyst-gateway/tests/api_tests/utils/admin.py @@ -1,14 +1,10 @@ import pytest import base64 -import json from pycardano.crypto.bip32 import BIP32ED25519PrivateKey, BIP32ED25519PublicKey -from utils.rbac_chain import generate_rbac_auth_token +from utils.rbac_chain import generate_rbac_auth_token, ONLY_ROLE_0_REG_JSON, RoleID class AdminKey: - sk_hex: str - sk_key: BIP32ED25519PrivateKey - def __init__(self, sk_hex: str): self.sk_hex = sk_hex @@ -42,7 +38,4 @@ def auth_token(self) -> str: @pytest.fixture def admin_key() -> AdminKey: - with open("./test_data/rbac_regs/only_role_0.jsonc", "r") as json_file: - keys = json.load(json_file) - - return AdminKey(keys["0"][0]["sk"]) + return AdminKey(ONLY_ROLE_0_REG_JSON[f"{RoleID.ROLE_0}"][0]["sk"]) From 8eeaddfa8eb1c86ce4a07565407cb70e7a20575f Mon Sep 17 00:00:00 2001 From: Apisit Ritruengroj Date: Wed, 26 Nov 2025 20:47:16 +0700 Subject: [PATCH 35/40] chore: admin cat-id --- .../tests/api_tests/utils/admin.py | 20 ++++++++++++++----- .../tests/api_tests/utils/rbac_chain.py | 7 ++++--- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/catalyst-gateway/tests/api_tests/utils/admin.py b/catalyst-gateway/tests/api_tests/utils/admin.py index cc73ccd512e..da495051f24 100644 --- a/catalyst-gateway/tests/api_tests/utils/admin.py +++ b/catalyst-gateway/tests/api_tests/utils/admin.py @@ -1,7 +1,12 @@ import pytest import base64 from pycardano.crypto.bip32 import BIP32ED25519PrivateKey, BIP32ED25519PublicKey -from utils.rbac_chain import generate_rbac_auth_token, ONLY_ROLE_0_REG_JSON, RoleID +from utils.rbac_chain import ( + generate_rbac_auth_token, + ONLY_ROLE_0_REG_JSON, + RoleID, + generate_cat_id, +) class AdminKey: @@ -20,12 +25,17 @@ def pk_hex(self) -> str: return self.pk().public_key.hex() def pk_base64url(self) -> str: - return ( - base64.urlsafe_b64encode(self.pk().public_key).rstrip(b"=").decode("ascii") - ) + return base64.urlsafe_b64encode(self.pk().public_key).decode().rstrip("=") def cat_id(self) -> str: - return f"admin.catalyst://preprod.cardano/{self.pk_base64url()}/0/10" + return generate_cat_id( + network="cardano", + subnet="preprod", + pk_hex=self.pk_hex(), + scheme="admin.catalyst", + role_id=RoleID.ROLE_0, + rotation="10", + ) def auth_token(self) -> str: return generate_rbac_auth_token( diff --git a/catalyst-gateway/tests/api_tests/utils/rbac_chain.py b/catalyst-gateway/tests/api_tests/utils/rbac_chain.py index ad7d1d69173..79461493c50 100644 --- a/catalyst-gateway/tests/api_tests/utils/rbac_chain.py +++ b/catalyst-gateway/tests/api_tests/utils/rbac_chain.py @@ -104,6 +104,7 @@ def __rbac_chain_factory( def generate_cat_id( network: str, pk_hex: str, + scheme: str = "id.catalyst", is_uri: bool = True, subnet: str | None = None, role_id: RoleID | None = None, @@ -134,13 +135,13 @@ def generate_cat_id( # Path path = f"{role0_pk_b64}" - if role_id: + if role_id is not None: path += f"/{role_id}" - if rotation: + if rotation is not None: path += f"/{rotation}" if is_uri: - return f"id.catalyst://{authority}/{path}" + return f"{scheme}://{authority}/{path}" else: return f"{authority}/{path}" From 006badd7de319f4931744448188781a6ed9ea038 Mon Sep 17 00:00:00 2001 From: Alex Pozhylenkov Date: Thu, 27 Nov 2025 18:43:24 +0700 Subject: [PATCH 36/40] test(cat-gateway): Prepare document submission in different types for integration test [CI fixes] (#3781) * rename ProposalParameterType to ParameterType * cleanup * set the 3.13 python version * fix * fix * fix * fix * fix * try * try * bump mk_signed_doc to `catalyst-signed-doc/v0.0.10` --- .../src/service/api/documents/common/mod.rs | 9 +- .../tests/api_tests/.python-version | 1 + catalyst-gateway/tests/api_tests/Earthfile | 2 +- .../integration/signed_doc/test_signed_doc.py | 9 +- catalyst-gateway/tests/api_tests/poetry.lock | 407 ++- .../tests/api_tests/pyproject.toml | 2 +- .../signed_docs/proposal_form_template.json | 2858 ++++++++--------- .../tests/api_tests/utils/admin.py | 4 +- .../tests/api_tests/utils/rbac_chain.py | 2 +- .../tests/api_tests/utils/signed_doc.py | 212 +- 10 files changed, 1739 insertions(+), 1767 deletions(-) create mode 100644 catalyst-gateway/tests/api_tests/.python-version diff --git a/catalyst-gateway/bin/src/service/api/documents/common/mod.rs b/catalyst-gateway/bin/src/service/api/documents/common/mod.rs index 1d5cc8b5e54..006e7405aac 100644 --- a/catalyst-gateway/bin/src/service/api/documents/common/mod.rs +++ b/catalyst-gateway/bin/src/service/api/documents/common/mod.rs @@ -208,12 +208,13 @@ impl VerifyingKeyProvider { token: &mut CatalystRBACTokenV1, kids: &[catalyst_signed_doc::CatalystId], ) -> anyhow::Result { - if kids.len() > 1 { - anyhow::bail!("Multi-signature document is currently unsupported"); - } + use itertools::Itertools as _; let [kid] = kids else { - anyhow::bail!("Multi-signature document is currently unsupported"); + anyhow::bail!( + "Must have only one signature. Multi-signature document is currently unsupported. kids: [{}]", + kids.iter().map(ToString::to_string).join(",") + ); }; if kid != token.catalyst_id() { diff --git a/catalyst-gateway/tests/api_tests/.python-version b/catalyst-gateway/tests/api_tests/.python-version new file mode 100644 index 00000000000..24ee5b1be99 --- /dev/null +++ b/catalyst-gateway/tests/api_tests/.python-version @@ -0,0 +1 @@ +3.13 diff --git a/catalyst-gateway/tests/api_tests/Earthfile b/catalyst-gateway/tests/api_tests/Earthfile index 48f2eb6054d..96d3123d309 100644 --- a/catalyst-gateway/tests/api_tests/Earthfile +++ b/catalyst-gateway/tests/api_tests/Earthfile @@ -1,7 +1,7 @@ VERSION 0.8 IMPORT github.com/input-output-hk/catalyst-ci/earthly/python:v3.6.3 AS python-ci -IMPORT github.com/input-output-hk/catalyst-libs/rust:catalyst-signed-doc/v0.0.8 AS cat-libs-rust +IMPORT github.com/input-output-hk/catalyst-libs/rust:catalyst-signed-doc/v0.0.10 AS cat-libs-rust IMPORT github.com/input-output-hk/catalyst-libs/rust:catalyst-signed-doc/v.0.0.4-fix-earthly-build AS dep-cat-libs-rust IMPORT github.com/input-output-hk/catalyst-storage AS cat-storage diff --git a/catalyst-gateway/tests/api_tests/integration/signed_doc/test_signed_doc.py b/catalyst-gateway/tests/api_tests/integration/signed_doc/test_signed_doc.py index 3c43c94c252..d51cc82c6c6 100644 --- a/catalyst-gateway/tests/api_tests/integration/signed_doc/test_signed_doc.py +++ b/catalyst-gateway/tests/api_tests/integration/signed_doc/test_signed_doc.py @@ -6,14 +6,13 @@ from utils.admin import admin_key from utils.signed_doc import ( proposal_doc_factory, - proposal_form_template_doc_factory, + proposal_form_template_doc, category_parameters_doc, category_parameters_form_template_doc, campaign_parameters_doc, campaign_parameters_form_template_doc, brand_parameters_doc, brand_parameters_form_template_doc, - ProposalParameterType ) @@ -21,7 +20,7 @@ def test_document_put_and_get_endpoints(proposal_doc_factory, rbac_chain_factory): rbac_chain = rbac_chain_factory() role_id = RoleID.PROPOSER - proposal_doc = proposal_doc_factory(ProposalParameterType.CATEGORY, role_id) + proposal_doc = proposal_doc_factory(role_id) (cat_id, sk_hex) = rbac_chain.cat_id_for_role(role_id) proposal_doc_id = proposal_doc.metadata["id"] @@ -65,7 +64,7 @@ def test_document_put_and_get_endpoints(proposal_doc_factory, rbac_chain_factory # Put a non valid document with same ID different content invalid_doc = proposal_doc.copy() - invalid_doc.content["setup"]["title"]["title"] = "another title" + invalid_doc.content["setup"]["title"] = {"title": "another title"} resp = document_v1.put( data=invalid_doc.build_and_sign(cat_id, sk_hex), token=rbac_chain.auth_token(), @@ -93,7 +92,7 @@ def test_document_index_endpoint( rbac_chain_factory, ): role_id = RoleID.PROPOSER - doc = proposal_doc_factory(ProposalParameterType.CATEGORY, role_id) + doc = proposal_doc_factory(role_id) rbac_chain = rbac_chain_factory() (cat_id, sk_hex) = rbac_chain.cat_id_for_role(role_id) diff --git a/catalyst-gateway/tests/api_tests/poetry.lock b/catalyst-gateway/tests/api_tests/poetry.lock index 34140f59b0e..5f81057fcf3 100644 --- a/catalyst-gateway/tests/api_tests/poetry.lock +++ b/catalyst-gateway/tests/api_tests/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.1.4 and should not be changed by hand. [[package]] name = "aioquic" @@ -129,7 +129,10 @@ files = [ ] [package.dependencies] -cffi = {version = ">=2.0.0b1", markers = "python_version >= \"3.14\""} +cffi = [ + {version = ">=1.0.1", markers = "python_version < \"3.14\""}, + {version = ">=2.0.0b1", markers = "python_version >= \"3.14\""}, +] [[package]] name = "asgiref" @@ -355,137 +358,112 @@ requests = "*" [[package]] name = "brotli" -version = "1.1.0" +version = "1.2.0" description = "Python bindings for the Brotli compression library" optional = false python-versions = "*" groups = ["dev"] files = [ - {file = "Brotli-1.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e1140c64812cb9b06c922e77f1c26a75ec5e3f0fb2bf92cc8c58720dec276752"}, - {file = "Brotli-1.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c8fd5270e906eef71d4a8d19b7c6a43760c6abcfcc10c9101d14eb2357418de9"}, - {file = "Brotli-1.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ae56aca0402a0f9a3431cddda62ad71666ca9d4dc3a10a142b9dce2e3c0cda3"}, - {file = "Brotli-1.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:43ce1b9935bfa1ede40028054d7f48b5469cd02733a365eec8a329ffd342915d"}, - {file = "Brotli-1.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:7c4855522edb2e6ae7fdb58e07c3ba9111e7621a8956f481c68d5d979c93032e"}, - {file = "Brotli-1.1.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:38025d9f30cf4634f8309c6874ef871b841eb3c347e90b0851f63d1ded5212da"}, - {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e6a904cb26bfefc2f0a6f240bdf5233be78cd2488900a2f846f3c3ac8489ab80"}, - {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a37b8f0391212d29b3a91a799c8e4a2855e0576911cdfb2515487e30e322253d"}, - {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e84799f09591700a4154154cab9787452925578841a94321d5ee8fb9a9a328f0"}, - {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f66b5337fa213f1da0d9000bc8dc0cb5b896b726eefd9c6046f699b169c41b9e"}, - {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5dab0844f2cf82be357a0eb11a9087f70c5430b2c241493fc122bb6f2bb0917c"}, - {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e4fe605b917c70283db7dfe5ada75e04561479075761a0b3866c081d035b01c1"}, - {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:1e9a65b5736232e7a7f91ff3d02277f11d339bf34099a56cdab6a8b3410a02b2"}, - {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:58d4b711689366d4a03ac7957ab8c28890415e267f9b6589969e74b6e42225ec"}, - {file = "Brotli-1.1.0-cp310-cp310-win32.whl", hash = "sha256:be36e3d172dc816333f33520154d708a2657ea63762ec16b62ece02ab5e4daf2"}, - {file = "Brotli-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:0c6244521dda65ea562d5a69b9a26120769b7a9fb3db2fe9545935ed6735b128"}, - {file = "Brotli-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a3daabb76a78f829cafc365531c972016e4aa8d5b4bf60660ad8ecee19df7ccc"}, - {file = "Brotli-1.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c8146669223164fc87a7e3de9f81e9423c67a79d6b3447994dfb9c95da16e2d6"}, - {file = "Brotli-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:30924eb4c57903d5a7526b08ef4a584acc22ab1ffa085faceb521521d2de32dd"}, - {file = "Brotli-1.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ceb64bbc6eac5a140ca649003756940f8d6a7c444a68af170b3187623b43bebf"}, - {file = "Brotli-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a469274ad18dc0e4d316eefa616d1d0c2ff9da369af19fa6f3daa4f09671fd61"}, - {file = "Brotli-1.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:524f35912131cc2cabb00edfd8d573b07f2d9f21fa824bd3fb19725a9cf06327"}, - {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5b3cc074004d968722f51e550b41a27be656ec48f8afaeeb45ebf65b561481dd"}, - {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:19c116e796420b0cee3da1ccec3b764ed2952ccfcc298b55a10e5610ad7885f9"}, - {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:510b5b1bfbe20e1a7b3baf5fed9e9451873559a976c1a78eebaa3b86c57b4265"}, - {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a1fd8a29719ccce974d523580987b7f8229aeace506952fa9ce1d53a033873c8"}, - {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c247dd99d39e0338a604f8c2b3bc7061d5c2e9e2ac7ba9cc1be5a69cb6cd832f"}, - {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1b2c248cd517c222d89e74669a4adfa5577e06ab68771a529060cf5a156e9757"}, - {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:2a24c50840d89ded6c9a8fdc7b6ed3692ed4e86f1c4a4a938e1e92def92933e0"}, - {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f31859074d57b4639318523d6ffdca586ace54271a73ad23ad021acd807eb14b"}, - {file = "Brotli-1.1.0-cp311-cp311-win32.whl", hash = "sha256:39da8adedf6942d76dc3e46653e52df937a3c4d6d18fdc94a7c29d263b1f5b50"}, - {file = "Brotli-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:aac0411d20e345dc0920bdec5548e438e999ff68d77564d5e9463a7ca9d3e7b1"}, - {file = "Brotli-1.1.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:32d95b80260d79926f5fab3c41701dbb818fde1c9da590e77e571eefd14abe28"}, - {file = "Brotli-1.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b760c65308ff1e462f65d69c12e4ae085cff3b332d894637f6273a12a482d09f"}, - {file = "Brotli-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:316cc9b17edf613ac76b1f1f305d2a748f1b976b033b049a6ecdfd5612c70409"}, - {file = "Brotli-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:caf9ee9a5775f3111642d33b86237b05808dafcd6268faa492250e9b78046eb2"}, - {file = "Brotli-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70051525001750221daa10907c77830bc889cb6d865cc0b813d9db7fefc21451"}, - {file = "Brotli-1.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7f4bf76817c14aa98cc6697ac02f3972cb8c3da93e9ef16b9c66573a68014f91"}, - {file = "Brotli-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d0c5516f0aed654134a2fc936325cc2e642f8a0e096d075209672eb321cff408"}, - {file = "Brotli-1.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c3020404e0b5eefd7c9485ccf8393cfb75ec38ce75586e046573c9dc29967a0"}, - {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4ed11165dd45ce798d99a136808a794a748d5dc38511303239d4e2363c0695dc"}, - {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4093c631e96fdd49e0377a9c167bfd75b6d0bad2ace734c6eb20b348bc3ea180"}, - {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7e4c4629ddad63006efa0ef968c8e4751c5868ff0b1c5c40f76524e894c50248"}, - {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:861bf317735688269936f755fa136a99d1ed526883859f86e41a5d43c61d8966"}, - {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:87a3044c3a35055527ac75e419dfa9f4f3667a1e887ee80360589eb8c90aabb9"}, - {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c5529b34c1c9d937168297f2c1fde7ebe9ebdd5e121297ff9c043bdb2ae3d6fb"}, - {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ca63e1890ede90b2e4454f9a65135a4d387a4585ff8282bb72964fab893f2111"}, - {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e79e6520141d792237c70bcd7a3b122d00f2613769ae0cb61c52e89fd3443839"}, - {file = "Brotli-1.1.0-cp312-cp312-win32.whl", hash = "sha256:5f4d5ea15c9382135076d2fb28dde923352fe02951e66935a9efaac8f10e81b0"}, - {file = "Brotli-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:906bc3a79de8c4ae5b86d3d75a8b77e44404b0f4261714306e3ad248d8ab0951"}, - {file = "Brotli-1.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8bf32b98b75c13ec7cf774164172683d6e7891088f6316e54425fde1efc276d5"}, - {file = "Brotli-1.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7bc37c4d6b87fb1017ea28c9508b36bbcb0c3d18b4260fcdf08b200c74a6aee8"}, - {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c0ef38c7a7014ffac184db9e04debe495d317cc9c6fb10071f7fefd93100a4f"}, - {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91d7cc2a76b5567591d12c01f019dd7afce6ba8cba6571187e21e2fc418ae648"}, - {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a93dde851926f4f2678e704fadeb39e16c35d8baebd5252c9fd94ce8ce68c4a0"}, - {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f0db75f47be8b8abc8d9e31bc7aad0547ca26f24a54e6fd10231d623f183d089"}, - {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6967ced6730aed543b8673008b5a391c3b1076d834ca438bbd70635c73775368"}, - {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7eedaa5d036d9336c95915035fb57422054014ebdeb6f3b42eac809928e40d0c"}, - {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d487f5432bf35b60ed625d7e1b448e2dc855422e87469e3f450aa5552b0eb284"}, - {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:832436e59afb93e1836081a20f324cb185836c617659b07b129141a8426973c7"}, - {file = "Brotli-1.1.0-cp313-cp313-win32.whl", hash = "sha256:43395e90523f9c23a3d5bdf004733246fba087f2948f87ab28015f12359ca6a0"}, - {file = "Brotli-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:9011560a466d2eb3f5a6e4929cf4a09be405c64154e12df0dd72713f6500e32b"}, - {file = "Brotli-1.1.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a090ca607cbb6a34b0391776f0cb48062081f5f60ddcce5d11838e67a01928d1"}, - {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2de9d02f5bda03d27ede52e8cfe7b865b066fa49258cbab568720aa5be80a47d"}, - {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2333e30a5e00fe0fe55903c8832e08ee9c3b1382aacf4db26664a16528d51b4b"}, - {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4d4a848d1837973bf0f4b5e54e3bec977d99be36a7895c61abb659301b02c112"}, - {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:fdc3ff3bfccdc6b9cc7c342c03aa2400683f0cb891d46e94b64a197910dc4064"}, - {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:5eeb539606f18a0b232d4ba45adccde4125592f3f636a6182b4a8a436548b914"}, - {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:fd5f17ff8f14003595ab414e45fce13d073e0762394f957182e69035c9f3d7c2"}, - {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:069a121ac97412d1fe506da790b3e69f52254b9df4eb665cd42460c837193354"}, - {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:e93dfc1a1165e385cc8239fab7c036fb2cd8093728cbd85097b284d7b99249a2"}, - {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_aarch64.whl", hash = "sha256:aea440a510e14e818e67bfc4027880e2fb500c2ccb20ab21c7a7c8b5b4703d75"}, - {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_i686.whl", hash = "sha256:6974f52a02321b36847cd19d1b8e381bf39939c21efd6ee2fc13a28b0d99348c"}, - {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_ppc64le.whl", hash = "sha256:a7e53012d2853a07a4a79c00643832161a910674a893d296c9f1259859a289d2"}, - {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_x86_64.whl", hash = "sha256:d7702622a8b40c49bffb46e1e3ba2e81268d5c04a34f460978c6b5517a34dd52"}, - {file = "Brotli-1.1.0-cp36-cp36m-win32.whl", hash = "sha256:a599669fd7c47233438a56936988a2478685e74854088ef5293802123b5b2460"}, - {file = "Brotli-1.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:d143fd47fad1db3d7c27a1b1d66162e855b5d50a89666af46e1679c496e8e579"}, - {file = "Brotli-1.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:11d00ed0a83fa22d29bc6b64ef636c4552ebafcef57154b4ddd132f5638fbd1c"}, - {file = "Brotli-1.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f733d788519c7e3e71f0855c96618720f5d3d60c3cb829d8bbb722dddce37985"}, - {file = "Brotli-1.1.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:929811df5462e182b13920da56c6e0284af407d1de637d8e536c5cd00a7daf60"}, - {file = "Brotli-1.1.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0b63b949ff929fbc2d6d3ce0e924c9b93c9785d877a21a1b678877ffbbc4423a"}, - {file = "Brotli-1.1.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:d192f0f30804e55db0d0e0a35d83a9fead0e9a359a9ed0285dbacea60cc10a84"}, - {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:f296c40e23065d0d6650c4aefe7470d2a25fffda489bcc3eb66083f3ac9f6643"}, - {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:919e32f147ae93a09fe064d77d5ebf4e35502a8df75c29fb05788528e330fe74"}, - {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:23032ae55523cc7bccb4f6a0bf368cd25ad9bcdcc1990b64a647e7bbcce9cb5b"}, - {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:224e57f6eac61cc449f498cc5f0e1725ba2071a3d4f48d5d9dffba42db196438"}, - {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:cb1dac1770878ade83f2ccdf7d25e494f05c9165f5246b46a621cc849341dc01"}, - {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:3ee8a80d67a4334482d9712b8e83ca6b1d9bc7e351931252ebef5d8f7335a547"}, - {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:5e55da2c8724191e5b557f8e18943b1b4839b8efc3ef60d65985bcf6f587dd38"}, - {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:d342778ef319e1026af243ed0a07c97acf3bad33b9f29e7ae6a1f68fd083e90c"}, - {file = "Brotli-1.1.0-cp37-cp37m-win32.whl", hash = "sha256:587ca6d3cef6e4e868102672d3bd9dc9698c309ba56d41c2b9c85bbb903cdb95"}, - {file = "Brotli-1.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:2954c1c23f81c2eaf0b0717d9380bd348578a94161a65b3a2afc62c86467dd68"}, - {file = "Brotli-1.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:efa8b278894b14d6da122a72fefcebc28445f2d3f880ac59d46c90f4c13be9a3"}, - {file = "Brotli-1.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:03d20af184290887bdea3f0f78c4f737d126c74dc2f3ccadf07e54ceca3bf208"}, - {file = "Brotli-1.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6172447e1b368dcbc458925e5ddaf9113477b0ed542df258d84fa28fc45ceea7"}, - {file = "Brotli-1.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a743e5a28af5f70f9c080380a5f908d4d21d40e8f0e0c8901604d15cfa9ba751"}, - {file = "Brotli-1.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0541e747cce78e24ea12d69176f6a7ddb690e62c425e01d31cc065e69ce55b48"}, - {file = "Brotli-1.1.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:cdbc1fc1bc0bff1cef838eafe581b55bfbffaed4ed0318b724d0b71d4d377619"}, - {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:890b5a14ce214389b2cc36ce82f3093f96f4cc730c1cffdbefff77a7c71f2a97"}, - {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ab4fbee0b2d9098c74f3057b2bc055a8bd92ccf02f65944a241b4349229185a"}, - {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:141bd4d93984070e097521ed07e2575b46f817d08f9fa42b16b9b5f27b5ac088"}, - {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fce1473f3ccc4187f75b4690cfc922628aed4d3dd013d047f95a9b3919a86596"}, - {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d2b35ca2c7f81d173d2fadc2f4f31e88cc5f7a39ae5b6db5513cf3383b0e0ec7"}, - {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:af6fa6817889314555aede9a919612b23739395ce767fe7fcbea9a80bf140fe5"}, - {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:2feb1d960f760a575dbc5ab3b1c00504b24caaf6986e2dc2b01c09c87866a943"}, - {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:4410f84b33374409552ac9b6903507cdb31cd30d2501fc5ca13d18f73548444a"}, - {file = "Brotli-1.1.0-cp38-cp38-win32.whl", hash = "sha256:db85ecf4e609a48f4b29055f1e144231b90edc90af7481aa731ba2d059226b1b"}, - {file = "Brotli-1.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:3d7954194c36e304e1523f55d7042c59dc53ec20dd4e9ea9d151f1b62b4415c0"}, - {file = "Brotli-1.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5fb2ce4b8045c78ebbc7b8f3c15062e435d47e7393cc57c25115cfd49883747a"}, - {file = "Brotli-1.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7905193081db9bfa73b1219140b3d315831cbff0d8941f22da695832f0dd188f"}, - {file = "Brotli-1.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a77def80806c421b4b0af06f45d65a136e7ac0bdca3c09d9e2ea4e515367c7e9"}, - {file = "Brotli-1.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8dadd1314583ec0bf2d1379f7008ad627cd6336625d6679cf2f8e67081b83acf"}, - {file = "Brotli-1.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:901032ff242d479a0efa956d853d16875d42157f98951c0230f69e69f9c09bac"}, - {file = "Brotli-1.1.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:22fc2a8549ffe699bfba2256ab2ed0421a7b8fadff114a3d201794e45a9ff578"}, - {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ae15b066e5ad21366600ebec29a7ccbc86812ed267e4b28e860b8ca16a2bc474"}, - {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:949f3b7c29912693cee0afcf09acd6ebc04c57af949d9bf77d6101ebb61e388c"}, - {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:89f4988c7203739d48c6f806f1e87a1d96e0806d44f0fba61dba81392c9e474d"}, - {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:de6551e370ef19f8de1807d0a9aa2cdfdce2e85ce88b122fe9f6b2b076837e59"}, - {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0737ddb3068957cf1b054899b0883830bb1fec522ec76b1098f9b6e0f02d9419"}, - {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:4f3607b129417e111e30637af1b56f24f7a49e64763253bbc275c75fa887d4b2"}, - {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:6c6e0c425f22c1c719c42670d561ad682f7bfeeef918edea971a79ac5252437f"}, - {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:494994f807ba0b92092a163a0a283961369a65f6cbe01e8891132b7a320e61eb"}, - {file = "Brotli-1.1.0-cp39-cp39-win32.whl", hash = "sha256:f0d8a7a6b5983c2496e364b969f0e526647a06b075d034f3297dc66f3b360c64"}, - {file = "Brotli-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:cdad5b9014d83ca68c25d2e9444e28e967ef16e80f6b436918c700c117a85467"}, - {file = "Brotli-1.1.0.tar.gz", hash = "sha256:81de08ac11bcb85841e440c13611c00b67d3bf82698314928d0b676362546724"}, + {file = "brotli-1.2.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:99cfa69813d79492f0e5d52a20fd18395bc82e671d5d40bd5a91d13e75e468e8"}, + {file = "brotli-1.2.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:3ebe801e0f4e56d17cd386ca6600573e3706ce1845376307f5d2cbd32149b69a"}, + {file = "brotli-1.2.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:a387225a67f619bf16bd504c37655930f910eb03675730fc2ad69d3d8b5e7e92"}, + {file = "brotli-1.2.0-cp27-cp27m-win32.whl", hash = "sha256:b908d1a7b28bc72dfb743be0d4d3f8931f8309f810af66c906ae6cd4127c93cb"}, + {file = "brotli-1.2.0-cp27-cp27m-win_amd64.whl", hash = "sha256:d206a36b4140fbb5373bf1eb73fb9de589bb06afd0d22376de23c5e91d0ab35f"}, + {file = "brotli-1.2.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:7e9053f5fb4e0dfab89243079b3e217f2aea4085e4d58c5c06115fc34823707f"}, + {file = "brotli-1.2.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:4735a10f738cb5516905a121f32b24ce196ab82cfc1e4ba2e3ad1b371085fd46"}, + {file = "brotli-1.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3b90b767916ac44e93a8e28ce6adf8d551e43affb512f2377c732d486ac6514e"}, + {file = "brotli-1.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6be67c19e0b0c56365c6a76e393b932fb0e78b3b56b711d180dd7013cb1fd984"}, + {file = "brotli-1.2.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0bbd5b5ccd157ae7913750476d48099aaf507a79841c0d04a9db4415b14842de"}, + {file = "brotli-1.2.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3f3c908bcc404c90c77d5a073e55271a0a498f4e0756e48127c35d91cf155947"}, + {file = "brotli-1.2.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1b557b29782a643420e08d75aea889462a4a8796e9a6cf5621ab05a3f7da8ef2"}, + {file = "brotli-1.2.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:81da1b229b1889f25adadc929aeb9dbc4e922bd18561b65b08dd9343cfccca84"}, + {file = "brotli-1.2.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:ff09cd8c5eec3b9d02d2408db41be150d8891c5566addce57513bf546e3d6c6d"}, + {file = "brotli-1.2.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a1778532b978d2536e79c05dac2d8cd857f6c55cd0c95ace5b03740824e0e2f1"}, + {file = "brotli-1.2.0-cp310-cp310-win32.whl", hash = "sha256:b232029d100d393ae3c603c8ffd7e3fe6f798c5e28ddca5feabb8e8fdb732997"}, + {file = "brotli-1.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:ef87b8ab2704da227e83a246356a2b179ef826f550f794b2c52cddb4efbd0196"}, + {file = "brotli-1.2.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:15b33fe93cedc4caaff8a0bd1eb7e3dab1c61bb22a0bf5bdfdfd97cd7da79744"}, + {file = "brotli-1.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:898be2be399c221d2671d29eed26b6b2713a02c2119168ed914e7d00ceadb56f"}, + {file = "brotli-1.2.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:350c8348f0e76fff0a0fd6c26755d2653863279d086d3aa2c290a6a7251135dd"}, + {file = "brotli-1.2.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2e1ad3fda65ae0d93fec742a128d72e145c9c7a99ee2fcd667785d99eb25a7fe"}, + {file = "brotli-1.2.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:40d918bce2b427a0c4ba189df7a006ac0c7277c180aee4617d99e9ccaaf59e6a"}, + {file = "brotli-1.2.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2a7f1d03727130fc875448b65b127a9ec5d06d19d0148e7554384229706f9d1b"}, + {file = "brotli-1.2.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:9c79f57faa25d97900bfb119480806d783fba83cd09ee0b33c17623935b05fa3"}, + {file = "brotli-1.2.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:844a8ceb8483fefafc412f85c14f2aae2fb69567bf2a0de53cdb88b73e7c43ae"}, + {file = "brotli-1.2.0-cp311-cp311-win32.whl", hash = "sha256:aa47441fa3026543513139cb8926a92a8e305ee9c71a6209ef7a97d91640ea03"}, + {file = "brotli-1.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:022426c9e99fd65d9475dce5c195526f04bb8be8907607e27e747893f6ee3e24"}, + {file = "brotli-1.2.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:35d382625778834a7f3061b15423919aa03e4f5da34ac8e02c074e4b75ab4f84"}, + {file = "brotli-1.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7a61c06b334bd99bc5ae84f1eeb36bfe01400264b3c352f968c6e30a10f9d08b"}, + {file = "brotli-1.2.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:acec55bb7c90f1dfc476126f9711a8e81c9af7fb617409a9ee2953115343f08d"}, + {file = "brotli-1.2.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:260d3692396e1895c5034f204f0db022c056f9e2ac841593a4cf9426e2a3faca"}, + {file = "brotli-1.2.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:072e7624b1fc4d601036ab3f4f27942ef772887e876beff0301d261210bca97f"}, + {file = "brotli-1.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:adedc4a67e15327dfdd04884873c6d5a01d3e3b6f61406f99b1ed4865a2f6d28"}, + {file = "brotli-1.2.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7a47ce5c2288702e09dc22a44d0ee6152f2c7eda97b3c8482d826a1f3cfc7da7"}, + {file = "brotli-1.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:af43b8711a8264bb4e7d6d9a6d004c3a2019c04c01127a868709ec29962b6036"}, + {file = "brotli-1.2.0-cp312-cp312-win32.whl", hash = "sha256:e99befa0b48f3cd293dafeacdd0d191804d105d279e0b387a32054c1180f3161"}, + {file = "brotli-1.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:b35c13ce241abdd44cb8ca70683f20c0c079728a36a996297adb5334adfc1c44"}, + {file = "brotli-1.2.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:9e5825ba2c9998375530504578fd4d5d1059d09621a02065d1b6bfc41a8e05ab"}, + {file = "brotli-1.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0cf8c3b8ba93d496b2fae778039e2f5ecc7cff99df84df337ca31d8f2252896c"}, + {file = "brotli-1.2.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c8565e3cdc1808b1a34714b553b262c5de5fbda202285782173ec137fd13709f"}, + {file = "brotli-1.2.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:26e8d3ecb0ee458a9804f47f21b74845cc823fd1bb19f02272be70774f56e2a6"}, + {file = "brotli-1.2.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:67a91c5187e1eec76a61625c77a6c8c785650f5b576ca732bd33ef58b0dff49c"}, + {file = "brotli-1.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4ecdb3b6dc36e6d6e14d3a1bdc6c1057c8cbf80db04031d566eb6080ce283a48"}, + {file = "brotli-1.2.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3e1b35d56856f3ed326b140d3c6d9db91740f22e14b06e840fe4bb1923439a18"}, + {file = "brotli-1.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:54a50a9dad16b32136b2241ddea9e4df159b41247b2ce6aac0b3276a66a8f1e5"}, + {file = "brotli-1.2.0-cp313-cp313-win32.whl", hash = "sha256:1b1d6a4efedd53671c793be6dd760fcf2107da3a52331ad9ea429edf0902f27a"}, + {file = "brotli-1.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:b63daa43d82f0cdabf98dee215b375b4058cce72871fd07934f179885aad16e8"}, + {file = "brotli-1.2.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:6c12dad5cd04530323e723787ff762bac749a7b256a5bece32b2243dd5c27b21"}, + {file = "brotli-1.2.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:3219bd9e69868e57183316ee19c84e03e8f8b5a1d1f2667e1aa8c2f91cb061ac"}, + {file = "brotli-1.2.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:963a08f3bebd8b75ac57661045402da15991468a621f014be54e50f53a58d19e"}, + {file = "brotli-1.2.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9322b9f8656782414b37e6af884146869d46ab85158201d82bab9abbcb971dc7"}, + {file = "brotli-1.2.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cf9cba6f5b78a2071ec6fb1e7bd39acf35071d90a81231d67e92d637776a6a63"}, + {file = "brotli-1.2.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7547369c4392b47d30a3467fe8c3330b4f2e0f7730e45e3103d7d636678a808b"}, + {file = "brotli-1.2.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:fc1530af5c3c275b8524f2e24841cbe2599d74462455e9bae5109e9ff42e9361"}, + {file = "brotli-1.2.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d2d085ded05278d1c7f65560aae97b3160aeb2ea2c0b3e26204856beccb60888"}, + {file = "brotli-1.2.0-cp314-cp314-win32.whl", hash = "sha256:832c115a020e463c2f67664560449a7bea26b0c1fdd690352addad6d0a08714d"}, + {file = "brotli-1.2.0-cp314-cp314-win_amd64.whl", hash = "sha256:e7c0af964e0b4e3412a0ebf341ea26ec767fa0b4cf81abb5e897c9338b5ad6a3"}, + {file = "brotli-1.2.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:82676c2781ecf0ab23833796062786db04648b7aae8be139f6b8065e5e7b1518"}, + {file = "brotli-1.2.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c16ab1ef7bb55651f5836e8e62db1f711d55b82ea08c3b8083ff037157171a69"}, + {file = "brotli-1.2.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e85190da223337a6b7431d92c799fca3e2982abd44e7b8dec69938dcc81c8e9e"}, + {file = "brotli-1.2.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:d8c05b1dfb61af28ef37624385b0029df902ca896a639881f594060b30ffc9a7"}, + {file = "brotli-1.2.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:465a0d012b3d3e4f1d6146ea019b5c11e3e87f03d1676da1cc3833462e672fb0"}, + {file = "brotli-1.2.0-cp36-cp36m-musllinux_1_2_aarch64.whl", hash = "sha256:96fbe82a58cdb2f872fa5d87dedc8477a12993626c446de794ea025bbda625ea"}, + {file = "brotli-1.2.0-cp36-cp36m-musllinux_1_2_i686.whl", hash = "sha256:1b71754d5b6eda54d16fbbed7fce2d8bc6c052a1b91a35c320247946ee103502"}, + {file = "brotli-1.2.0-cp36-cp36m-musllinux_1_2_ppc64le.whl", hash = "sha256:66c02c187ad250513c2f4fce973ef402d22f80e0adce734ee4e4efd657b6cb64"}, + {file = "brotli-1.2.0-cp36-cp36m-musllinux_1_2_x86_64.whl", hash = "sha256:ba76177fd318ab7b3b9bf6522be5e84c2ae798754b6cc028665490f6e66b5533"}, + {file = "brotli-1.2.0-cp36-cp36m-win32.whl", hash = "sha256:c1702888c9f3383cc2f09eb3e88b8babf5965a54afb79649458ec7c3c7a63e96"}, + {file = "brotli-1.2.0-cp36-cp36m-win_amd64.whl", hash = "sha256:f8d635cafbbb0c61327f942df2e3f474dde1cff16c3cd0580564774eaba1ee13"}, + {file = "brotli-1.2.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e80a28f2b150774844c8b454dd288be90d76ba6109670fe33d7ff54d96eb5cb8"}, + {file = "brotli-1.2.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50b1b799f45da91292ffaa21a473ab3a3054fa78560e8ff67082a185274431c8"}, + {file = "brotli-1.2.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:29b7e6716ee4ea0c59e3b241f682204105f7da084d6254ec61886508efeb43bc"}, + {file = "brotli-1.2.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:640fe199048f24c474ec6f3eae67c48d286de12911110437a36a87d7c89573a6"}, + {file = "brotli-1.2.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:92edab1e2fd6cd5ca605f57d4545b6599ced5dea0fd90b2bcdf8b247a12bd190"}, + {file = "brotli-1.2.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:7274942e69b17f9cef76691bcf38f2b2d4c8a5f5dba6ec10958363dcb3308a0a"}, + {file = "brotli-1.2.0-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:a56ef534b66a749759ebd091c19c03ef81eb8cd96f0d1d16b59127eaf1b97a12"}, + {file = "brotli-1.2.0-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:5732eff8973dd995549a18ecbd8acd692ac611c5c0bb3f59fa3541ae27b33be3"}, + {file = "brotli-1.2.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:598e88c736f63a0efec8363f9eb34e5b5536b7b6b1821e401afcb501d881f59a"}, + {file = "brotli-1.2.0-cp37-cp37m-win32.whl", hash = "sha256:7ad8cec81f34edf44a1c6a7edf28e7b7806dfb8886e371d95dcf789ccd4e4982"}, + {file = "brotli-1.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:865cedc7c7c303df5fad14a57bc5db1d4f4f9b2b4d0a7523ddd206f00c121a16"}, + {file = "brotli-1.2.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ac27a70bda257ae3f380ec8310b0a06680236bea547756c277b5dfe55a2452a8"}, + {file = "brotli-1.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e813da3d2d865e9793ef681d3a6b66fa4b7c19244a45b817d0cceda67e615990"}, + {file = "brotli-1.2.0-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9fe11467c42c133f38d42289d0861b6b4f9da31e8087ca2c0d7ebb4543625526"}, + {file = "brotli-1.2.0-cp38-cp38-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c0d6770111d1879881432f81c369de5cde6e9467be7c682a983747ec800544e2"}, + {file = "brotli-1.2.0-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:eda5a6d042c698e28bda2507a89b16555b9aa954ef1d750e1c20473481aff675"}, + {file = "brotli-1.2.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:3173e1e57cebb6d1de186e46b5680afbd82fd4301d7b2465beebe83ed317066d"}, + {file = "brotli-1.2.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:71a66c1c9be66595d628467401d5976158c97888c2c9379c034e1e2312c5b4f5"}, + {file = "brotli-1.2.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:1e68cdf321ad05797ee41d1d09169e09d40fdf51a725bb148bff892ce04583d7"}, + {file = "brotli-1.2.0-cp38-cp38-win32.whl", hash = "sha256:f16dace5e4d3596eaeb8af334b4d2c820d34b8278da633ce4a00020b2eac981c"}, + {file = "brotli-1.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:14ef29fc5f310d34fc7696426071067462c9292ed98b5ff5a27ac70a200e5470"}, + {file = "brotli-1.2.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8d4f47f284bdd28629481c97b5f29ad67544fa258d9091a6ed1fda47c7347cd1"}, + {file = "brotli-1.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2881416badd2a88a7a14d981c103a52a23a276a553a8aacc1346c2ff47c8dc17"}, + {file = "brotli-1.2.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2d39b54b968f4b49b5e845758e202b1035f948b0561ff5e6385e855c96625971"}, + {file = "brotli-1.2.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:95db242754c21a88a79e01504912e537808504465974ebb92931cfca2510469e"}, + {file = "brotli-1.2.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bba6e7e6cfe1e6cb6eb0b7c2736a6059461de1fa2c0ad26cf845de6c078d16c8"}, + {file = "brotli-1.2.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:88ef7d55b7bcf3331572634c3fd0ed327d237ceb9be6066810d39020a3ebac7a"}, + {file = "brotli-1.2.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:7fa18d65a213abcfbb2f6cafbb4c58863a8bd6f2103d65203c520ac117d1944b"}, + {file = "brotli-1.2.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:09ac247501d1909e9ee47d309be760c89c990defbb2e0240845c892ea5ff0de4"}, + {file = "brotli-1.2.0-cp39-cp39-win32.whl", hash = "sha256:c25332657dee6052ca470626f18349fc1fe8855a56218e19bd7a8c6ad4952c49"}, + {file = "brotli-1.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:1ce223652fd4ed3eb2b7f78fbea31c52314baecfac68db44037bb4167062a937"}, + {file = "brotli-1.2.0.tar.gz", hash = "sha256:e310f77e41941c13340a95976fe66a8a95b01e783d430eeaf7a2f87e0a57dd0a"}, ] [[package]] @@ -1784,13 +1762,13 @@ files = [ [[package]] name = "mitmproxy" -version = "12.2.0" +version = "12.2.1" description = "An interactive, SSL/TLS-capable intercepting proxy for HTTP/1, HTTP/2, and WebSockets." optional = false python-versions = ">=3.12" groups = ["dev"] files = [ - {file = "mitmproxy-12.2.0-py3-none-any.whl", hash = "sha256:f645781af2bd62431b4cf96fcf0eab2c02055956765be54e4f32dc04eec09d12"}, + {file = "mitmproxy-12.2.1-py3-none-any.whl", hash = "sha256:7a508cc9fb906253eb26460d99b3572bf5a7b4a185ab62534379ac1915677dd2"}, ] [package.dependencies] @@ -1798,7 +1776,7 @@ aioquic = "1.2.0" argon2-cffi = ">=23.1.0,<=25.1.0" asgiref = ">=3.2.10,<=3.10.0" bcrypt = "5.0.0" -Brotli = ">=1.0,<=1.1.0" +Brotli = ">=1.0,<=1.2.0" certifi = ">=2019.9.11" cryptography = ">=42.0,<=46.1" flask = ">=3.0,<=3.1.2" @@ -1814,7 +1792,7 @@ pydivert = {version = ">=2.0.3,<=2.1.0", markers = "sys_platform == \"win32\""} pyOpenSSL = ">=24.3,<=25.3.0" pyparsing = ">=2.4.2,<=3.2.5" pyperclip = ">=1.9.0,<=1.11.0" -"ruamel.yaml" = ">=0.18.10,<=0.18.15" +"ruamel.yaml" = ">=0.18.10,<=0.18.16" sortedcontainers = ">=2.3,<=2.4.0" tornado = ">=6.5.0,<=6.5.2" urwid = ">=2.6.14,<=3.0.3" @@ -1823,60 +1801,60 @@ zstandard = "0.25" [[package]] name = "mitmproxy-linux" -version = "0.12.7" +version = "0.12.8" description = "" optional = false python-versions = ">=3.12" groups = ["dev"] markers = "sys_platform == \"linux\"" files = [ - {file = "mitmproxy_linux-0.12.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47ce06e6de8dfecffce3b9205cff45366921822ffdc3fbb88e80166d6c5209b1"}, - {file = "mitmproxy_linux-0.12.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1f708fadad84f36b6bc8d47850bb9501c63678bba092ea887c65d72fe49a2e0"}, - {file = "mitmproxy_linux-0.12.7.tar.gz", hash = "sha256:af5287a98a055979e755c58b71b443619370af4e5897eaa2fe2c2364620e2f1f"}, + {file = "mitmproxy_linux-0.12.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2238455e65970382825baed2e998601ea82d8dcaae51bd8ee0859d596524a822"}, + {file = "mitmproxy_linux-0.12.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fbcb25316e95d0b2b5ced4e0cc3d90fdb1b7169300a005cc79339894d665363a"}, + {file = "mitmproxy_linux-0.12.8.tar.gz", hash = "sha256:0bea9353c71ebfd2174f6730b3fd0fdff3adea1aa15450035bed3b83e36ef455"}, ] [[package]] name = "mitmproxy-macos" -version = "0.12.7" +version = "0.12.8" description = "" optional = false python-versions = ">=3.12" groups = ["dev"] markers = "sys_platform == \"darwin\"" files = [ - {file = "mitmproxy_macos-0.12.7-py3-none-any.whl", hash = "sha256:340ae9d74ca111193b1e1c397c853e7a46eea7295dcb3a4b41d2a079b894a4e3"}, + {file = "mitmproxy_macos-0.12.8-py3-none-any.whl", hash = "sha256:6da01f118e2110ddf038489c804e77818ef5217d34dc9605cb265a349ed4f140"}, ] [[package]] name = "mitmproxy-rs" -version = "0.12.7" +version = "0.12.8" description = "" optional = false python-versions = ">=3.12" groups = ["dev"] files = [ - {file = "mitmproxy_rs-0.12.7-cp312-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:287dfafc4ae78b05ac3fd434fdd7ad9ab3c1cbc9fc0a1f918a15ebe0880643da"}, - {file = "mitmproxy_rs-0.12.7-cp312-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:948c66181038e56c451ff099e9a2fc13610f55713a64188703e631dc61770663"}, - {file = "mitmproxy_rs-0.12.7-cp312-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea7d8b5fbb8a6263500bc2cebcfadf06efb0778acfd2ca4238a6971ea4dc8735"}, - {file = "mitmproxy_rs-0.12.7-cp312-abi3-win_amd64.whl", hash = "sha256:538858ecb480949eba72f43e071fb68fa4b5ec0c62b659abdc0c661ff389ed3a"}, - {file = "mitmproxy_rs-0.12.7.tar.gz", hash = "sha256:b4d6654e58489886c16afb3dc2e587ef26d5152480d4e48d0e4425f6ff0fcdf9"}, + {file = "mitmproxy_rs-0.12.8-cp312-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:c5b0799808a4de0ee60e8f350043820ad56eea738ce3ce25d5c6faaa245b6c9a"}, + {file = "mitmproxy_rs-0.12.8-cp312-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:739591f696cf29913302a72fa9644cf97228774604304a2ea3987fe5588d231c"}, + {file = "mitmproxy_rs-0.12.8-cp312-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14ea236d0950ab35d667b78b5fe15d43e7345e166e22144624a1283edc78443e"}, + {file = "mitmproxy_rs-0.12.8-cp312-abi3-win_amd64.whl", hash = "sha256:b0ead519f5a4ab019e7912544c0642f28f8336036ef1480e42a772a8cc947550"}, + {file = "mitmproxy_rs-0.12.8.tar.gz", hash = "sha256:16afd0fc1a00d586ffe2027d217908c3e0389d7d0897eccda6e59fda991e89ba"}, ] [package.dependencies] -mitmproxy-linux = {version = "0.12.7", markers = "sys_platform == \"linux\""} -mitmproxy-macos = {version = "0.12.7", markers = "sys_platform == \"darwin\""} -mitmproxy-windows = {version = "0.12.7", markers = "os_name == \"nt\""} +mitmproxy-linux = {version = "0.12.8", markers = "sys_platform == \"linux\""} +mitmproxy-macos = {version = "0.12.8", markers = "sys_platform == \"darwin\""} +mitmproxy-windows = {version = "0.12.8", markers = "os_name == \"nt\""} [[package]] name = "mitmproxy-windows" -version = "0.12.7" +version = "0.12.8" description = "" optional = false python-versions = ">=3.12" groups = ["dev"] markers = "os_name == \"nt\"" files = [ - {file = "mitmproxy_windows-0.12.7-py3-none-any.whl", hash = "sha256:f4eb580f377a33f8550a4f893c6bee27261052a39167098763995a68be9f2683"}, + {file = "mitmproxy_windows-0.12.8-py3-none-any.whl", hash = "sha256:2dd727e2caed642ecfbbad1ca4d07d28fca0c5ab1b0be9dc62ccecbdb2257dce"}, ] [[package]] @@ -2264,14 +2242,14 @@ markers = {main = "platform_python_implementation != \"PyPy\" and implementation [[package]] name = "pydantic" -version = "2.12.4" +version = "2.12.5" description = "Data validation using Python type hints" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "pydantic-2.12.4-py3-none-any.whl", hash = "sha256:92d3d202a745d46f9be6df459ac5a064fdaa3c1c4cd8adcfa332ccf3c05f871e"}, - {file = "pydantic-2.12.4.tar.gz", hash = "sha256:0f8cb9555000a4b5b617f66bfd2566264c4984b27589d3b845685983e8ea85ac"}, + {file = "pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d"}, + {file = "pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49"}, ] [package.dependencies] @@ -2879,47 +2857,122 @@ files = [ [[package]] name = "ruamel-yaml" -version = "0.18.15" +version = "0.18.16" description = "ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order" optional = false python-versions = ">=3.8" groups = ["dev"] files = [ - {file = "ruamel.yaml-0.18.15-py3-none-any.whl", hash = "sha256:148f6488d698b7a5eded5ea793a025308b25eca97208181b6a026037f391f701"}, - {file = "ruamel.yaml-0.18.15.tar.gz", hash = "sha256:dbfca74b018c4c3fba0b9cc9ee33e53c371194a9000e694995e620490fd40700"}, + {file = "ruamel.yaml-0.18.16-py3-none-any.whl", hash = "sha256:048f26d64245bae57a4f9ef6feb5b552a386830ef7a826f235ffb804c59efbba"}, + {file = "ruamel.yaml-0.18.16.tar.gz", hash = "sha256:a6e587512f3c998b2225d68aa1f35111c29fad14aed561a26e73fab729ec5e5a"}, ] +[package.dependencies] +"ruamel.yaml.clib" = {version = ">=0.2.7", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.14\""} + [package.extras] docs = ["mercurial (>5.7)", "ryd"] jinja2 = ["ruamel.yaml.jinja2 (>=0.2)"] +[[package]] +name = "ruamel-yaml-clib" +version = "0.2.15" +description = "C version of reader, parser and emitter for ruamel.yaml derived from libyaml" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +markers = "platform_python_implementation == \"CPython\" and python_version == \"3.13\"" +files = [ + {file = "ruamel_yaml_clib-0.2.15-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:88eea8baf72f0ccf232c22124d122a7f26e8a24110a0273d9bcddcb0f7e1fa03"}, + {file = "ruamel_yaml_clib-0.2.15-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9b6f7d74d094d1f3a4e157278da97752f16ee230080ae331fcc219056ca54f77"}, + {file = "ruamel_yaml_clib-0.2.15-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:4be366220090d7c3424ac2b71c90d1044ea34fca8c0b88f250064fd06087e614"}, + {file = "ruamel_yaml_clib-0.2.15-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1f66f600833af58bea694d5892453f2270695b92200280ee8c625ec5a477eed3"}, + {file = "ruamel_yaml_clib-0.2.15-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:da3d6adadcf55a93c214d23941aef4abfd45652110aed6580e814152f385b862"}, + {file = "ruamel_yaml_clib-0.2.15-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e9fde97ecb7bb9c41261c2ce0da10323e9227555c674989f8d9eb7572fc2098d"}, + {file = "ruamel_yaml_clib-0.2.15-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:05c70f7f86be6f7bee53794d80050a28ae7e13e4a0087c1839dcdefd68eb36b6"}, + {file = "ruamel_yaml_clib-0.2.15-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:6f1d38cbe622039d111b69e9ca945e7e3efebb30ba998867908773183357f3ed"}, + {file = "ruamel_yaml_clib-0.2.15-cp310-cp310-win32.whl", hash = "sha256:fe239bdfdae2302e93bd6e8264bd9b71290218fff7084a9db250b55caaccf43f"}, + {file = "ruamel_yaml_clib-0.2.15-cp310-cp310-win_amd64.whl", hash = "sha256:468858e5cbde0198337e6a2a78eda8c3fb148bdf4c6498eaf4bc9ba3f8e780bd"}, + {file = "ruamel_yaml_clib-0.2.15-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c583229f336682b7212a43d2fa32c30e643d3076178fb9f7a6a14dde85a2d8bd"}, + {file = "ruamel_yaml_clib-0.2.15-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:56ea19c157ed8c74b6be51b5fa1c3aff6e289a041575f0556f66e5fb848bb137"}, + {file = "ruamel_yaml_clib-0.2.15-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5fea0932358e18293407feb921d4f4457db837b67ec1837f87074667449f9401"}, + {file = "ruamel_yaml_clib-0.2.15-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef71831bd61fbdb7aa0399d5c4da06bea37107ab5c79ff884cc07f2450910262"}, + {file = "ruamel_yaml_clib-0.2.15-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:617d35dc765715fa86f8c3ccdae1e4229055832c452d4ec20856136acc75053f"}, + {file = "ruamel_yaml_clib-0.2.15-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1b45498cc81a4724a2d42273d6cfc243c0547ad7c6b87b4f774cb7bcc131c98d"}, + {file = "ruamel_yaml_clib-0.2.15-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:def5663361f6771b18646620fca12968aae730132e104688766cf8a3b1d65922"}, + {file = "ruamel_yaml_clib-0.2.15-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:014181cdec565c8745b7cbc4de3bf2cc8ced05183d986e6d1200168e5bb59490"}, + {file = "ruamel_yaml_clib-0.2.15-cp311-cp311-win32.whl", hash = "sha256:d290eda8f6ada19e1771b54e5706b8f9807e6bb08e873900d5ba114ced13e02c"}, + {file = "ruamel_yaml_clib-0.2.15-cp311-cp311-win_amd64.whl", hash = "sha256:bdc06ad71173b915167702f55d0f3f027fc61abd975bd308a0968c02db4a4c3e"}, + {file = "ruamel_yaml_clib-0.2.15-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cb15a2e2a90c8475df45c0949793af1ff413acfb0a716b8b94e488ea95ce7cff"}, + {file = "ruamel_yaml_clib-0.2.15-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:64da03cbe93c1e91af133f5bec37fd24d0d4ba2418eaf970d7166b0a26a148a2"}, + {file = "ruamel_yaml_clib-0.2.15-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f6d3655e95a80325b84c4e14c080b2470fe4f33b6846f288379ce36154993fb1"}, + {file = "ruamel_yaml_clib-0.2.15-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:71845d377c7a47afc6592aacfea738cc8a7e876d586dfba814501d8c53c1ba60"}, + {file = "ruamel_yaml_clib-0.2.15-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11e5499db1ccbc7f4b41f0565e4f799d863ea720e01d3e99fa0b7b5fcd7802c9"}, + {file = "ruamel_yaml_clib-0.2.15-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4b293a37dc97e2b1e8a1aec62792d1e52027087c8eea4fc7b5abd2bdafdd6642"}, + {file = "ruamel_yaml_clib-0.2.15-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:512571ad41bba04eac7268fe33f7f4742210ca26a81fe0c75357fa682636c690"}, + {file = "ruamel_yaml_clib-0.2.15-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e5e9f630c73a490b758bf14d859a39f375e6999aea5ddd2e2e9da89b9953486a"}, + {file = "ruamel_yaml_clib-0.2.15-cp312-cp312-win32.whl", hash = "sha256:f4421ab780c37210a07d138e56dd4b51f8642187cdfb433eb687fe8c11de0144"}, + {file = "ruamel_yaml_clib-0.2.15-cp312-cp312-win_amd64.whl", hash = "sha256:2b216904750889133d9222b7b873c199d48ecbb12912aca78970f84a5aa1a4bc"}, + {file = "ruamel_yaml_clib-0.2.15-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4dcec721fddbb62e60c2801ba08c87010bd6b700054a09998c4d09c08147b8fb"}, + {file = "ruamel_yaml_clib-0.2.15-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:65f48245279f9bb301d1276f9679b82e4c080a1ae25e679f682ac62446fac471"}, + {file = "ruamel_yaml_clib-0.2.15-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:46895c17ead5e22bea5e576f1db7e41cb273e8d062c04a6a49013d9f60996c25"}, + {file = "ruamel_yaml_clib-0.2.15-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3eb199178b08956e5be6288ee0b05b2fb0b5c1f309725ad25d9c6ea7e27f962a"}, + {file = "ruamel_yaml_clib-0.2.15-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4d1032919280ebc04a80e4fb1e93f7a738129857eaec9448310e638c8bccefcf"}, + {file = "ruamel_yaml_clib-0.2.15-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ab0df0648d86a7ecbd9c632e8f8d6b21bb21b5fc9d9e095c796cacf32a728d2d"}, + {file = "ruamel_yaml_clib-0.2.15-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:331fb180858dd8534f0e61aa243b944f25e73a4dae9962bd44c46d1761126bbf"}, + {file = "ruamel_yaml_clib-0.2.15-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fd4c928ddf6bce586285daa6d90680b9c291cfd045fc40aad34e445d57b1bf51"}, + {file = "ruamel_yaml_clib-0.2.15-cp313-cp313-win32.whl", hash = "sha256:bf0846d629e160223805db9fe8cc7aec16aaa11a07310c50c8c7164efa440aec"}, + {file = "ruamel_yaml_clib-0.2.15-cp313-cp313-win_amd64.whl", hash = "sha256:45702dfbea1420ba3450bb3dd9a80b33f0badd57539c6aac09f42584303e0db6"}, + {file = "ruamel_yaml_clib-0.2.15-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:753faf20b3a5906faf1fc50e4ddb8c074cb9b251e00b14c18b28492f933ac8ef"}, + {file = "ruamel_yaml_clib-0.2.15-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:480894aee0b29752560a9de46c0e5f84a82602f2bc5c6cde8db9a345319acfdf"}, + {file = "ruamel_yaml_clib-0.2.15-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:4d3b58ab2454b4747442ac76fab66739c72b1e2bb9bd173d7694b9f9dbc9c000"}, + {file = "ruamel_yaml_clib-0.2.15-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bfd309b316228acecfa30670c3887dcedf9b7a44ea39e2101e75d2654522acd4"}, + {file = "ruamel_yaml_clib-0.2.15-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2812ff359ec1f30129b62372e5f22a52936fac13d5d21e70373dbca5d64bb97c"}, + {file = "ruamel_yaml_clib-0.2.15-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7e74ea87307303ba91073b63e67f2c667e93f05a8c63079ee5b7a5c8d0d7b043"}, + {file = "ruamel_yaml_clib-0.2.15-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:713cd68af9dfbe0bb588e144a61aad8dcc00ef92a82d2e87183ca662d242f524"}, + {file = "ruamel_yaml_clib-0.2.15-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:542d77b72786a35563f97069b9379ce762944e67055bea293480f7734b2c7e5e"}, + {file = "ruamel_yaml_clib-0.2.15-cp314-cp314-win32.whl", hash = "sha256:424ead8cef3939d690c4b5c85ef5b52155a231ff8b252961b6516ed7cf05f6aa"}, + {file = "ruamel_yaml_clib-0.2.15-cp314-cp314-win_amd64.whl", hash = "sha256:ac9b8d5fa4bb7fd2917ab5027f60d4234345fd366fe39aa711d5dca090aa1467"}, + {file = "ruamel_yaml_clib-0.2.15-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:923816815974425fbb1f1bf57e85eca6e14d8adc313c66db21c094927ad01815"}, + {file = "ruamel_yaml_clib-0.2.15-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:dcc7f3162d3711fd5d52e2267e44636e3e566d1e5675a5f0b30e98f2c4af7974"}, + {file = "ruamel_yaml_clib-0.2.15-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5d3c9210219cbc0f22706f19b154c9a798ff65a6beeafbf77fc9c057ec806f7d"}, + {file = "ruamel_yaml_clib-0.2.15-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1bb7b728fd9f405aa00b4a0b17ba3f3b810d0ccc5f77f7373162e9b5f0ff75d5"}, + {file = "ruamel_yaml_clib-0.2.15-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3cb75a3c14f1d6c3c2a94631e362802f70e83e20d1f2b2ef3026c05b415c4900"}, + {file = "ruamel_yaml_clib-0.2.15-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:badd1d7283f3e5894779a6ea8944cc765138b96804496c91812b2829f70e18a7"}, + {file = "ruamel_yaml_clib-0.2.15-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:0ba6604bbc3dfcef844631932d06a1a4dcac3fee904efccf582261948431628a"}, + {file = "ruamel_yaml_clib-0.2.15-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a8220fd4c6f98485e97aea65e1df76d4fed1678ede1fe1d0eed2957230d287c4"}, + {file = "ruamel_yaml_clib-0.2.15-cp39-cp39-win32.whl", hash = "sha256:04d21dc9c57d9608225da28285900762befbb0165ae48482c15d8d4989d4af14"}, + {file = "ruamel_yaml_clib-0.2.15-cp39-cp39-win_amd64.whl", hash = "sha256:27dc656e84396e6d687f97c6e65fb284d100483628f02d95464fd731743a4afe"}, + {file = "ruamel_yaml_clib-0.2.15.tar.gz", hash = "sha256:46e4cc8c43ef6a94885f72512094e482114a8a706d3c555a34ed4b0d20200600"}, +] + [[package]] name = "ruff" -version = "0.14.5" +version = "0.14.6" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" groups = ["dev"] files = [ - {file = "ruff-0.14.5-py3-none-linux_armv6l.whl", hash = "sha256:f3b8248123b586de44a8018bcc9fefe31d23dda57a34e6f0e1e53bd51fd63594"}, - {file = "ruff-0.14.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:f7a75236570318c7a30edd7f5491945f0169de738d945ca8784500b517163a72"}, - {file = "ruff-0.14.5-py3-none-macosx_11_0_arm64.whl", hash = "sha256:6d146132d1ee115f8802356a2dc9a634dbf58184c51bff21f313e8cd1c74899a"}, - {file = "ruff-0.14.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2380596653dcd20b057794d55681571a257a42327da8894b93bbd6111aa801f"}, - {file = "ruff-0.14.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2d1fa985a42b1f075a098fa1ab9d472b712bdb17ad87a8ec86e45e7fa6273e68"}, - {file = "ruff-0.14.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88f0770d42b7fa02bbefddde15d235ca3aa24e2f0137388cc15b2dcbb1f7c7a7"}, - {file = "ruff-0.14.5-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:3676cb02b9061fee7294661071c4709fa21419ea9176087cb77e64410926eb78"}, - {file = "ruff-0.14.5-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b595bedf6bc9cab647c4a173a61acf4f1ac5f2b545203ba82f30fcb10b0318fb"}, - {file = "ruff-0.14.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f55382725ad0bdb2e8ee2babcbbfb16f124f5a59496a2f6a46f1d9d99d93e6e2"}, - {file = "ruff-0.14.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7497d19dce23976bdaca24345ae131a1d38dcfe1b0850ad8e9e6e4fa321a6e19"}, - {file = "ruff-0.14.5-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:410e781f1122d6be4f446981dd479470af86537fb0b8857f27a6e872f65a38e4"}, - {file = "ruff-0.14.5-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:c01be527ef4c91a6d55e53b337bfe2c0f82af024cc1a33c44792d6844e2331e1"}, - {file = "ruff-0.14.5-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:f66e9bb762e68d66e48550b59c74314168ebb46199886c5c5aa0b0fbcc81b151"}, - {file = "ruff-0.14.5-py3-none-musllinux_1_2_i686.whl", hash = "sha256:d93be8f1fa01022337f1f8f3bcaa7ffee2d0b03f00922c45c2207954f351f465"}, - {file = "ruff-0.14.5-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:c135d4b681f7401fe0e7312017e41aba9b3160861105726b76cfa14bc25aa367"}, - {file = "ruff-0.14.5-py3-none-win32.whl", hash = "sha256:c83642e6fccfb6dea8b785eb9f456800dcd6a63f362238af5fc0c83d027dd08b"}, - {file = "ruff-0.14.5-py3-none-win_amd64.whl", hash = "sha256:9d55d7af7166f143c94eae1db3312f9ea8f95a4defef1979ed516dbb38c27621"}, - {file = "ruff-0.14.5-py3-none-win_arm64.whl", hash = "sha256:4b700459d4649e2594b31f20a9de33bc7c19976d4746d8d0798ad959621d64a4"}, - {file = "ruff-0.14.5.tar.gz", hash = "sha256:8d3b48d7d8aad423d3137af7ab6c8b1e38e4de104800f0d596990f6ada1a9fc1"}, + {file = "ruff-0.14.6-py3-none-linux_armv6l.whl", hash = "sha256:d724ac2f1c240dbd01a2ae98db5d1d9a5e1d9e96eba999d1c48e30062df578a3"}, + {file = "ruff-0.14.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:9f7539ea257aa4d07b7ce87aed580e485c40143f2473ff2f2b75aee003186004"}, + {file = "ruff-0.14.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7f6007e55b90a2a7e93083ba48a9f23c3158c433591c33ee2e99a49b889c6332"}, + {file = "ruff-0.14.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a8e7b9d73d8728b68f632aa8e824ef041d068d231d8dbc7808532d3629a6bef"}, + {file = "ruff-0.14.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d50d45d4553a3ebcbd33e7c5e0fe6ca4aafd9a9122492de357205c2c48f00775"}, + {file = "ruff-0.14.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:118548dd121f8a21bfa8ab2c5b80e5b4aed67ead4b7567790962554f38e598ce"}, + {file = "ruff-0.14.6-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:57256efafbfefcb8748df9d1d766062f62b20150691021f8ab79e2d919f7c11f"}, + {file = "ruff-0.14.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ff18134841e5c68f8e5df1999a64429a02d5549036b394fafbe410f886e1989d"}, + {file = "ruff-0.14.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:29c4b7ec1e66a105d5c27bd57fa93203637d66a26d10ca9809dc7fc18ec58440"}, + {file = "ruff-0.14.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:167843a6f78680746d7e226f255d920aeed5e4ad9c03258094a2d49d3028b105"}, + {file = "ruff-0.14.6-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:16a33af621c9c523b1ae006b1b99b159bf5ac7e4b1f20b85b2572455018e0821"}, + {file = "ruff-0.14.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:1432ab6e1ae2dc565a7eea707d3b03a0c234ef401482a6f1621bc1f427c2ff55"}, + {file = "ruff-0.14.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:4c55cfbbe7abb61eb914bfd20683d14cdfb38a6d56c6c66efa55ec6570ee4e71"}, + {file = "ruff-0.14.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:efea3c0f21901a685fff4befda6d61a1bf4cb43de16da87e8226a281d614350b"}, + {file = "ruff-0.14.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:344d97172576d75dc6afc0e9243376dbe1668559c72de1864439c4fc95f78185"}, + {file = "ruff-0.14.6-py3-none-win32.whl", hash = "sha256:00169c0c8b85396516fdd9ce3446c7ca20c2a8f90a77aa945ba6b8f2bfe99e85"}, + {file = "ruff-0.14.6-py3-none-win_amd64.whl", hash = "sha256:390e6480c5e3659f8a4c8d6a0373027820419ac14fa0d2713bd8e6c3e125b8b9"}, + {file = "ruff-0.14.6-py3-none-win_arm64.whl", hash = "sha256:d43c81fbeae52cfa8728d8766bbf46ee4298c888072105815b392da70ca836b2"}, + {file = "ruff-0.14.6.tar.gz", hash = "sha256:6f0c742ca6a7783a736b867a263b9a7a80a45ce9bee391eeda296895f1b4e1cc"}, ] [[package]] @@ -3552,5 +3605,5 @@ cffi = ["cffi (>=1.17,<2.0) ; platform_python_implementation != \"PyPy\" and pyt [metadata] lock-version = "2.1" -python-versions = "^3.14" -content-hash = "a608b28c4a5f08ea116de52c2a9bd9a838847c79ade7b445efde0abf678c3671" \ No newline at end of file +python-versions = "^3.13" +content-hash = "f04c63b0c4234a314bb92cfca4d92f11b4777e86eb7745db6f790a8de1f738ab" diff --git a/catalyst-gateway/tests/api_tests/pyproject.toml b/catalyst-gateway/tests/api_tests/pyproject.toml index 06f56e3488c..e24f47eec23 100644 --- a/catalyst-gateway/tests/api_tests/pyproject.toml +++ b/catalyst-gateway/tests/api_tests/pyproject.toml @@ -13,7 +13,7 @@ packages = [ ] [tool.poetry.dependencies] -python = "^3.14" +python = "^3.13" loguru = "^0.7.2" asyncpg = "^0.30.0" requests = "^2.32.3" diff --git a/catalyst-gateway/tests/api_tests/test_data/signed_docs/proposal_form_template.json b/catalyst-gateway/tests/api_tests/test_data/signed_docs/proposal_form_template.json index 0bcecd4eff4..b312e2e9bf5 100644 --- a/catalyst-gateway/tests/api_tests/test_data/signed_docs/proposal_form_template.json +++ b/catalyst-gateway/tests/api_tests/test_data/signed_docs/proposal_form_template.json @@ -1,1497 +1,1481 @@ -[ - { - "content-encoding": "br", - "content-type": "application/json", - "id": "0199802c-21b4-7d6c-aacd-54aa31fe1e4c", - "parameters": [ - { - "cid": "0x", - "id": "0199802c-21b4-7161-a16e-a77af492780f", - "ver": "0199802c-21b4-7161-a16e-a77af492780f" - } - ], - "type": "0ce8ab38-9258-4fbc-a62e-7faa6e58318f", - "ver": "0199802c-21b4-7d6c-aacd-54aa31fe1e4c" - }, - { - "$id": "https://raw.githubusercontent.com/input-output-hk/catalyst-voices/refs/heads/main/docs/src/architecture/08_concepts/document_templates/proposal/f15/0199802c-21b4-7d6c-aacd-54aa31fe1e4c.schema.json", - "$schema": "http://json-schema.org/draft-07/schema#", - "additionalProperties": false, - "definitions": { - "agreementConfirmation": { - "$comment": "UI - A Boolean choice, defaults to `false` but its invalid if its not set to `true`.", - "const": true, - "default": false, - "format": "agreementConfirmation", - "type": "boolean", - "x-note": "Select Yes or No." - }, - "borderGroup": { - "$comment": "UI - Border Group for better UI rendering", - "type": "object", - "x-border-color": "#ff0000" - }, - "currency": { - "title": "Currency/Token amount", - "type": "integer" - }, - "dropDownSingleSelect": { - "$comment": "UI - Drop Down Selection of a single entry from the defined enum.", - "contentMediaType": "text/plain", - "format": "dropDownSingleSelect", - "pattern": "^.*$", - "type": "string", - "x-note": "Select one option from the dropdown menu. Only one choice is allowed." - }, - "durationInMonths": { - "$comment": "UI - A Duration represented in total months.", - "format": "datetime:duration:months", - "type": "integer", - "x-note": "Enter the duration of the proposal in months." - }, - "languageCode": { - "$comment": "UI - ISO 639-1 language code selection", - "default": "en", - "description": "Two-letter ISO 639-1 language code", - "enum": [ - "aa", - "ab", - "af", - "ak", - "am", - "ar", - "as", - "ay", - "az", - "ba", - "be", - "bg", - "bh", - "bi", - "bn", - "bo", - "br", - "bs", - "ca", - "ce", - "ch", - "co", - "cs", - "cu", - "cv", - "cy", - "da", - "de", - "dv", - "dz", - "ee", - "el", - "en", - "eo", - "es", - "et", - "eu", - "fa", - "ff", - "fi", - "fj", - "fo", - "fr", - "fy", - "ga", - "gd", - "gl", - "gn", - "gu", - "gv", - "ha", - "he", - "hi", - "ho", - "hr", - "ht", - "hu", - "hy", - "hz", - "ia", - "id", - "ie", - "ig", - "ii", - "ik", - "io", - "is", - "it", - "iu", - "ja", - "jv", - "ka", - "kg", - "ki", - "kj", - "kk", - "kl", - "km", - "kn", - "ko", - "kr", - "ks", - "ku", - "kv", - "kw", - "ky", - "la", - "lb", - "lg", - "li", - "ln", - "lo", - "lt", - "lu", - "lv", - "mg", - "mh", - "mi", - "mk", - "ml", - "mn", - "mr", - "ms", - "mt", - "my", - "na", - "nb", - "nd", - "ne", - "ng", - "nl", - "nn", - "no", - "nr", - "nv", - "ny", - "oc", - "oj", - "om", - "or", - "os", - "pa", - "pi", - "pl", - "ps", - "pt", - "qu", - "rm", - "rn", - "ro", - "ru", - "rw", - "sa", - "sc", - "sd", - "se", - "sg", - "si", - "sk", - "sl", - "sm", - "sn", - "so", - "sq", - "sr", - "ss", - "st", - "su", - "sv", - "sw", - "ta", - "te", - "tg", - "th", - "ti", - "tk", - "tl", - "tn", - "to", - "tr", - "ts", - "tt", - "tw", - "ty", - "ug", - "uk", - "ur", - "uz", - "ve", - "vi", - "vo", - "wa", - "wo", - "xh", - "yi", - "yo", - "za", - "zh", - "zu" - ], - "title": "Language Code", - "type": "string", - "x-note": "Select the ISO 639-1 two-letter code for the language. For example: ''en'' for English, ''es'' for Spanish, ''fr'' for French, etc." - }, - "multiLineTextEntry": { - "$comment": "UI - Multiline text entry without any markup or rich text capability.", - "contentMediaType": "text/plain", - "pattern": "^[\\S\\s]*$", - "type": "string", - "x-note": "Enter multiple lines of plain text. You can use line breaks but no special formatting." +{ + "$id": "https://raw.githubusercontent.com/input-output-hk/catalyst-voices/refs/heads/main/docs/src/architecture/08_concepts/document_templates/proposal/f15/0199802c-21b4-7d6c-aacd-54aa31fe1e4c.schema.json", + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "definitions": { + "agreementConfirmation": { + "$comment": "UI - A Boolean choice, defaults to `false` but its invalid if its not set to `true`.", + "const": true, + "default": false, + "format": "agreementConfirmation", + "type": "boolean", + "x-note": "Select Yes or No." + }, + "borderGroup": { + "$comment": "UI - Border Group for better UI rendering", + "type": "object", + "x-border-color": "#ff0000" + }, + "currency": { + "title": "Currency/Token amount", + "type": "integer" + }, + "dropDownSingleSelect": { + "$comment": "UI - Drop Down Selection of a single entry from the defined enum.", + "contentMediaType": "text/plain", + "format": "dropDownSingleSelect", + "pattern": "^.*$", + "type": "string", + "x-note": "Select one option from the dropdown menu. Only one choice is allowed." + }, + "durationInMonths": { + "$comment": "UI - A Duration represented in total months.", + "format": "datetime:duration:months", + "type": "integer", + "x-note": "Enter the duration of the proposal in months." + }, + "languageCode": { + "$comment": "UI - ISO 639-1 language code selection", + "default": "en", + "description": "Two-letter ISO 639-1 language code", + "enum": [ + "aa", + "ab", + "af", + "ak", + "am", + "ar", + "as", + "ay", + "az", + "ba", + "be", + "bg", + "bh", + "bi", + "bn", + "bo", + "br", + "bs", + "ca", + "ce", + "ch", + "co", + "cs", + "cu", + "cv", + "cy", + "da", + "de", + "dv", + "dz", + "ee", + "el", + "en", + "eo", + "es", + "et", + "eu", + "fa", + "ff", + "fi", + "fj", + "fo", + "fr", + "fy", + "ga", + "gd", + "gl", + "gn", + "gu", + "gv", + "ha", + "he", + "hi", + "ho", + "hr", + "ht", + "hu", + "hy", + "hz", + "ia", + "id", + "ie", + "ig", + "ii", + "ik", + "io", + "is", + "it", + "iu", + "ja", + "jv", + "ka", + "kg", + "ki", + "kj", + "kk", + "kl", + "km", + "kn", + "ko", + "kr", + "ks", + "ku", + "kv", + "kw", + "ky", + "la", + "lb", + "lg", + "li", + "ln", + "lo", + "lt", + "lu", + "lv", + "mg", + "mh", + "mi", + "mk", + "ml", + "mn", + "mr", + "ms", + "mt", + "my", + "na", + "nb", + "nd", + "ne", + "ng", + "nl", + "nn", + "no", + "nr", + "nv", + "ny", + "oc", + "oj", + "om", + "or", + "os", + "pa", + "pi", + "pl", + "ps", + "pt", + "qu", + "rm", + "rn", + "ro", + "ru", + "rw", + "sa", + "sc", + "sd", + "se", + "sg", + "si", + "sk", + "sl", + "sm", + "sn", + "so", + "sq", + "sr", + "ss", + "st", + "su", + "sv", + "sw", + "ta", + "te", + "tg", + "th", + "ti", + "tk", + "tl", + "tn", + "to", + "tr", + "ts", + "tt", + "tw", + "ty", + "ug", + "uk", + "ur", + "uz", + "ve", + "vi", + "vo", + "wa", + "wo", + "xh", + "yi", + "yo", + "za", + "zh", + "zu" + ], + "title": "Language Code", + "type": "string", + "x-note": "Select the ISO 639-1 two-letter code for the language. For example: ''en'' for English, ''es'' for Spanish, ''fr'' for French, etc." + }, + "multiLineTextEntry": { + "$comment": "UI - Multiline text entry without any markup or rich text capability.", + "contentMediaType": "text/plain", + "pattern": "^[\\S\\s]*$", + "type": "string", + "x-note": "Enter multiple lines of plain text. You can use line breaks but no special formatting." + }, + "multiLineTextEntryListMarkdown": { + "$comment": "UI - A Growable List of markdown formatted text fields.", + "default": [], + "format": "multiLineTextEntryListMarkdown", + "items": { + "$ref": "#/definitions/multiLineTextEntryMarkdown", + "maxLength": 10240 }, - "multiLineTextEntryListMarkdown": { - "$comment": "UI - A Growable List of markdown formatted text fields.", - "default": [], - "format": "multiLineTextEntryListMarkdown", - "items": { - "$ref": "#/definitions/multiLineTextEntryMarkdown", - "maxLength": 10240 + "type": "array", + "uniqueItems": true, + "x-note": "Add multiple markdown-formatted text entries. Each entry can include rich formatting and should be unique." + }, + "multiLineTextEntryMarkdown": { + "$comment": "UI - Multiline text entry with Markdown content.", + "contentMediaType": "text/markdown", + "pattern": "^[\\S\\s]*$", + "type": "string", + "x-note": "Use Markdown formatting for rich text. Available formatting:\n- Headers: # for h1, ## for h2, etc.\n- Lists: * or - for bullets, 1. for numbered\n- Emphasis: *italic* or **bold**\n- Links: [text](url)\n- Code: `inline` or ```block```" + }, + "multiSelect": { + "$comment": "UI - Multiselect from the given items.", + "format": "multiSelect", + "type": "array", + "uniqueItems": true, + "x-note": "Select multiple options from the dropdown menu. Multiple choices are allowed." + }, + "nestedQuestions": { + "$comment": "UI - The container for a nested question set.", + "additionalProperties": false, + "format": "nestedQuestions", + "type": "object", + "x-note": "Add multiple questions. Each question should be unique." + }, + "nestedQuestionsList": { + "$comment": "UI - A Growable List of Questions. The contents are an object, that can have any UI elements within.", + "default": [], + "format": "nestedQuestionsList", + "type": "array", + "uniqueItems": true, + "x-note": "Add multiple questions. Each question should be unique." + }, + "radioButtonSelect": { + "$comment": "UI - Radio Button Selection", + "format": "radioButtonSelect", + "type": "string", + "x-note": "Select one option from a list of radio buttons" + }, + "schemaReferenceNonUI": { + "$comment": "NOT UI: used to identify the kind of template document used.", + "format": "path", + "readOnly": true, + "type": "string" + }, + "section": { + "$comment": "UI - Logical Document Sub-Section Break.", + "additionalProperties": false, + "type": "object", + "x-note": "Subsections containing specific details about the proposal." + }, + "segment": { + "$comment": "UI - Logical Document Section Break.", + "additionalProperties": false, + "properties": { + "budget": { + "type": "object" }, - "type": "array", - "uniqueItems": true, - "x-note": "Add multiple markdown-formatted text entries. Each entry can include rich formatting and should be unique." - }, - "multiLineTextEntryMarkdown": { - "$comment": "UI - Multiline text entry with Markdown content.", - "contentMediaType": "text/markdown", - "pattern": "^[\\S\\s]*$", - "type": "string", - "x-note": "Use Markdown formatting for rich text. Available formatting:\n- Headers: # for h1, ## for h2, etc.\n- Lists: * or - for bullets, 1. for numbered\n- Emphasis: *italic* or **bold**\n- Links: [text](url)\n- Code: `inline` or ```block```" - }, - "multiSelect": { - "$comment": "UI - Multiselect from the given items.", - "format": "multiSelect", - "type": "array", - "uniqueItems": true, - "x-note": "Select multiple options from the dropdown menu. Multiple choices are allowed." - }, - "nestedQuestions": { - "$comment": "UI - The container for a nested question set.", - "additionalProperties": false, - "format": "nestedQuestions", - "type": "object", - "x-note": "Add multiple questions. Each question should be unique." - }, - "nestedQuestionsList": { - "$comment": "UI - A Growable List of Questions. The contents are an object, that can have any UI elements within.", - "default": [], - "format": "nestedQuestionsList", - "type": "array", - "uniqueItems": true, - "x-note": "Add multiple questions. Each question should be unique." - }, - "radioButtonSelect": { - "$comment": "UI - Radio Button Selection", - "format": "radioButtonSelect", - "type": "string", - "x-note": "Select one option from a list of radio buttons" + "category_details": { + "type": "object" + }, + "category_questions": { + "type": "object" + }, + "consent_confirmation": { + "type": "object" + }, + "dependencies": { + "type": "object" + }, + "feasibility": { + "type": "object" + }, + "impact": { + "type": "object" + }, + "milestones": { + "type": "object" + }, + "ongoing_projects": { + "type": "object" + }, + "open_source": { + "type": "object" + }, + "problem": { + "type": "object" + }, + "proposer": { + "type": "object" + }, + "self_assessment": { + "type": "object" + }, + "self_assessment_checklist": { + "type": "object" + }, + "solution": { + "type": "object" + }, + "supportingLinks": { + "type": "object" + }, + "team": { + "type": "object" + }, + "theme": { + "type": "object" + }, + "time": { + "type": "object" + }, + "title": { + "type": "object" + }, + "translation": { + "type": "object" + }, + "value": { + "type": "object" + } }, - "schemaReferenceNonUI": { - "$comment": "NOT UI: used to identify the kind of template document used.", - "format": "path", - "readOnly": true, - "type": "string" + "type": "object", + "x-note": "Major sections of the proposal. Each segment contains sections of information grouped together." + }, + "singleGroupedTagSelector": { + "$comment": "UI - A selector where a top level selection, gives a single choice from a list of tags.", + "additionalProperties": true, + "format": "singleGroupedTagSelector", + "type": "object", + "x-note": "Select one option from the dropdown menu. Only one choice is allowed." + }, + "singleLineHttpsURLEntry": { + "$comment": "UI - Single Line text entry for HTTPS Urls.", + "format": "uri", + "maxLength": 255, + "pattern": "^https://[^\\s]+$", + "type": "string", + "x-note": "Must start with ''https://'' and is followed by one or more non-whitespace characters, ending at the end of the string." + }, + "singleLineHttpsURLEntryList": { + "$comment": "UI - A Growable List of HTTPS URLs.", + "default": [], + "format": "singleLineHttpsURLEntryList", + "items": { + "$ref": "#/definitions/singleLineHttpsURLEntry", + "maxLength": 255 }, - "section": { - "$comment": "UI - Logical Document Sub-Section Break.", - "additionalProperties": false, - "type": "object", - "x-note": "Subsections containing specific details about the proposal." + "type": "array", + "uniqueItems": true, + "x-note": "Enter multiple HTTPS URLs. Each URL should be unique and under 1024 characters." + }, + "singleLineTextEntry": { + "$comment": "UI - Single Line text entry without any markup or rich text capability.", + "contentMediaType": "text/plain", + "pattern": "^.*$", + "type": "string", + "x-note": "Enter a single line of text. No formatting, line breaks, or special characters are allowed." + }, + "singleLineTextEntryList": { + "$comment": "UI - A Growable List of single line text (no markup or richtext).", + "default": [], + "format": "singleLineTextEntryList", + "items": { + "$ref": "#/definitions/singleLineTextEntry", + "maxLength": 1024 }, - "segment": { - "$comment": "UI - Logical Document Section Break.", - "additionalProperties": false, - "properties": { - "budget": { - "type": "object" - }, - "category_details": { - "type": "object" - }, - "category_questions": { - "type": "object" - }, - "consent_confirmation": { - "type": "object" - }, - "dependencies": { - "type": "object" - }, - "feasibility": { - "type": "object" - }, - "impact": { - "type": "object" - }, - "milestones": { - "type": "object" - }, - "ongoing_projects": { - "type": "object" - }, - "open_source": { - "type": "object" - }, - "problem": { - "type": "object" - }, - "proposer": { - "type": "object" - }, - "self_assessment": { - "type": "object" - }, - "self_assessment_checklist": { - "type": "object" - }, - "solution": { - "type": "object" - }, - "supportingLinks": { - "type": "object" - }, - "team": { - "type": "object" - }, - "theme": { - "type": "object" - }, - "time": { - "type": "object" + "type": "array", + "uniqueItems": true, + "x-note": "Add multiple single-line text entries. Each entry should be unique and under 1024 characters." + }, + "tagGroup": { + "$comment": "UI - An individual group within a singleGroupedTagSelector.", + "format": "tagGroup", + "pattern": "^.*$", + "type": "string", + "x-note": "Select one option from the dropdown menu. Only one choice is allowed." + }, + "tagInput": { + "$comment": "UI - A tag-style input field for adding/removing items with visual tags", + "default": [], + "format": "tag-input", + "type": "array", + "uniqueItems": true, + "x-note": "Add or remove items that will be displayed as tags with remove buttons" + }, + "tagSelection": { + "$comment": "UI - An individual tag within the group of a singleGroupedTagSelector.", + "format": "tagSelection", + "pattern": "^.*$", + "type": "string", + "x-note": "Select one option from the dropdown menu. Only one choice is allowed." + }, + "tokenValueCardanoADA": { + "$comment": "UI - A Token Value denominated in Cardano ADA.", + "format": "token:cardano:ada", + "type": "integer", + "x-note": "Enter the amount of Cardano ADA to be used in the proposal." + }, + "yesNoChoice": { + "$comment": "UI - A Boolean choice, represented as a Yes/No selection. Yes = true.", + "default": false, + "format": "yesNoChoice", + "type": "boolean", + "x-note": "Select Yes or No." + } + }, + "description": "Schema for the F15 Midnight: Compact DApps", + "maintainers": [ + { + "name": "Catalyst Team", + "url": "https://projectcatalyst.io/" + } + ], + "properties": { + "$schema": { + "$ref": "#/definitions/schemaReferenceNonUI", + "const": "./0199802c-21b4-7d6c-aacd-54aa31fe1e4c.schema.json", + "default": "./0199802c-21b4-7d6c-aacd-54aa31fe1e4c.schema.json" + }, + "agreements": { + "$ref": "#/definitions/segment", + "properties": { + "consent_confirmation": { + "$ref": "#/definitions/section", + "properties": { + "terms_and_conditions": { + "$ref": "#/definitions/agreementConfirmation", + "description": "I confirm that I have read, understand and shall adhere to the [Project Catalyst Platform Terms of Use](https://docs.projectcatalyst.io/current-fund/fund-basics/project-catalyst-terms-and-conditions/project-catalyst-platform-terms-of-use), [Project Catalyst Platform Privacy Policy](https://docs.projectcatalyst.io/current-fund/fund-basics/project-catalyst-terms-and-conditions/project-catalyst-platform-privacy-policy), [Project Catalyst Terms and Conditions – Midnight Compact DApps Submissions](https://docs.projectcatalyst.io/current-fund/fund-basics/project-catalyst-terms-and-conditions/project-catalyst-terms-and-conditions-midnight-compact-dapps-submissions), [Fund Rules](https://docs.projectcatalyst.io/current-fund/fund-basics/fund-rules), [Privacy Policy](https://docs.projectcatalyst.io/current-fund/fund-basics/project-catalyst-terms-and-conditions/catalyst-fc-privacy-policy). I understand that providing accurate and truthful information is essential for my proposal to remain eligible to participate in the current Fund.", + "title": "I Agree" + } }, - "title": { - "type": "object" + "required": [ + "terms_and_conditions" + ], + "title": "Consent & Confirmation", + "x-order": [ + "terms_and_conditions" + ] + }, + "ongoing_projects": { + "$ref": "#/definitions/section", + "properties": { + "has_ongoing_projects": { + "$ref": "#/definitions/yesNoChoice", + "description": "Do you have not yet completed projects?", + "title": "Have you previously submitted a project in Catalyst that was funded and is not yet completed?", + "x-placeholder": "Select if you or co-proposers have any ongoing projects" + }, + "projects": { + "$ref": "#/definitions/tagInput", + "description": "If you answered “yes” to the previous question, please select first how many there are in total and then list all IDs for any projects you are currently involved in that have not been completed yet.", + "items": { + "properties": { + "project_id": { + "description": "Seven digit project ID from Catalyst website", + "maxLength": 7, + "minLength": 7, + "pattern": "^[0-9]{7}$", + "title": "Project ID", + "type": "string" + } + }, + "required": [ + "project_id" + ], + "type": "object" + }, + "maxItems": 15, + "minItems": 0, + "title": "Project ID''s", + "x-guidance": "You can find a seven digit project ID on each original proposal page listed on Catalyst website. [Search for yours here.](https://projectcatalyst.io/search)", + "x-subsection": false + } }, - "translation": { - "type": "object" + "required": [ + "has_ongoing_projects", + "projects" + ], + "title": "Ongoing Projects", + "x-guidance": "Important Notice\n\nPlease review [Fund Rules](https://docs.projectcatalyst.io/current-fund/fund-basics/fund-rules) carefully, as eligibility to participate may be affected by the status of your ongoing projects. Providing accurate information is mandatory, failure to do so will result in your proposal being deemed ineligible in the current Fund.", + "x-order": [ + "has_ongoing_projects", + "projects" + ] + } + }, + "title": "Required Acknowledgements", + "x-icon": "double_check", + "x-order": [ + "ongoing_projects", + "consent_confirmation" + ] + }, + "campaign_category": { + "$ref": "#/definitions/segment", + "description": "Determine the eligibility of the proposal for this category", + "properties": { + "category_details": { + "$ref": "#/definitions/section", + "properties": { + "details": { + "title": "Selected Category", + "type": "object" + } }, - "value": { - "type": "object" - } + "title": "Selected Category" }, - "type": "object", - "x-note": "Major sections of the proposal. Each segment contains sections of information grouped together." - }, - "singleGroupedTagSelector": { - "$comment": "UI - A selector where a top level selection, gives a single choice from a list of tags.", - "additionalProperties": true, - "format": "singleGroupedTagSelector", - "type": "object", - "x-note": "Select one option from the dropdown menu. Only one choice is allowed." - }, - "singleLineHttpsURLEntry": { - "$comment": "UI - Single Line text entry for HTTPS Urls.", - "format": "uri", - "maxLength": 255, - "pattern": "^https://[^\\s]+$", - "type": "string", - "x-note": "Must start with ''https://'' and is followed by one or more non-whitespace characters, ending at the end of the string." + "category_questions": { + "$ref": "#/definitions/section", + "description": "Answer the following questions to determine the eligibility of your proposal.", + "properties": { + "build_goal": { + "$ref": "#/definitions/multiLineTextEntryMarkdown", + "description": "Please provide your rationale.", + "maxLength": 10240, + "minLength": 200, + "title": "What exactly will you build? List the Compact contract(s) and key functions/proofs, the demo UI flow, Lace (Midnight) wallet integration, and your basic test plan.", + "x-guidance": "Usefulness (Pattern & Necessity)\n\n- Clear mapping from problem to data-protection pattern (e.g., selective disclosure for KYC, private votes, confidential asset ops).\n- Why this requires Midnight + Compact versus a public ledger.\n- Novelty relative to existing examples; what new building block it adds.", + "x-placeholder": "Please provide your rationale." + }, + "dapp_purpose": { + "$ref": "#/definitions/multiLineTextEntryMarkdown", + "description": "Please provide your rationale.", + "maxLength": 10240, + "minLength": 200, + "title": "What is useful about your DApp within one of the specified industry or enterprise verticals?", + "x-guidance": "General Guidance\n\n- Be concise; prefer bullets over prose.\n- This is a code-first PoC, not a company pitch. No pitch decks/marketing.\n- Include concrete details (e.g., file paths, API/entrypoints, test commands).\n- If helpful, include a simple architecture diagram or sequence.\n- Keep within 3 months and 3 milestones; each milestone needs deliverables + acceptance criteria + evidence.\n- Use correct nomenclature (Compact ≠ TypeScript; privacy-enhancing/data protection).", + "x-placeholder": "Please provide your rationale." + }, + "developer_reuse": { + "$ref": "#/definitions/multiLineTextEntryMarkdown", + "description": "Please provide your rationale.", + "maxLength": 10240, + "minLength": 200, + "title": "How will other developers learn from and reuse your repo? Describe repo structure, README contents, docs/tutorials, test instructions, and extension points. Which developer personas benefit, and how will you gauge impact (forks, stars, issues, remixes)?", + "x-guidance": "Developer Value\n\n- Identified developer personas (e.g., DApp devs, zk-curious, integrators) and how this example helps them.\n- Potential to attract new builders (clarity, reusability) and measurable signals (forks, stars, issues, downstream examples)", + "x-placeholder": "Please provide your rationale." + } + }, + "required": [ + "dapp_purpose", + "build_goal", + "developer_reuse" + ], + "title": "Category Questions", + "x-guidance": "General Guidance\n\nIn consideration of the Reviewers assessing your proposal, be concise and use bulleted lists where possible. ", + "x-order": [ + "dapp_purpose", + "build_goal", + "developer_reuse" + ] + } }, - "singleLineHttpsURLEntryList": { - "$comment": "UI - A Growable List of HTTPS URLs.", - "default": [], - "format": "singleLineHttpsURLEntryList", - "items": { - "$ref": "#/definitions/singleLineHttpsURLEntry", - "maxLength": 255 + "title": "Campaign Category", + "x-icon": "top-bar", + "x-order": [ + "category_details", + "category_questions" + ] + }, + "details": { + "$ref": "#/definitions/segment", + "properties": { + "feasibility": { + "$ref": "#/definitions/section", + "properties": { + "feasibility": { + "$ref": "#/definitions/multiLineTextEntryMarkdown", + "description": "What is your capability to deliver your project with high levels of trust and accountability? How do you intend to validate if your approach is feasible?", + "maxLength": 10240, + "minLength": 200, + "title": "What is your capability to deliver your project with high levels of trust and accountability? How do you intend to validate if your approach is feasible?", + "x-guidance": "Please describe your existing capabilities that demonstrate how and why you believe you’re best suited to deliver this project?\nPlease include the steps or processes that demonstrate that you can be trusted to manage funds properly." + } + }, + "required": [ + "feasibility" + ], + "title": "Capabilities & Feasibility" }, - "type": "array", - "uniqueItems": true, - "x-note": "Enter multiple HTTPS URLs. Each URL should be unique and under 1024 characters." - }, - "singleLineTextEntry": { - "$comment": "UI - Single Line text entry without any markup or rich text capability.", - "contentMediaType": "text/plain", - "pattern": "^.*$", - "type": "string", - "x-note": "Enter a single line of text. No formatting, line breaks, or special characters are allowed." - }, - "singleLineTextEntryList": { - "$comment": "UI - A Growable List of single line text (no markup or richtext).", - "default": [], - "format": "singleLineTextEntryList", - "items": { - "$ref": "#/definitions/singleLineTextEntry", - "maxLength": 1024 + "impact": { + "$ref": "#/definitions/section", + "properties": { + "impact": { + "$ref": "#/definitions/multiLineTextEntryMarkdown", + "description": "Please define the positive impact your project will have on Midnight ecosystem", + "maxLength": 10240, + "minLength": 200, + "title": "Please define the positive impact your project will have on Midnight ecosystem", + "x-guidance": "Please include here a description of how you intend to measure impact (whether quantitative or qualitative) and how and with whom you will share your outputs:\n\n- In what way will the success of your project bring value to the Midnight ecosystem?\n- How will you measure this impact?\n- How will you share the outputs and opportunities that result from your project?" + } + }, + "required": [ + "impact" + ], + "title": "Impact" }, - "type": "array", - "uniqueItems": true, - "x-note": "Add multiple single-line text entries. Each entry should be unique and under 1024 characters." - }, - "tagGroup": { - "$comment": "UI - An individual group within a singleGroupedTagSelector.", - "format": "tagGroup", - "pattern": "^.*$", - "type": "string", - "x-note": "Select one option from the dropdown menu. Only one choice is allowed." - }, - "tagInput": { - "$comment": "UI - A tag-style input field for adding/removing items with visual tags", - "default": [], - "format": "tag-input", - "type": "array", - "uniqueItems": true, - "x-note": "Add or remove items that will be displayed as tags with remove buttons" - }, - "tagSelection": { - "$comment": "UI - An individual tag within the group of a singleGroupedTagSelector.", - "format": "tagSelection", - "pattern": "^.*$", - "type": "string", - "x-note": "Select one option from the dropdown menu. Only one choice is allowed." - }, - "tokenValueCardanoADA": { - "$comment": "UI - A Token Value denominated in Cardano ADA.", - "format": "token:cardano:ada", - "type": "integer", - "x-note": "Enter the amount of Cardano ADA to be used in the proposal." + "solution": { + "$ref": "#/definitions/section", + "properties": { + "solution": { + "$ref": "#/definitions/multiLineTextEntryMarkdown", + "description": "Please describe your proposed solution and how it addresses the problem", + "maxLength": 10240, + "minLength": 200, + "title": "Please describe your proposed solution and how it addresses the problem", + "x-guidance": "How you write this section will depend on what type of proposal you are writing. You might want to include details on:\n\n- How do you perceive the problem you are solving?\n- What are your reasons for approaching it in the way that you have?\n- Who will your project engage?\n\n Explain what is unique about your solution, who will benefit, and why this is important to Midnight." + } + }, + "required": [ + "solution" + ], + "title": "Solution" + } }, - "yesNoChoice": { - "$comment": "UI - A Boolean choice, represented as a Yes/No selection. Yes = true.", - "default": false, - "format": "yesNoChoice", - "type": "boolean", - "x-note": "Select Yes or No." - } + "required": [ + "solution", + "impact", + "feasibility" + ], + "title": "Your Project and Solution", + "x-icon": "chart-pie", + "x-order": [ + "solution", + "impact", + "feasibility" + ] }, - "description": "Schema for the F15 Midnight: Compact DApps", - "maintainers": [ - { - "name": "Catalyst Team", - "url": "https://projectcatalyst.io/" - } - ], - "properties": { - "$schema": { - "$ref": "#/definitions/schemaReferenceNonUI", - "const": "./0199802c-21b4-7d6c-aacd-54aa31fe1e4c.schema.json", - "default": "./0199802c-21b4-7d6c-aacd-54aa31fe1e4c.schema.json" - }, - "agreements": { - "$ref": "#/definitions/segment", - "properties": { - "consent_confirmation": { - "$ref": "#/definitions/section", - "properties": { - "terms_and_conditions": { - "$ref": "#/definitions/agreementConfirmation", - "description": "I confirm that I have read, understand and shall adhere to the [Project Catalyst Platform Terms of Use](https://docs.projectcatalyst.io/current-fund/fund-basics/project-catalyst-terms-and-conditions/project-catalyst-platform-terms-of-use), [Project Catalyst Platform Privacy Policy](https://docs.projectcatalyst.io/current-fund/fund-basics/project-catalyst-terms-and-conditions/project-catalyst-platform-privacy-policy), [Project Catalyst Terms and Conditions – Midnight Compact DApps Submissions](https://docs.projectcatalyst.io/current-fund/fund-basics/project-catalyst-terms-and-conditions/project-catalyst-terms-and-conditions-midnight-compact-dapps-submissions), [Fund Rules](https://docs.projectcatalyst.io/current-fund/fund-basics/fund-rules), [Privacy Policy](https://docs.projectcatalyst.io/current-fund/fund-basics/project-catalyst-terms-and-conditions/catalyst-fc-privacy-policy). I understand that providing accurate and truthful information is essential for my proposal to remain eligible to participate in the current Fund.", - "title": "I Agree" - } - }, - "required": [ - "terms_and_conditions" - ], - "title": "Consent & Confirmation", - "x-order": [ - "terms_and_conditions" - ] - }, - "ongoing_projects": { - "$ref": "#/definitions/section", - "properties": { - "has_ongoing_projects": { - "$ref": "#/definitions/yesNoChoice", - "description": "Do you have not yet completed projects?", - "title": "Have you previously submitted a project in Catalyst that was funded and is not yet completed?", - "x-placeholder": "Select if you or co-proposers have any ongoing projects" - }, - "projects": { - "$ref": "#/definitions/tagInput", - "description": "If you answered “yes” to the previous question, please select first how many there are in total and then list all IDs for any projects you are currently involved in that have not been completed yet.", - "items": { - "properties": { - "project_id": { - "description": "Seven digit project ID from Catalyst website", - "maxLength": 7, - "minLength": 7, - "pattern": "^[0-9]{7}$", - "title": "Project ID", - "type": "string" - } + "milestones": { + "$ref": "#/definitions/segment", + "properties": { + "milestones": { + "$ref": "#/definitions/section", + "description": "What are the key milestones you need to achieve in order to complete your project successfully?", + "properties": { + "milestone_list": { + "description": "Please refer to the Guidance card on the right for details", + "items": { + "properties": { + "acceptance_criteria": { + "$ref": "#/definitions/multiLineTextEntryMarkdown", + "description": "Specific conditions that must be met", + "maxLength": 2000, + "minLength": 200, + "title": "Acceptance Criteria", + "x-placeholder": "Define the criteria for success in this milestone" + }, + "cost": { + "$ref": "#/definitions/currency", + "description": "The cost of this milestone in $USDM", + "format": "token:usdm", + "multipleOf": 1, + "title": "Cost", + "x-placeholder": "Enter the cost associated with this milestone" }, - "required": [ - "project_id" - ], - "type": "object" + "delivery_month": { + "$ref": "#/definitions/durationInMonths", + "description": "The month when this milestone will be delivered", + "maximum": 3, + "minimum": 1, + "title": "Delivery Month", + "x-placeholder": "Enter the month when this milestone is expected to be delivered" + }, + "evidence": { + "$ref": "#/definitions/multiLineTextEntryMarkdown", + "description": "How you will demonstrate achievement", + "maxLength": 2000, + "minLength": 100, + "title": "Evidence of Completion", + "x-placeholder": "Specify the evidence that will demonstrate this milestone is complete" + }, + "outputs": { + "$ref": "#/definitions/multiLineTextEntryMarkdown", + "description": "What will be delivered in this milestone", + "maxLength": 2000, + "minLength": 200, + "title": "Milestone Outputs", + "x-placeholder": "Describe the deliverables or outcomes for this milestone" + }, + "progress": { + "$ref": "#/definitions/dropDownSingleSelect", + "description": "Current status of the milestone", + "enum": [ + "10 %", + "20 %", + "30 %", + "40 %", + "50 %", + "60 %", + "70 %", + "80 %", + "90 %", + "100 %" + ], + "title": "Progress", + "x-placeholder": "Enter the percentage of overall project completion progress for this milestone (10-100%)" + }, + "title": { + "$ref": "#/definitions/singleLineTextEntry", + "description": "A clear, concise title for this milestone", + "maxLength": 100, + "title": "Milestone Title", + "x-placeholder": "Enter the title for this milestone" + } }, - "maxItems": 15, - "minItems": 0, - "title": "Project ID''s", - "x-guidance": "You can find a seven digit project ID on each original proposal page listed on Catalyst website. [Search for yours here.](https://projectcatalyst.io/search)", - "x-subsection": false - } - }, - "required": [ - "has_ongoing_projects", - "projects" - ], - "title": "Ongoing Projects", - "x-guidance": "Important Notice\n\nPlease review [Fund Rules](https://docs.projectcatalyst.io/current-fund/fund-basics/fund-rules) carefully, as eligibility to participate may be affected by the status of your ongoing projects. Providing accurate information is mandatory, failure to do so will result in your proposal being deemed ineligible in the current Fund.", - "x-order": [ - "has_ongoing_projects", - "projects" - ] - } - }, - "title": "Required Acknowledgements", - "x-icon": "double_check", - "x-order": [ - "ongoing_projects", - "consent_confirmation" - ] + "required": [ + "title", + "outputs", + "acceptance_criteria", + "evidence", + "delivery_month", + "cost" + ], + "title": "Milestone", + "type": "object", + "x-guidance": "Please follow this [guideline here](https://docs.projectcatalyst.io/current-fund/project-onboarding/milestone-based-proposals) to understand how to build your indicative milestones. Each milestone must have declared:\n\nA. **Milestone outputs**\n - What will be delivered\n\nB. **Acceptance criteria**\n - What conditions must be met\n\nC. **Evidence of completion**\n - How you will prove the milestone is complete", + "x-subsection": true + }, + "maxItems": 3, + "minItems": 3, + "title": "What are the key milestones you need to achieve in order to complete your project successfully?", + "type": "array", + "x-guidance": "For Midnight Grant Amounts 2 milestones, plus the final one including Project Close-out Report and Video, must be included (**3 milestones in total**)\n\nExpected Project outcomes:\n\n- M1: Publish public repository of required assets + the smart contract\n\n- M2: Connect smart contract to user interface (UI), make assets available into the repository, and have documentation\n\n- M3: Proof of Concept UI demonstrated in the Project Completion Video, testing suite complete, and Project Completion Report" + } + }, + "required": [ + "milestone_list" + ], + "title": "Project Milestones", + "x-guidance": "Please note milestones are only indicative during the proposal submission stage. Each funded project will formalize a milestone schedule and amounts during the onboarding stage." + } }, - "campaign_category": { - "$ref": "#/definitions/segment", - "description": "Determine the eligibility of the proposal for this category", - "properties": { - "category_details": { - "$ref": "#/definitions/section", - "properties": { - "details": { - "title": "Selected Category", - "type": "object" - } - }, - "title": "Selected Category" + "title": "Milestones", + "x-icon": "flag", + "x-order": [ + "milestones" + ] + }, + "pitch": { + "$ref": "#/definitions/segment", + "properties": { + "budget": { + "$ref": "#/definitions/section", + "properties": { + "costs": { + "$ref": "#/definitions/multiLineTextEntryMarkdown", + "description": "Please provide a cost breakdown of the proposed work and resources", + "maxLength": 10240, + "minLength": 200, + "title": "Please provide a cost breakdown of the proposed work and resources", + "x-guidance": "Give an account of how the work is budgeted. It may be helpful to refer to your plan and timeline, list the resources you will need at each stage, and what they cost.\n\nIt is your responsibility to properly manage the funds provided. Make sure to reference [Fund Rules](https://docs.projectcatalyst.io/current-fund/fund-basics/fund-rules) to understand eligibility around costs." + } }, - "category_questions": { - "$ref": "#/definitions/section", - "description": "Answer the following questions to determine the eligibility of your proposal.", - "properties": { - "build_goal": { - "$ref": "#/definitions/multiLineTextEntryMarkdown", - "description": "Please provide your rationale.", - "maxLength": 10240, - "minLength": 200, - "title": "What exactly will you build? List the Compact contract(s) and key functions/proofs, the demo UI flow, Lace (Midnight) wallet integration, and your basic test plan.", - "x-guidance": "Usefulness (Pattern & Necessity)\n\n- Clear mapping from problem to data-protection pattern (e.g., selective disclosure for KYC, private votes, confidential asset ops).\n- Why this requires Midnight + Compact versus a public ledger.\n- Novelty relative to existing examples; what new building block it adds.", - "x-placeholder": "Please provide your rationale." - }, - "dapp_purpose": { - "$ref": "#/definitions/multiLineTextEntryMarkdown", - "description": "Please provide your rationale.", - "maxLength": 10240, - "minLength": 200, - "title": "What is useful about your DApp within one of the specified industry or enterprise verticals?", - "x-guidance": "General Guidance\n\n- Be concise; prefer bullets over prose.\n- This is a code-first PoC, not a company pitch. No pitch decks/marketing.\n- Include concrete details (e.g., file paths, API/entrypoints, test commands).\n- If helpful, include a simple architecture diagram or sequence.\n- Keep within 3 months and 3 milestones; each milestone needs deliverables + acceptance criteria + evidence.\n- Use correct nomenclature (Compact ≠ TypeScript; privacy-enhancing/data protection).", - "x-placeholder": "Please provide your rationale." - }, - "developer_reuse": { - "$ref": "#/definitions/multiLineTextEntryMarkdown", - "description": "Please provide your rationale.", - "maxLength": 10240, - "minLength": 200, - "title": "How will other developers learn from and reuse your repo? Describe repo structure, README contents, docs/tutorials, test instructions, and extension points. Which developer personas benefit, and how will you gauge impact (forks, stars, issues, remixes)?", - "x-guidance": "Developer Value\n\n- Identified developer personas (e.g., DApp devs, zk-curious, integrators) and how this example helps them.\n- Potential to attract new builders (clarity, reusability) and measurable signals (forks, stars, issues, downstream examples)", - "x-placeholder": "Please provide your rationale." - } - }, - "required": [ - "dapp_purpose", - "build_goal", - "developer_reuse" - ], - "title": "Category Questions", - "x-guidance": "General Guidance\n\nIn consideration of the Reviewers assessing your proposal, be concise and use bulleted lists where possible. ", - "x-order": [ - "dapp_purpose", - "build_goal", - "developer_reuse" - ] - } + "required": [ + "costs" + ], + "title": "Budget & Costs" }, - "title": "Campaign Category", - "x-icon": "top-bar", - "x-order": [ - "category_details", - "category_questions" - ] + "team": { + "$ref": "#/definitions/section", + "properties": { + "who": { + "$ref": "#/definitions/multiLineTextEntryMarkdown", + "description": "Who is participating in the project team and what are their roles?", + "maxLength": 10240, + "minLength": 100, + "title": "Who is participating in the project team and what are their roles?", + "x-guidance": "List your team and collaborators, include links to their Linkedin profiles (or similar) and state what aspect of the project each participant will undertake.\n\nIf additional team members need to be recruited, please state what skills gaps you are looking to fill so readers understand what other roles will be needed to complete the project.\n\nYou are expected to have already engaged the relevant members of the organizations referenced so you have their commitment and capacity to support the project. If you have not taken any steps to this effect, it is likely that the resources will not be available if you are approved for funding, which can jeopardize the project before it has even begun.\n\nYour proposal will be publicly available, so make sure to obtain any consent required before including confidential or third party information.\n\nAll applicants must disclose their project team’s roles and scope of services across all submitted proposals. Failure to disclose this information may lead to disqualification from the current fund round." + } + }, + "required": [ + "who" + ], + "title": "Project Team" + }, + "value": { + "$ref": "#/definitions/section", + "properties": { + "note": { + "$ref": "#/definitions/multiLineTextEntryMarkdown", + "description": "How does the cost of the project represent value for the Midnight ecosystem?", + "maxLength": 10240, + "minLength": 200, + "title": "How does the cost of the project represent value for the Midnight ecosystem?", + "x-guidance": "Provide a compelling reason about how your project represents a good use of funds and justify how it will create value for the Midnight ecosystem." + } + }, + "required": [ + "note" + ], + "title": "Value for Money" + } }, - "details": { - "$ref": "#/definitions/segment", - "properties": { - "feasibility": { - "$ref": "#/definitions/section", - "properties": { - "feasibility": { - "$ref": "#/definitions/multiLineTextEntryMarkdown", - "description": "What is your capability to deliver your project with high levels of trust and accountability? How do you intend to validate if your approach is feasible?", - "maxLength": 10240, - "minLength": 200, - "title": "What is your capability to deliver your project with high levels of trust and accountability? How do you intend to validate if your approach is feasible?", - "x-guidance": "Please describe your existing capabilities that demonstrate how and why you believe you’re best suited to deliver this project?\nPlease include the steps or processes that demonstrate that you can be trusted to manage funds properly." - } + "required": [ + "team", + "budget", + "value" + ], + "title": "Final Pitch", + "x-icon": "presentation-chart-line", + "x-order": [ + "team", + "budget", + "value" + ] + }, + "self_assessment": { + "$ref": "#/definitions/segment", + "properties": { + "self_assessment_checklist": { + "$ref": "#/definitions/section", + "properties": { + "developer_experience": { + "$ref": "#/definitions/agreementConfirmation", + "description": "Developer Experience Focus:", + "title": "I confirm that the proposal clearly defines which part of the developer journey it improves and how it makes building on Midnight easier and more productive." }, - "required": [ - "feasibility" - ], - "title": "Capabilities & Feasibility" - }, - "impact": { - "$ref": "#/definitions/section", - "properties": { - "impact": { - "$ref": "#/definitions/multiLineTextEntryMarkdown", - "description": "Please define the positive impact your project will have on Midnight ecosystem", - "maxLength": 10240, - "minLength": 200, - "title": "Please define the positive impact your project will have on Midnight ecosystem", - "x-guidance": "Please include here a description of how you intend to measure impact (whether quantitative or qualitative) and how and with whom you will share your outputs:\n\n- In what way will the success of your project bring value to the Midnight ecosystem?\n- How will you measure this impact?\n- How will you share the outputs and opportunities that result from your project?" - } + "open_source_commitment": { + "$ref": "#/definitions/agreementConfirmation", + "description": "Open Source Commitment:", + "title": "I confirm that the proposal explicitly states the chosen permissive open-source license (e.g., MIT, Apache 2.0) and commits to a public code repository." }, - "required": [ - "impact" - ], - "title": "Impact" - }, - "solution": { - "$ref": "#/definitions/section", - "properties": { - "solution": { - "$ref": "#/definitions/multiLineTextEntryMarkdown", - "description": "Please describe your proposed solution and how it addresses the problem", - "maxLength": 10240, - "minLength": 200, - "title": "Please describe your proposed solution and how it addresses the problem", - "x-guidance": "How you write this section will depend on what type of proposal you are writing. You might want to include details on:\n\n- How do you perceive the problem you are solving?\n- What are your reasons for approaching it in the way that you have?\n- Who will your project engage?\n\n Explain what is unique about your solution, who will benefit, and why this is important to Midnight." - } + "quality_documentation": { + "$ref": "#/definitions/agreementConfirmation", + "description": "High-Quality Documentation:", + "title": "I confirm that a plan for creating and maintaining clear, comprehensive documentation is a core part of the proposal''s scope." }, - "required": [ - "solution" - ], - "title": "Solution" - } - }, - "required": [ - "solution", - "impact", - "feasibility" - ], - "title": "Your Project and Solution", - "x-icon": "chart-pie", - "x-order": [ - "solution", - "impact", - "feasibility" - ] - }, - "milestones": { - "$ref": "#/definitions/segment", - "properties": { - "milestones": { - "$ref": "#/definitions/section", - "description": "What are the key milestones you need to achieve in order to complete your project successfully?", - "properties": { - "milestone_list": { - "description": "Please refer to the Guidance card on the right for details", - "items": { - "properties": { - "acceptance_criteria": { - "$ref": "#/definitions/multiLineTextEntryMarkdown", - "description": "Specific conditions that must be met", - "maxLength": 2000, - "minLength": 200, - "title": "Acceptance Criteria", - "x-placeholder": "Define the criteria for success in this milestone" - }, - "cost": { - "$ref": "#/definitions/currency", - "description": "The cost of this milestone in $USDM", - "format": "token:usdm", - "multipleOf": 1, - "title": "Cost", - "x-placeholder": "Enter the cost associated with this milestone" - }, - "delivery_month": { - "$ref": "#/definitions/durationInMonths", - "description": "The month when this milestone will be delivered", - "maximum": 3, - "minimum": 1, - "title": "Delivery Month", - "x-placeholder": "Enter the month when this milestone is expected to be delivered" - }, - "evidence": { - "$ref": "#/definitions/multiLineTextEntryMarkdown", - "description": "How you will demonstrate achievement", - "maxLength": 2000, - "minLength": 100, - "title": "Evidence of Completion", - "x-placeholder": "Specify the evidence that will demonstrate this milestone is complete" - }, - "outputs": { - "$ref": "#/definitions/multiLineTextEntryMarkdown", - "description": "What will be delivered in this milestone", - "maxLength": 2000, - "minLength": 200, - "title": "Milestone Outputs", - "x-placeholder": "Describe the deliverables or outcomes for this milestone" - }, - "progress": { - "$ref": "#/definitions/dropDownSingleSelect", - "description": "Current status of the milestone", - "enum": [ - "10 %", - "20 %", - "30 %", - "40 %", - "50 %", - "60 %", - "70 %", - "80 %", - "90 %", - "100 %" - ], - "title": "Progress", - "x-placeholder": "Enter the percentage of overall project completion progress for this milestone (10-100%)" - }, - "title": { - "$ref": "#/definitions/singleLineTextEntry", - "description": "A clear, concise title for this milestone", - "maxLength": 100, - "title": "Milestone Title", - "x-placeholder": "Enter the title for this milestone" - } - }, - "required": [ - "title", - "outputs", - "acceptance_criteria", - "evidence", - "delivery_month", - "cost" - ], - "title": "Milestone", - "type": "object", - "x-guidance": "Please follow this [guideline here](https://docs.projectcatalyst.io/current-fund/project-onboarding/milestone-based-proposals) to understand how to build your indicative milestones. Each milestone must have declared:\n\nA. **Milestone outputs**\n - What will be delivered\n\nB. **Acceptance criteria**\n - What conditions must be met\n\nC. **Evidence of completion**\n - How you will prove the milestone is complete", - "x-subsection": true - }, - "maxItems": 3, - "minItems": 3, - "title": "What are the key milestones you need to achieve in order to complete your project successfully?", - "type": "array", - "x-guidance": "For Midnight Grant Amounts 2 milestones, plus the final one including Project Close-out Report and Video, must be included (**3 milestones in total**)\n\nExpected Project outcomes:\n\n- M1: Publish public repository of required assets + the smart contract\n\n- M2: Connect smart contract to user interface (UI), make assets available into the repository, and have documentation\n\n- M3: Proof of Concept UI demonstrated in the Project Completion Video, testing suite complete, and Project Completion Report" - } + "realistic_scope": { + "$ref": "#/definitions/agreementConfirmation", + "description": "Realistic Scope:", + "title": "I confirm that the budget and timeline (3 months) are realistic for delivering the proposed tool or resource." }, - "required": [ - "milestone_list" - ], - "title": "Project Milestones", - "x-guidance": "Please note milestones are only indicative during the proposal submission stage. Each funded project will formalize a milestone schedule and amounts during the onboarding stage." - } - }, - "title": "Milestones", - "x-icon": "flag", - "x-order": [ - "milestones" - ] - }, - "pitch": { - "$ref": "#/definitions/segment", - "properties": { - "budget": { - "$ref": "#/definitions/section", - "properties": { - "costs": { - "$ref": "#/definitions/multiLineTextEntryMarkdown", - "description": "Please provide a cost breakdown of the proposed work and resources", - "maxLength": 10240, - "minLength": 200, - "title": "Please provide a cost breakdown of the proposed work and resources", - "x-guidance": "Give an account of how the work is budgeted. It may be helpful to refer to your plan and timeline, list the resources you will need at each stage, and what they cost.\n\nIt is your responsibility to properly manage the funds provided. Make sure to reference [Fund Rules](https://docs.projectcatalyst.io/current-fund/fund-basics/fund-rules) to understand eligibility around costs." - } + "strategic_fit": { + "$ref": "#/definitions/agreementConfirmation", + "description": "Strategic Fit:", + "title": "I confirm that the proposal clearly provides a basic prototype reference application for one of the areas of interest." }, - "required": [ - "costs" - ], - "title": "Budget & Costs" + "verifiable_builder_credentials": { + "$ref": "#/definitions/agreementConfirmation", + "description": "Verifiable Builder Credentials:", + "title": "I confirm that the team provides evidence of their technical ability and experience in creating developer tools or high-quality technical content (e.g., GitHub, portfolio)." + } }, - "team": { - "$ref": "#/definitions/section", - "properties": { - "who": { - "$ref": "#/definitions/multiLineTextEntryMarkdown", - "description": "Who is participating in the project team and what are their roles?", - "maxLength": 10240, - "minLength": 100, - "title": "Who is participating in the project team and what are their roles?", - "x-guidance": "List your team and collaborators, include links to their Linkedin profiles (or similar) and state what aspect of the project each participant will undertake.\n\nIf additional team members need to be recruited, please state what skills gaps you are looking to fill so readers understand what other roles will be needed to complete the project.\n\nYou are expected to have already engaged the relevant members of the organizations referenced so you have their commitment and capacity to support the project. If you have not taken any steps to this effect, it is likely that the resources will not be available if you are approved for funding, which can jeopardize the project before it has even begun.\n\nYour proposal will be publicly available, so make sure to obtain any consent required before including confidential or third party information.\n\nAll applicants must disclose their project team’s roles and scope of services across all submitted proposals. Failure to disclose this information may lead to disqualification from the current fund round." - } + "required": [ + "strategic_fit", + "developer_experience", + "open_source_commitment", + "verifiable_builder_credentials", + "quality_documentation", + "realistic_scope" + ], + "title": "Self-Assessment Checklist", + "x-guidance": "Use this checklist to ensure your proposal meets all foundational and content requirements before submission.", + "x-order": [ + "strategic_fit", + "developer_experience", + "open_source_commitment", + "verifiable_builder_credentials", + "quality_documentation", + "realistic_scope" + ] + } + }, + "required": [ + "self_assessment_checklist" + ], + "title": "Self-Assessment", + "x-icon": "shield-check", + "x-order": [ + "self_assessment_checklist" + ] + }, + "setup": { + "$ref": "#/definitions/segment", + "description": "Proposal title", + "properties": { + "proposer": { + "$ref": "#/definitions/section", + "description": "Proposer Information", + "properties": { + "applicant": { + "$ref": "#/definitions/singleLineTextEntry", + "description": "Name and surname of main applicant", + "maxLength": 100, + "minLength": 2, + "title": "Applicant name and surname", + "x-guidance": "The applicant is the individual or entity responsible for the proposed project.", + "x-placeholder": "Enter the full name of the applicant submitting the proposal" }, - "required": [ - "who" - ], - "title": "Project Team" + "type": { + "$ref": "#/definitions/radioButtonSelect", + "description": "Are you submitting this proposal as an individual or as an entity (whether formally incorporated or not)", + "enum": [ + "Individual", + "Entity (Incorporated)", + "Entity (Not Incorporated)" + ], + "title": "Are you submitting this proposal as an individual or as an entity (whether formally incorporated or not)?", + "x-guidance": "Please select from one of the following:\n\n1. Individual\n2. Entity (Incorporated)\n3. Entity (Not Incorporated)" + } }, - "value": { - "$ref": "#/definitions/section", - "properties": { - "note": { - "$ref": "#/definitions/multiLineTextEntryMarkdown", - "description": "How does the cost of the project represent value for the Midnight ecosystem?", - "maxLength": 10240, - "minLength": 200, - "title": "How does the cost of the project represent value for the Midnight ecosystem?", - "x-guidance": "Provide a compelling reason about how your project represents a good use of funds and justify how it will create value for the Midnight ecosystem." - } - }, - "required": [ - "note" - ], - "title": "Value for Money" - } + "required": [ + "applicant", + "type" + ], + "title": "Applicant", + "x-order": [ + "applicant", + "type" + ] }, - "required": [ - "team", - "budget", - "value" - ], - "title": "Final Pitch", - "x-icon": "presentation-chart-line", - "x-order": [ - "team", - "budget", - "value" - ] + "title": { + "$ref": "#/definitions/section", + "description": "Proposal title", + "properties": { + "title": { + "$ref": "#/definitions/singleLineTextEntry", + "description": "**Proposal title**\n\nPlease note we suggest you use no more than 60 characters for your proposal title so that it can be easily viewed in the voting app.", + "maxLength": 60, + "minLength": 3, + "title": "Please provide your proposal title", + "x-guidance": "This is the first detail about your proposal that voters see so it should clearly express what the proposal is about. A clear, unambiguous, and concise title is very important.", + "x-placeholder": "Summarize your proposal in 60 characters or less" + } + }, + "required": [ + "title" + ], + "title": "Proposal title" + } }, - "self_assessment": { - "$ref": "#/definitions/segment", - "properties": { - "self_assessment_checklist": { - "$ref": "#/definitions/section", - "properties": { - "developer_experience": { - "$ref": "#/definitions/agreementConfirmation", - "description": "Developer Experience Focus:", - "title": "I confirm that the proposal clearly defines which part of the developer journey it improves and how it makes building on Midnight easier and more productive." - }, - "open_source_commitment": { - "$ref": "#/definitions/agreementConfirmation", - "description": "Open Source Commitment:", - "title": "I confirm that the proposal explicitly states the chosen permissive open-source license (e.g., MIT, Apache 2.0) and commits to a public code repository." - }, - "quality_documentation": { - "$ref": "#/definitions/agreementConfirmation", - "description": "High-Quality Documentation:", - "title": "I confirm that a plan for creating and maintaining clear, comprehensive documentation is a core part of the proposal''s scope." - }, - "realistic_scope": { - "$ref": "#/definitions/agreementConfirmation", - "description": "Realistic Scope:", - "title": "I confirm that the budget and timeline (3 months) are realistic for delivering the proposed tool or resource." - }, - "strategic_fit": { - "$ref": "#/definitions/agreementConfirmation", - "description": "Strategic Fit:", - "title": "I confirm that the proposal clearly provides a basic prototype reference application for one of the areas of interest." - }, - "verifiable_builder_credentials": { - "$ref": "#/definitions/agreementConfirmation", - "description": "Verifiable Builder Credentials:", - "title": "I confirm that the team provides evidence of their technical ability and experience in creating developer tools or high-quality technical content (e.g., GitHub, portfolio)." - } - }, - "required": [ - "strategic_fit", - "developer_experience", - "open_source_commitment", - "verifiable_builder_credentials", - "quality_documentation", - "realistic_scope" - ], - "title": "Self-Assessment Checklist", - "x-guidance": "Use this checklist to ensure your proposal meets all foundational and content requirements before submission.", - "x-order": [ - "strategic_fit", - "developer_experience", - "open_source_commitment", - "verifiable_builder_credentials", - "quality_documentation", - "realistic_scope" - ] - } + "required": [ + "title", + "proposer" + ], + "title": "Proposal setup", + "x-icon": "view-grid", + "x-order": [ + "title", + "proposer" + ] + }, + "summary": { + "$ref": "#/definitions/segment", + "description": "Key information about your proposal", + "properties": { + "budget": { + "$ref": "#/definitions/section", + "properties": { + "requestedFunds": { + "$ref": "#/definitions/currency", + "description": "Requested amount should be between $USDM 2,500 or $USDM 10,000", + "format": "token:usdm", + "maximum": 10000, + "minimum": 2500, + "title": "Enter the amount of funding you are requesting in $USDM", + "x-guidance": "Each funding category has a minimum and maximum amount of funding a single proposal can request.\n\n**Midnight: Compact DApps**\n\n- Minimum Funding Amount per proposal: **$USDM 2,500**\n- Maximum Funding Amount per proposal: **$USDM 10,000**", + "x-placeholder": "Requested amount should be between $USDM 2,500 or $USDM 10,000" + } + }, + "required": [ + "requestedFunds" + ], + "title": "Budget Information", + "x-order": [ + "requestedFunds" + ] }, - "required": [ - "self_assessment_checklist" - ], - "title": "Self-Assessment", - "x-icon": "shield-check", - "x-order": [ - "self_assessment_checklist" - ] - }, - "setup": { - "$ref": "#/definitions/segment", - "description": "Proposal title", - "properties": { - "proposer": { - "$ref": "#/definitions/section", - "description": "Proposer Information", - "properties": { - "applicant": { - "$ref": "#/definitions/singleLineTextEntry", - "description": "Name and surname of main applicant", - "maxLength": 100, - "minLength": 2, - "title": "Applicant name and surname", - "x-guidance": "The applicant is the individual or entity responsible for the proposed project.", - "x-placeholder": "Enter the full name of the applicant submitting the proposal" - }, - "type": { - "$ref": "#/definitions/radioButtonSelect", - "description": "Are you submitting this proposal as an individual or as an entity (whether formally incorporated or not)", - "enum": [ - "Individual", - "Entity (Incorporated)", - "Entity (Not Incorporated)" - ], - "title": "Are you submitting this proposal as an individual or as an entity (whether formally incorporated or not)?", - "x-guidance": "Please select from one of the following:\n\n1. Individual\n2. Entity (Incorporated)\n3. Entity (Not Incorporated)" - } + "dependencies": { + "$ref": "#/definitions/section", + "description": "External dependencies and requirements for project success", + "properties": { + "dependencyDetail": { + "$ref": "#/definitions/singleLineTextEntry", + "description": "Here you should list any dependencies and prerequisites for your project''s success. These are usually external factors (such as third-party suppliers, external resources, third-party software, etc.) that may cause a delay, since a project has less control over them. In case of third party software, indicate whether you have the necessary licenses and permission to use such software.", + "maxLength": 10240, + "minLength": 15, + "title": "Describe any dependencies or write ''No dependencies''", + "x-guidance": "If “YES”, please describe the dependencies and why you believe them essential to the project''s delivery. If “NO”, please write \"No dependencies\"." }, - "required": [ - "applicant", - "type" - ], - "title": "Applicant", - "x-order": [ - "applicant", - "type" - ] + "hasDependency": { + "$ref": "#/definitions/yesNoChoice", + "description": "Does your project have any dependencies on other organizations, technical or otherwise?", + "title": "Does your project have any dependencies on other organizations, technical or otherwise?", + "x-guidance": "List dependencies and prerequisites for your project’s success. These are usually external factors (such as third-party suppliers, external resources, third-party software, etc.) that may cause a delay or represent a risk due to less control over resources.\n\nIn the case of third-party software, indicate whether you have the necessary licenses and permission to use such software.", + "x-placeholder": "Select if your project has any dependencies" + } }, - "title": { - "$ref": "#/definitions/section", - "description": "Proposal title", - "properties": { - "title": { - "$ref": "#/definitions/singleLineTextEntry", - "description": "**Proposal title**\n\nPlease note we suggest you use no more than 60 characters for your proposal title so that it can be easily viewed in the voting app.", - "maxLength": 60, - "minLength": 3, - "title": "Please provide your proposal title", - "x-guidance": "This is the first detail about your proposal that voters see so it should clearly express what the proposal is about. A clear, unambiguous, and concise title is very important.", - "x-placeholder": "Summarize your proposal in 60 characters or less" - } - }, - "required": [ - "title" - ], - "title": "Proposal title" - } + "required": [ + "hasDependency", + "dependencyDetail" + ], + "title": "Project Dependencies" }, - "required": [ - "title", - "proposer" - ], - "title": "Proposal setup", - "x-icon": "view-grid", - "x-order": [ - "title", - "proposer" - ] - }, - "summary": { - "$ref": "#/definitions/segment", - "description": "Key information about your proposal", - "properties": { - "budget": { - "$ref": "#/definitions/section", - "properties": { - "requestedFunds": { - "$ref": "#/definitions/currency", - "description": "Requested amount should be between $USDM 2,500 or $USDM 10,000", - "format": "token:usdm", - "maximum": 10000, - "minimum": 2500, - "title": "Enter the amount of funding you are requesting in $USDM", - "x-guidance": "Each funding category has a minimum and maximum amount of funding a single proposal can request.\n\n**Midnight: Compact DApps**\n\n- Minimum Funding Amount per proposal: **$USDM 2,500**\n- Maximum Funding Amount per proposal: **$USDM 10,000**", - "x-placeholder": "Requested amount should be between $USDM 2,500 or $USDM 10,000" - } + "open_source": { + "$ref": "#/definitions/section", + "description": "Will your project''s output be fully open source? Open source refers to something people can modify and share because its design is publicly accessible.", + "properties": { + "isOpenSource": { + "$ref": "#/definitions/yesNoChoice", + "description": "Select Yes if the project is open source, No if it is not.", + "title": "Will your project''s outputs be fully open source?", + "x-guidance": "If you answered “YES” to the question above:\n\nIf you declare that the project will be open source in the application form, it must remain open source throughout the entire lifecycle of the project, with a publicly accessible repository.\n\nPlease indicate the type of open source license you intend to use and provide any additional information relevant to the open source status of your project outputs.\n\nIf you answered “NO” to the question above:\n\nYou will not be eligible for this funding." }, - "required": [ - "requestedFunds" - ], - "title": "Budget Information", - "x-order": [ - "requestedFunds" - ] + "openSourceInformation": { + "$ref": "#/definitions/multiLineTextEntry", + "description": "Please provide here more information on the open source status of your project outputs", + "maxLength": 500, + "title": "Please provide here more information on the open source status of your project outputs", + "x-guidance": "Open source licenses ensure intellectual property (IP) (such as source code) remains publicly accessible and licensed in a way that allows anyone to use, modify, and share the IP freely, typically under licenses approved by the Open Source Initiative.\n\nProprietary IP (such as source code) is not publicly available and the rights to use, modify, or redistribute the proprietary IP are restricted and typically reserved by the IP''s owner.\n\nhttps://opensource.org/licenses", + "x-placeholder": "Describe the open source status of your project and the license type" + } }, - "dependencies": { - "$ref": "#/definitions/section", - "description": "External dependencies and requirements for project success", - "properties": { - "dependencyDetail": { - "$ref": "#/definitions/singleLineTextEntry", - "description": "Here you should list any dependencies and prerequisites for your project''s success. These are usually external factors (such as third-party suppliers, external resources, third-party software, etc.) that may cause a delay, since a project has less control over them. In case of third party software, indicate whether you have the necessary licenses and permission to use such software.", - "maxLength": 10240, - "minLength": 15, - "title": "Describe any dependencies or write ''No dependencies''", - "x-guidance": "If “YES”, please describe the dependencies and why you believe them essential to the project''s delivery. If “NO”, please write \"No dependencies\"." - }, - "hasDependency": { - "$ref": "#/definitions/yesNoChoice", - "description": "Does your project have any dependencies on other organizations, technical or otherwise?", - "title": "Does your project have any dependencies on other organizations, technical or otherwise?", - "x-guidance": "List dependencies and prerequisites for your project’s success. These are usually external factors (such as third-party suppliers, external resources, third-party software, etc.) that may cause a delay or represent a risk due to less control over resources.\n\nIn the case of third-party software, indicate whether you have the necessary licenses and permission to use such software.", - "x-placeholder": "Select if your project has any dependencies" - } - }, - "required": [ - "hasDependency", - "dependencyDetail" - ], - "title": "Project Dependencies" + "required": [ + "isOpenSource", + "openSourceInformation" + ], + "title": "Project Open Source", + "x-order": [ + "isOpenSource", + "openSourceInformation" + ] + }, + "problem": { + "$ref": "#/definitions/section", + "description": "Define the problem your proposal aims to solve", + "properties": { + "statement": { + "$ref": "#/definitions/multiLineTextEntry", + "description": "Clearly define the problem you aim to solve. This will be visible in the Catalyst voting app.", + "maxLength": 200, + "minLength": 50, + "title": "What is the problem you want to solve?", + "x-guidance": "Ensure you present a well-defined problem. What is the core issue that you hope to fix? Remember: the reader might not recognize the problem unless you state it clearly.\n\nThis answer will be displayed on the Catalyst voting app, so voters will see it even if they do not open your proposal to read it in detail.", + "x-placeholder": "Describe the problem to be solved with your proposed project" + } }, - "open_source": { - "$ref": "#/definitions/section", - "description": "Will your project''s output be fully open source? Open source refers to something people can modify and share because its design is publicly accessible.", - "properties": { - "isOpenSource": { - "$ref": "#/definitions/yesNoChoice", - "description": "Select Yes if the project is open source, No if it is not.", - "title": "Will your project''s outputs be fully open source?", - "x-guidance": "If you answered “YES” to the question above:\n\nIf you declare that the project will be open source in the application form, it must remain open source throughout the entire lifecycle of the project, with a publicly accessible repository.\n\nPlease indicate the type of open source license you intend to use and provide any additional information relevant to the open source status of your project outputs.\n\nIf you answered “NO” to the question above:\n\nYou will not be eligible for this funding." - }, - "openSourceInformation": { - "$ref": "#/definitions/multiLineTextEntry", - "description": "Please provide here more information on the open source status of your project outputs", - "maxLength": 500, - "title": "Please provide here more information on the open source status of your project outputs", - "x-guidance": "Open source licenses ensure intellectual property (IP) (such as source code) remains publicly accessible and licensed in a way that allows anyone to use, modify, and share the IP freely, typically under licenses approved by the Open Source Initiative.\n\nProprietary IP (such as source code) is not publicly available and the rights to use, modify, or redistribute the proprietary IP are restricted and typically reserved by the IP''s owner.\n\nhttps://opensource.org/licenses", - "x-placeholder": "Describe the open source status of your project and the license type" - } - }, - "required": [ - "isOpenSource", - "openSourceInformation" - ], - "title": "Project Open Source", - "x-order": [ - "isOpenSource", - "openSourceInformation" - ] + "required": [ + "statement" + ], + "title": "Problem Statement" + }, + "solution": { + "$ref": "#/definitions/section", + "description": "Describe your proposed solution to the problem", + "properties": { + "summary": { + "$ref": "#/definitions/multiLineTextEntry", + "description": "Briefly describe your solution. Focus on what you will do or create to solve the problem.", + "maxLength": 200, + "minLength": 50, + "title": "Summarize your solution to the problem", + "x-guidance": "Focus on what you are going to do, or change to solve the problem. Avoid “There should be a way to...” and ensure “We will build a...!”.\n\nClearly state how the solution addresses the problem you have identified. Make sure you connect the ''why'' and the ''how'' of your solution.\n\nThis answer will be displayed on the Catalyst voting app, so voters will see it even if they do not open your proposal and read it in detail.", + "x-placeholder": "Describe how you will solve the problem" + } }, - "problem": { - "$ref": "#/definitions/section", - "description": "Define the problem your proposal aims to solve", - "properties": { - "statement": { - "$ref": "#/definitions/multiLineTextEntry", - "description": "Clearly define the problem you aim to solve. This will be visible in the Catalyst voting app.", - "maxLength": 200, - "minLength": 50, - "title": "What is the problem you want to solve?", - "x-guidance": "Ensure you present a well-defined problem. What is the core issue that you hope to fix? Remember: the reader might not recognize the problem unless you state it clearly.\n\nThis answer will be displayed on the Catalyst voting app, so voters will see it even if they do not open your proposal to read it in detail.", - "x-placeholder": "Describe the problem to be solved with your proposed project" - } - }, - "required": [ - "statement" - ], - "title": "Problem Statement" + "required": [ + "summary" + ], + "title": "Solution Overview" + }, + "supportingLinks": { + "$ref": "#/definitions/section", + "description": "Additional resources and documentation for your proposal", + "properties": { + "links": { + "$ref": "#/definitions/singleLineHttpsURLEntryList", + "description": "Links to any relevant documentation, code repositories, or marketing materials. All links must use HTTPS. Please make sure that submitters own or have rights to share any linked content.", + "maxItems": 5, + "minItems": 0, + "title": "Supporting links" + } }, - "solution": { - "$ref": "#/definitions/section", - "description": "Describe your proposed solution to the problem", - "properties": { - "summary": { - "$ref": "#/definitions/multiLineTextEntry", - "description": "Briefly describe your solution. Focus on what you will do or create to solve the problem.", - "maxLength": 200, - "minLength": 50, - "title": "Summarize your solution to the problem", - "x-guidance": "Focus on what you are going to do, or change to solve the problem. Avoid “There should be a way to...” and ensure “We will build a...!”.\n\nClearly state how the solution addresses the problem you have identified. Make sure you connect the ''why'' and the ''how'' of your solution.\n\nThis answer will be displayed on the Catalyst voting app, so voters will see it even if they do not open your proposal and read it in detail.", - "x-placeholder": "Describe how you will solve the problem" - } - }, - "required": [ - "summary" - ], - "title": "Solution Overview" + "title": "Supporting Documentation", + "x-guidance": "Provide **GitHub handles for all builders** and **1–3 representative repositories** that show your ability to ship code. Optionally include a link to an early PoC repo for this proposal and any **technical write-ups** (READMEs/docs).\n\n**Do not include** websites, social media, pitch decks, or marketing materials-this fund evaluates **code**, not marketing. All links must use HTTPS.", + "x-placeholder": "Links to any relevant documentation, code repositories, or marketing materials. All links must use HTTPS. Please make sure that submitters own or have rights to share any linked content." + }, + "time": { + "$ref": "#/definitions/section", + "properties": { + "duration": { + "$ref": "#/definitions/durationInMonths", + "description": "Specify the expected duration of your project. Projects must be completable within 2-12 months.", + "maximum": 3, + "minimum": 3, + "title": "Please specify how many months you expect your project to last ", + "x-guidance": "Midnight projects must be completed within 3 months.\n\nProjects can be completed earlier if milestone [Proof-of-Achievements](https://docs.projectcatalyst.io/current-fund/project-onboarding/milestone-based-proposals) and [Project Completion](https://docs.projectcatalyst.io/current-fund/general-information/project-close-out-report-and-project-close-out-video-pcr-and-pcv) steps are submitted and approved ahead of the expected delivery due dates in compliance with the [Fund Rules](https://docs.projectcatalyst.io/current-fund/fund-basics/fund-rules).", + "x-placeholder": "Select the expected duration of your project (3 months)" + } }, - "supportingLinks": { - "$ref": "#/definitions/section", - "description": "Additional resources and documentation for your proposal", - "properties": { - "links": { - "$ref": "#/definitions/singleLineHttpsURLEntryList", - "description": "Links to any relevant documentation, code repositories, or marketing materials. All links must use HTTPS. Please make sure that submitters own or have rights to share any linked content.", - "maxItems": 5, - "minItems": 0, - "title": "Supporting links" - } + "required": [ + "duration" + ], + "title": "Time" + }, + "translation": { + "$ref": "#/definitions/section", + "description": "Information about the proposal''s language and translation status", + "properties": { + "isTranslated": { + "$ref": "#/definitions/yesNoChoice", + "description": "Indicate if your proposal has been auto-translated into English from another language", + "title": "Please indicate if your proposal has been auto-translated", + "x-guidance": "Tick “YES” to remind readers that your proposal has been automatically translated and that there may be translation inaccuracies.\n\nIf you wish, you can link a document with the proposal in your native language. \n\nTick “NO” if your proposal has not been automatically translated into English from another language.", + "x-placeholder": "Select if your proposal has been auto-translated into English" }, - "title": "Supporting Documentation", - "x-guidance": "Provide **GitHub handles for all builders** and **1–3 representative repositories** that show your ability to ship code. Optionally include a link to an early PoC repo for this proposal and any **technical write-ups** (READMEs/docs).\n\n**Do not include** websites, social media, pitch decks, or marketing materials-this fund evaluates **code**, not marketing. All links must use HTTPS.", - "x-placeholder": "Links to any relevant documentation, code repositories, or marketing materials. All links must use HTTPS. Please make sure that submitters own or have rights to share any linked content." - }, - "time": { - "$ref": "#/definitions/section", - "properties": { - "duration": { - "$ref": "#/definitions/durationInMonths", - "description": "Specify the expected duration of your project. Projects must be completable within 2-12 months.", - "maximum": 3, - "minimum": 3, - "title": "Please specify how many months you expect your project to last ", - "x-guidance": "Midnight projects must be completed within 3 months.\n\nProjects can be completed earlier if milestone [Proof-of-Achievements](https://docs.projectcatalyst.io/current-fund/project-onboarding/milestone-based-proposals) and [Project Completion](https://docs.projectcatalyst.io/current-fund/general-information/project-close-out-report-and-project-close-out-video-pcr-and-pcv) steps are submitted and approved ahead of the expected delivery due dates in compliance with the [Fund Rules](https://docs.projectcatalyst.io/current-fund/fund-basics/fund-rules).", - "x-placeholder": "Select the expected duration of your project (3 months)" - } + "originalDocumentLink": { + "$ref": "#/definitions/singleLineHttpsURLEntry", + "description": "Provide a link to the original proposal document in its original language", + "title": "Original Document Link" }, - "required": [ - "duration" - ], - "title": "Time" + "originalLanguage": { + "$ref": "#/definitions/languageCode", + "description": "If auto-translated, specify the original language of your proposal", + "title": "Original Language" + } }, - "translation": { - "$ref": "#/definitions/section", - "description": "Information about the proposal''s language and translation status", - "properties": { - "isTranslated": { - "$ref": "#/definitions/yesNoChoice", - "description": "Indicate if your proposal has been auto-translated into English from another language", - "title": "Please indicate if your proposal has been auto-translated", - "x-guidance": "Tick “YES” to remind readers that your proposal has been automatically translated and that there may be translation inaccuracies.\n\nIf you wish, you can link a document with the proposal in your native language. \n\nTick “NO” if your proposal has not been automatically translated into English from another language.", - "x-placeholder": "Select if your proposal has been auto-translated into English" - }, - "originalDocumentLink": { - "$ref": "#/definitions/singleLineHttpsURLEntry", - "description": "Provide a link to the original proposal document in its original language", - "title": "Original Document Link" - }, - "originalLanguage": { - "$ref": "#/definitions/languageCode", - "description": "If auto-translated, specify the original language of your proposal", - "title": "Original Language" - } - }, - "required": [ - "isTranslated" - ], - "title": "Translation Information" - } - }, - "title": "Proposal Summary", - "x-icon": "light-bulb", - "x-order": [ - "budget", - "time", - "translation", - "problem", - "solution", - "supportingLinks", - "dependencies", - "open_source" - ] + "required": [ + "isTranslated" + ], + "title": "Translation Information" + } }, - "theme": { - "$ref": "#/definitions/segment", - "properties": { - "theme": { - "$ref": "#/definitions/section", - "description": "Long-term vision and categorization of your project", - "properties": { - "grouped_tag": { - "$ref": "#/definitions/singleGroupedTagSelector", - "oneOf": [ - { - "properties": { - "group": { - "$ref": "#/definitions/tagGroup", - "const": "Governance" - }, - "tag": { - "$ref": "#/definitions/tagSelection", - "enum": [ - "Governance", - "DAO" - ] - } + "title": "Proposal Summary", + "x-icon": "light-bulb", + "x-order": [ + "budget", + "time", + "translation", + "problem", + "solution", + "supportingLinks", + "dependencies", + "open_source" + ] + }, + "theme": { + "$ref": "#/definitions/segment", + "properties": { + "theme": { + "$ref": "#/definitions/section", + "description": "Long-term vision and categorization of your project", + "properties": { + "grouped_tag": { + "$ref": "#/definitions/singleGroupedTagSelector", + "oneOf": [ + { + "properties": { + "group": { + "$ref": "#/definitions/tagGroup", + "const": "Governance" + }, + "tag": { + "$ref": "#/definitions/tagSelection", + "enum": [ + "Governance", + "DAO" + ] } - }, - { - "properties": { - "group": { - "$ref": "#/definitions/tagGroup", - "const": "Education" - }, - "tag": { - "$ref": "#/definitions/tagSelection", - "enum": [ - "Education", - "Learn to Earn", - "Training", - "Translation" - ] - } + } + }, + { + "properties": { + "group": { + "$ref": "#/definitions/tagGroup", + "const": "Education" + }, + "tag": { + "$ref": "#/definitions/tagSelection", + "enum": [ + "Education", + "Learn to Earn", + "Training", + "Translation" + ] } - }, - { - "properties": { - "group": { - "$ref": "#/definitions/tagGroup", - "const": "Community & Outreach" - }, - "tag": { - "$ref": "#/definitions/tagSelection", - "enum": [ - "Connected Community", - "Community", - "Community Outreach", - "Social Media" - ] - } + } + }, + { + "properties": { + "group": { + "$ref": "#/definitions/tagGroup", + "const": "Community & Outreach" + }, + "tag": { + "$ref": "#/definitions/tagSelection", + "enum": [ + "Connected Community", + "Community", + "Community Outreach", + "Social Media" + ] } - }, - { - "properties": { - "group": { - "$ref": "#/definitions/tagGroup", - "const": "Development & Tools" - }, - "tag": { - "$ref": "#/definitions/tagSelection", - "enum": [ - "Developer Tools", - "Infrastructure", - "Analytics", - "AI", - "Research", - "UTXO", - "P2P", - "Layer2" - ] - } + } + }, + { + "properties": { + "group": { + "$ref": "#/definitions/tagGroup", + "const": "Development & Tools" + }, + "tag": { + "$ref": "#/definitions/tagSelection", + "enum": [ + "Developer Tools", + "Infrastructure", + "Analytics", + "AI", + "Research", + "UTXO", + "P2P", + "Layer2" + ] } - }, - { - "properties": { - "group": { - "$ref": "#/definitions/tagGroup", - "const": "Identity & Security" - }, - "tag": { - "$ref": "#/definitions/tagSelection", - "enum": [ - "Identity & Verification", - "Cybersecurity", - "Security", - "Authentication", - "Privacy" - ] - } + } + }, + { + "properties": { + "group": { + "$ref": "#/definitions/tagGroup", + "const": "Identity & Security" + }, + "tag": { + "$ref": "#/definitions/tagSelection", + "enum": [ + "Identity & Verification", + "Cybersecurity", + "Security", + "Authentication", + "Privacy" + ] } - }, - { - "properties": { - "group": { - "$ref": "#/definitions/tagGroup", - "const": "DeFi" - }, - "tag": { - "$ref": "#/definitions/tagSelection", - "enum": [ - "DeFi", - "DEX", - "Payments", - "Stablecoin", - "Risk Management", - "Yield", - "Staking", - "Lending" - ] - } + } + }, + { + "properties": { + "group": { + "$ref": "#/definitions/tagGroup", + "const": "DeFi" + }, + "tag": { + "$ref": "#/definitions/tagSelection", + "enum": [ + "DeFi", + "DEX", + "Payments", + "Stablecoin", + "Risk Management", + "Yield", + "Staking", + "Lending" + ] } - }, - { - "properties": { - "group": { - "$ref": "#/definitions/tagGroup", - "const": "Real World Applications (RWA)" - }, - "tag": { - "$ref": "#/definitions/tagSelection", - "enum": [ - "Wallet", - "Marketplace", - "Manufacturing", - "IoT", - "Financial Services", - "E-commerce", - "Business Services", - "Supply Chain", - "Real Estate", - "Healthcare", - "Tourism", - "Entertainment", - "RWA", - "Music", - "Tokenization" - ] - } + } + }, + { + "properties": { + "group": { + "$ref": "#/definitions/tagGroup", + "const": "Real World Applications (RWA)" + }, + "tag": { + "$ref": "#/definitions/tagSelection", + "enum": [ + "Wallet", + "Marketplace", + "Manufacturing", + "IoT", + "Financial Services", + "E-commerce", + "Business Services", + "Supply Chain", + "Real Estate", + "Healthcare", + "Tourism", + "Entertainment", + "RWA", + "Music", + "Tokenization" + ] } - }, - { - "properties": { - "group": { - "$ref": "#/definitions/tagGroup", - "const": "Events & Marketing" - }, - "tag": { - "$ref": "#/definitions/tagSelection", - "enum": [ - "Events", - "Marketing", - "Hackathons", - "Accelerator", - "Incubator" - ] - } + } + }, + { + "properties": { + "group": { + "$ref": "#/definitions/tagGroup", + "const": "Events & Marketing" + }, + "tag": { + "$ref": "#/definitions/tagSelection", + "enum": [ + "Events", + "Marketing", + "Hackathons", + "Accelerator", + "Incubator" + ] } - }, - { - "properties": { - "group": { - "$ref": "#/definitions/tagGroup", - "const": "Interoperability" - }, - "tag": { - "$ref": "#/definitions/tagSelection", - "enum": [ - "Cross-chain", - "Interoperability", - "Off-chain", - "Legal", - "Policy", - "Advocacy", - "Standards" - ] - } + } + }, + { + "properties": { + "group": { + "$ref": "#/definitions/tagGroup", + "const": "Interoperability" + }, + "tag": { + "$ref": "#/definitions/tagSelection", + "enum": [ + "Cross-chain", + "Interoperability", + "Off-chain", + "Legal", + "Policy", + "Advocacy", + "Standards" + ] } - }, - { - "properties": { - "group": { - "$ref": "#/definitions/tagGroup", - "const": "Sustainability" - }, - "tag": { - "$ref": "#/definitions/tagSelection", - "enum": [ - "Sustainability", - "Environment", - "Agriculture" - ] - } + } + }, + { + "properties": { + "group": { + "$ref": "#/definitions/tagGroup", + "const": "Sustainability" + }, + "tag": { + "$ref": "#/definitions/tagSelection", + "enum": [ + "Sustainability", + "Environment", + "Agriculture" + ] } - }, - { - "properties": { - "group": { - "$ref": "#/definitions/tagGroup", - "const": "Smart Contracts" - }, - "tag": { - "$ref": "#/definitions/tagSelection", - "enum": [ - "Smart Contracts", - "Audit", - "Oracles" - ] - } + } + }, + { + "properties": { + "group": { + "$ref": "#/definitions/tagGroup", + "const": "Smart Contracts" + }, + "tag": { + "$ref": "#/definitions/tagSelection", + "enum": [ + "Smart Contracts", + "Audit", + "Oracles" + ] } - }, - { - "properties": { - "group": { - "$ref": "#/definitions/tagGroup", - "const": "GameFi" - }, - "tag": { - "$ref": "#/definitions/tagSelection", - "enum": [ - "Games", - "Gaming", - "GameFi", - "Entertainment", - "Metaverse" - ] - } + } + }, + { + "properties": { + "group": { + "$ref": "#/definitions/tagGroup", + "const": "GameFi" + }, + "tag": { + "$ref": "#/definitions/tagSelection", + "enum": [ + "Games", + "Gaming", + "GameFi", + "Entertainment", + "Metaverse" + ] } - }, - { - "properties": { - "group": { - "$ref": "#/definitions/tagGroup", - "const": "NFT" - }, - "tag": { - "$ref": "#/definitions/tagSelection", - "enum": [ - "NFT", - "CNFT", - "Collectibles", - "Digital Twin" - ] - } + } + }, + { + "properties": { + "group": { + "$ref": "#/definitions/tagGroup", + "const": "NFT" + }, + "tag": { + "$ref": "#/definitions/tagSelection", + "enum": [ + "NFT", + "CNFT", + "Collectibles", + "Digital Twin" + ] } } - ], - "title": "Please choose the most relevant theme and tag related to the outcomes of your proposal" - } - }, - "required": [ - "grouped_tag" - ], - "title": "Theme", - "x-order": [ - "theme" - ] - } - }, - "title": "Theme Selection", - "x-icon": "tag", - "x-order": [ - "theme" - ] - } - }, - "required": [ - "setup", - "summary", - "theme", - "details", - "milestones", - "pitch", - "self_assessment", - "agreements" - ], - "title": "F15 Midnight: Compact DApps", - "type": "object", - "x-changelog": { - "2025-10-08": [ - "F15 Template Setup" - ], - "2025-11-05": [ - "Included two additional links in the Consent & Confirmation question" + } + ], + "title": "Please choose the most relevant theme and tag related to the outcomes of your proposal" + } + }, + "required": [ + "grouped_tag" + ], + "title": "Theme", + "x-order": [ + "theme" + ] + } + }, + "title": "Theme Selection", + "x-icon": "tag", + "x-order": [ + "theme" ] - }, - "x-order": [ - "setup", - "summary", - "theme", - "campaign_category", - "details", - "milestones", - "pitch", - "self_assessment", - "agreements" + } + }, + "required": [ + "setup", + "summary", + "theme", + "details", + "milestones", + "pitch", + "self_assessment", + "agreements" + ], + "title": "F15 Midnight: Compact DApps", + "type": "object", + "x-changelog": { + "2025-10-08": [ + "F15 Template Setup" + ], + "2025-11-05": [ + "Included two additional links in the Consent & Confirmation question" ] - } -] \ No newline at end of file + }, + "x-order": [ + "setup", + "summary", + "theme", + "campaign_category", + "details", + "milestones", + "pitch", + "self_assessment", + "agreements" + ] +} \ No newline at end of file diff --git a/catalyst-gateway/tests/api_tests/utils/admin.py b/catalyst-gateway/tests/api_tests/utils/admin.py index da495051f24..1aa8019b9c5 100644 --- a/catalyst-gateway/tests/api_tests/utils/admin.py +++ b/catalyst-gateway/tests/api_tests/utils/admin.py @@ -33,8 +33,6 @@ def cat_id(self) -> str: subnet="preprod", pk_hex=self.pk_hex(), scheme="admin.catalyst", - role_id=RoleID.ROLE_0, - rotation="10", ) def auth_token(self) -> str: @@ -46,6 +44,6 @@ def auth_token(self) -> str: ) -@pytest.fixture +@pytest.fixture(scope='session') def admin_key() -> AdminKey: return AdminKey(ONLY_ROLE_0_REG_JSON[f"{RoleID.ROLE_0}"][0]["sk"]) diff --git a/catalyst-gateway/tests/api_tests/utils/rbac_chain.py b/catalyst-gateway/tests/api_tests/utils/rbac_chain.py index 79461493c50..89ffc153e3e 100644 --- a/catalyst-gateway/tests/api_tests/utils/rbac_chain.py +++ b/catalyst-gateway/tests/api_tests/utils/rbac_chain.py @@ -78,7 +78,7 @@ def short_cat_id(self) -> str: ) -@pytest.fixture +@pytest.fixture(scope='session') def rbac_chain_factory(): def __rbac_chain_factory( chain: Chain = Chain.All, diff --git a/catalyst-gateway/tests/api_tests/utils/signed_doc.py b/catalyst-gateway/tests/api_tests/utils/signed_doc.py index f0bf38996fa..3d57fc0ee27 100644 --- a/catalyst-gateway/tests/api_tests/utils/signed_doc.py +++ b/catalyst-gateway/tests/api_tests/utils/signed_doc.py @@ -106,23 +106,27 @@ def __init__( def create_metadata( doc_type: str, - templates: list[Any] | None = None, - parameters: list[Any] | None = None, + content_type: str, + template: SignedDocumentBase | None = None, + parameters: list[SignedDocumentBase] | None = None, ) -> dict[str, Any]: doc_id = uuid_v7.uuid_v7() metadata: dict[str, Any] = { "content-encoding": "br", - "content-type": "application/json", + "content-type": content_type, "id": doc_id, "ver": doc_id, "type": doc_type, } - if templates is not None: - metadata["templates"] = templates + if template is not None: + metadata["template"] = {"id": template.metadata["id"], "ver": template.metadata["ver"], "cid": "0x"} if parameters is not None: - metadata["parameters"] = parameters + metadata["parameters"] = list([ + {"id": p.metadata["id"], "ver": p.metadata["ver"], "cid": "0x"} + for p in parameters + ]) return metadata @@ -156,7 +160,7 @@ def build_signed_doc( signed_doc_file.name, metadata_file.name, ], - capture_output=True, + # capture_output=True, ) subprocess.run( @@ -167,7 +171,7 @@ def build_signed_doc( bip32_sk_hex, cat_id, ], - capture_output=True, + # capture_output=True, ) signed_doc_hex = signed_doc_file.read().hex() @@ -179,51 +183,24 @@ def build_signed_doc( # ------------------- # -class ProposalParameterType(IntEnum): - CATEGORY = 0 - CAMPAIGN = 1 - BRAND = 2 - - # return a Proposal document which is already published to the cat-gateway and the corresponding RoleID -@pytest.fixture +@pytest.fixture(scope='session') def proposal_doc_factory( rbac_chain_factory, - proposal_form_template_doc_factory, - category_parameters_doc, - campaign_parameters_doc, + proposal_form_template_doc, brand_parameters_doc, ): def __factory__( - parameter_type: ProposalParameterType, role_id: RoleID ) -> SignedDocument: - param: SignedDocumentBase - if parameter_type == ProposalParameterType.CATEGORY: - param = category_parameters_doc - elif parameter_type == ProposalParameterType.CAMPAIGN: - param = campaign_parameters_doc - elif parameter_type == ProposalParameterType.BRAND: - param = brand_parameters_doc - else: - raise Exception("Invalid parameter type for proposal document") - - template: SignedDocumentBase = proposal_form_template_doc_factory( - parameter_type - ) + param: SignedDocumentBase = brand_parameters_doc + template: SignedDocumentBase = proposal_form_template_doc metadata = create_metadata( - DOC_TYPE["proposal"], - [ - { - "id": template.metadata["id"], - "ver": template.metadata["ver"], - "cid": "0x", - } - ], - [ - {"id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x"}, - ], + doc_type=DOC_TYPE["proposal"], + content_type="application/json", + template=template, + parameters=[param] ) content = JSF(template.content).generate() @@ -244,62 +221,39 @@ def __factory__( return __factory__ -@pytest.fixture -def proposal_form_template_doc_factory( - rbac_chain_factory, +@pytest.fixture(scope='session') +def proposal_form_template_doc( admin_key, - category_parameters_doc, - campaign_parameters_doc, brand_parameters_doc, ): - def __factory__(parameter_type: ProposalParameterType): - param: SignedDocumentBase - if parameter_type == ProposalParameterType.CATEGORY: - param = category_parameters_doc - elif parameter_type == ProposalParameterType.CAMPAIGN: - param = campaign_parameters_doc - elif parameter_type == ProposalParameterType.BRAND: - param = brand_parameters_doc - else: - raise Exception( - "Invalid parameter type for proposal form template document" - ) - - tmp_metadata = create_metadata( - DOC_TYPE["proposal_form_template"], - None, - [ - {"id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x"}, - ], - ) - - with open( - "./test_data/signed_docs/proposal_form_template.json", "r" - ) as json_file: - metadata, content = json.load(json_file) + param: SignedDocumentBase = brand_parameters_doc - metadata["id"] = tmp_metadata["id"] - metadata["ver"] = tmp_metadata["ver"] - metadata["parameters"] = tmp_metadata["parameters"] + metadata = create_metadata( + doc_type=DOC_TYPE["proposal_form_template"], + content_type="application/schema+json", + parameters=[param], + ) - doc = SignedDocument(metadata, content) + with open( + "./test_data/signed_docs/proposal_form_template.json", "r" + ) as json_file: + content = json.load(json_file) - resp = document.put( - data=doc.build_and_sign(admin_key.cat_id(), admin_key.sk_hex), - token=admin_key.auth_token(), - ) - assert resp.status_code == 201, ( - f"Failed to publish document: {resp.status_code} - {resp.text}" - ) + doc = SignedDocument(metadata, content) - return doc + resp = document.put( + data=doc.build_and_sign(admin_key.cat_id(), admin_key.sk_hex), + token=admin_key.auth_token(), + ) + assert resp.status_code == 201, ( + f"Failed to publish document: {resp.status_code} - {resp.text}" + ) - return __factory__ + return doc -@pytest.fixture +@pytest.fixture(scope='session') def category_parameters_doc( - rbac_chain_factory, admin_key, category_parameters_form_template_doc, campaign_parameters_doc, @@ -308,17 +262,10 @@ def category_parameters_doc( param: SignedDocumentBase = campaign_parameters_doc metadata = create_metadata( - DOC_TYPE["category_parameters"], - [ - { - "id": template.metadata["id"], - "ver": template.metadata["ver"], - "cid": "0x", - } - ], - [ - {"id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x"}, - ], + doc_type=DOC_TYPE["category_parameters"], + content_type="application/json", + template=template, + parameters=[param], ) content = JSF(template.content).generate() doc = SignedDocument(metadata, content) @@ -334,16 +281,17 @@ def category_parameters_doc( return doc -@pytest.fixture +@pytest.fixture(scope='session') def category_parameters_form_template_doc( - rbac_chain_factory, admin_key, campaign_parameters_doc + admin_key, + campaign_parameters_doc ) -> SignedDocumentBase: param: SignedDocumentBase = campaign_parameters_doc metadata = create_metadata( - DOC_TYPE["category_parameters_form_template"], - None, - [{"id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x"}], + doc_type=DOC_TYPE["category_parameters_form_template"], + content_type="application/schema+json", + parameters=[param], ) content = {"type": "object"} doc = SignedDocument(metadata, content) @@ -359,9 +307,8 @@ def category_parameters_form_template_doc( return doc -@pytest.fixture +@pytest.fixture(scope='session') def campaign_parameters_doc( - rbac_chain_factory, admin_key, campaign_parameters_form_template_doc, brand_parameters_doc, @@ -370,17 +317,11 @@ def campaign_parameters_doc( param: SignedDocumentBase = brand_parameters_doc metadata = create_metadata( - DOC_TYPE["campaign_parameters"], - [ - { - "id": template.metadata["id"], - "ver": template.metadata["ver"], - "cid": "0x", - } - ], - [ - {"id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x"}, - ], + doc_type=DOC_TYPE["campaign_parameters"], + content_type="application/json", + template=template, + parameters=[param], + ) content = JSF(template.content).generate() doc = SignedDocument(metadata, content) @@ -396,16 +337,17 @@ def campaign_parameters_doc( return doc -@pytest.fixture +@pytest.fixture(scope='session') def campaign_parameters_form_template_doc( - rbac_chain_factory, admin_key, brand_parameters_doc + admin_key, + brand_parameters_doc ) -> SignedDocumentBase: param: SignedDocumentBase = brand_parameters_doc metadata = create_metadata( - DOC_TYPE["campaign_parameters_form_template"], - None, - [{"id": param.metadata["id"], "ver": param.metadata["ver"], "cid": "0x"}], + doc_type=DOC_TYPE["campaign_parameters_form_template"], + content_type="application/schema+json", + parameters=[param], ) content = {"type": "object"} doc = SignedDocument(metadata, content) @@ -421,22 +363,17 @@ def campaign_parameters_form_template_doc( return doc -@pytest.fixture +@pytest.fixture(scope='session') def brand_parameters_doc( - rbac_chain_factory, admin_key, brand_parameters_form_template_doc + admin_key, + brand_parameters_form_template_doc ) -> SignedDocumentBase: template: SignedDocumentBase = brand_parameters_form_template_doc metadata = create_metadata( - DOC_TYPE["brand_parameters"], - [ - { - "id": template.metadata["id"], - "ver": template.metadata["ver"], - "cid": "0x", - } - ], - None, + doc_type=DOC_TYPE["brand_parameters"], + content_type="application/json", + template=template, ) content = JSF(template.content).generate() doc = SignedDocument(metadata, content) @@ -452,14 +389,13 @@ def brand_parameters_doc( return doc -@pytest.fixture +@pytest.fixture(scope='session') def brand_parameters_form_template_doc( - rbac_chain_factory, admin_key + admin_key ) -> SignedDocumentBase: metadata = create_metadata( - DOC_TYPE["brand_parameters_form_template"], - None, - None, + doc_type=DOC_TYPE["brand_parameters_form_template"], + content_type="application/schema+json" ) content = {"type": "object"} doc = SignedDocument(metadata, content) From 5bdaff8263a708b7eba27263630d2e9fafc7ac90 Mon Sep 17 00:00:00 2001 From: Mr-Leshiy Date: Thu, 27 Nov 2025 18:54:35 +0700 Subject: [PATCH 37/40] replace JSF with ranjg --- catalyst-gateway/tests/api_tests/poetry.lock | 215 ++---------------- .../tests/api_tests/pyproject.toml | 2 +- .../tests/api_tests/utils/signed_doc.py | 10 +- 3 files changed, 23 insertions(+), 204 deletions(-) diff --git a/catalyst-gateway/tests/api_tests/poetry.lock b/catalyst-gateway/tests/api_tests/poetry.lock index 5f81057fcf3..3fb7562d319 100644 --- a/catalyst-gateway/tests/api_tests/poetry.lock +++ b/catalyst-gateway/tests/api_tests/poetry.lock @@ -1104,21 +1104,6 @@ files = [ {file = "ECPy-1.2.5.tar.gz", hash = "sha256:9635cffb9b6ecf7fd7f72aea1665829ac74a1d272006d0057d45a621aae20228"}, ] -[[package]] -name = "faker" -version = "38.2.0" -description = "Faker is a Python package that generates fake data for you." -optional = false -python-versions = ">=3.10" -groups = ["main"] -files = [ - {file = "faker-38.2.0-py3-none-any.whl", hash = "sha256:35fe4a0a79dee0dc4103a6083ee9224941e7d3594811a50e3969e547b0d2ee65"}, - {file = "faker-38.2.0.tar.gz", hash = "sha256:20672803db9c7cb97f9b56c18c54b915b6f1d8991f63d1d673642dc43f5ce7ab"}, -] - -[package.dependencies] -tzdata = "*" - [[package]] name = "flask" version = "3.1.2" @@ -1555,29 +1540,6 @@ MarkupSafe = ">=2.0" [package.extras] i18n = ["Babel (>=2.7)"] -[[package]] -name = "jsf" -version = "0.11.2" -description = "Creates fake JSON files from a JSON schema" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "jsf-0.11.2-py3-none-any.whl", hash = "sha256:b4472c8c2d776eb3e0bb08368caa6ae0ead7ea78b20653facc07b6d93768612c"}, - {file = "jsf-0.11.2.tar.gz", hash = "sha256:07055b363281d38ce871a9256a00587d8472802c5108721a7fe5884465104b5d"}, -] - -[package.dependencies] -faker = ">=15.3.4" -jsonschema = ">=4.17.3" -pydantic = ">=2.0.0" -rstr = ">=3.2.0" -smart-open = {version = ">=6.3.0", extras = ["http"]} -typing-extensions = ">=4.9.0" - -[package.extras] -cli = ["typer (>=0.7.0)"] - [[package]] name = "jsonschema" version = "4.25.1" @@ -2680,6 +2642,22 @@ files = [ {file = "pywin32-311-cp39-cp39-win_arm64.whl", hash = "sha256:62ea666235135fee79bb154e695f3ff67370afefd71bd7fea7512fc70ef31e3d"}, ] +[[package]] +name = "ranjg" +version = "0.5.1.0" +description = "Generate json text randomly" +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "ranjg-0.5.1.0-py3-none-any.whl", hash = "sha256:06be19f8dfd51294471a284e0c80c23ad2b443ec896505d69824ff07c1385848"}, + {file = "ranjg-0.5.1.0.tar.gz", hash = "sha256:7d82e879da8228e799f0bdf15ba46c7d5dfaa4df06f66eb3745475232e38fd3e"}, +] + +[package.dependencies] +jsonschema = "*" +rstr = "*" + [[package]] name = "referencing" version = "0.37.0" @@ -3033,33 +3011,6 @@ files = [ {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, ] -[[package]] -name = "smart-open" -version = "7.5.0" -description = "Utils for streaming large files (S3, HDFS, GCS, SFTP, Azure Blob Storage, gzip, bz2, zst...)" -optional = false -python-versions = "<4.0,>=3.9" -groups = ["main"] -files = [ - {file = "smart_open-7.5.0-py3-none-any.whl", hash = "sha256:87e695c5148bbb988f15cec00971602765874163be85acb1c9fb8abc012e6599"}, - {file = "smart_open-7.5.0.tar.gz", hash = "sha256:f394b143851d8091011832ac8113ea4aba6b92e6c35f6e677ddaaccb169d7cb9"}, -] - -[package.dependencies] -requests = {version = "*", optional = true, markers = "extra == \"http\""} -wrapt = "*" - -[package.extras] -all = ["smart_open[azure,gcs,http,s3,ssh,webhdfs,zst]"] -azure = ["azure-common", "azure-core", "azure-storage-blob"] -gcs = ["google-api-core (<2.28) ; python_version < \"3.10\"", "google-cloud-storage (>=2.6.0)"] -http = ["requests"] -s3 = ["boto3 (>=1.9.17)"] -ssh = ["paramiko"] -test = ["awscli", "flake8", "moto[server]", "numpy", "pyopenssl", "pytest", "pytest-rerunfailures", "pytest-timeout", "pytest-xdist[psutil]", "pytest_benchmark", "responses", "smart_open[all]"] -webhdfs = ["requests"] -zst = ["backports.zstd (>=1.0.0) ; python_version < \"3.14\""] - [[package]] name = "sniffio" version = "1.3.1" @@ -3148,18 +3099,6 @@ files = [ [package.dependencies] typing-extensions = ">=4.12.0" -[[package]] -name = "tzdata" -version = "2025.2" -description = "Provider of IANA time zone data" -optional = false -python-versions = ">=2" -groups = ["main"] -files = [ - {file = "tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8"}, - {file = "tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9"}, -] - [[package]] name = "urllib3" version = "2.5.0" @@ -3356,126 +3295,6 @@ files = [ [package.extras] dev = ["black (>=19.3b0) ; python_version >= \"3.6\"", "pytest (>=4.6.2)"] -[[package]] -name = "wrapt" -version = "2.0.1" -description = "Module for decorators, wrappers and monkey patching." -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "wrapt-2.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:64b103acdaa53b7caf409e8d45d39a8442fe6dcfec6ba3f3d141e0cc2b5b4dbd"}, - {file = "wrapt-2.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:91bcc576260a274b169c3098e9a3519fb01f2989f6d3d386ef9cbf8653de1374"}, - {file = "wrapt-2.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ab594f346517010050126fcd822697b25a7031d815bb4fbc238ccbe568216489"}, - {file = "wrapt-2.0.1-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:36982b26f190f4d737f04a492a68accbfc6fa042c3f42326fdfbb6c5b7a20a31"}, - {file = "wrapt-2.0.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:23097ed8bc4c93b7bf36fa2113c6c733c976316ce0ee2c816f64ca06102034ef"}, - {file = "wrapt-2.0.1-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8bacfe6e001749a3b64db47bcf0341da757c95959f592823a93931a422395013"}, - {file = "wrapt-2.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8ec3303e8a81932171f455f792f8df500fc1a09f20069e5c16bd7049ab4e8e38"}, - {file = "wrapt-2.0.1-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:3f373a4ab5dbc528a94334f9fe444395b23c2f5332adab9ff4ea82f5a9e33bc1"}, - {file = "wrapt-2.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f49027b0b9503bf6c8cdc297ca55006b80c2f5dd36cecc72c6835ab6e10e8a25"}, - {file = "wrapt-2.0.1-cp310-cp310-win32.whl", hash = "sha256:8330b42d769965e96e01fa14034b28a2a7600fbf7e8f0cc90ebb36d492c993e4"}, - {file = "wrapt-2.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:1218573502a8235bb8a7ecaed12736213b22dcde9feab115fa2989d42b5ded45"}, - {file = "wrapt-2.0.1-cp310-cp310-win_arm64.whl", hash = "sha256:eda8e4ecd662d48c28bb86be9e837c13e45c58b8300e43ba3c9b4fa9900302f7"}, - {file = "wrapt-2.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0e17283f533a0d24d6e5429a7d11f250a58d28b4ae5186f8f47853e3e70d2590"}, - {file = "wrapt-2.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:85df8d92158cb8f3965aecc27cf821461bb5f40b450b03facc5d9f0d4d6ddec6"}, - {file = "wrapt-2.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c1be685ac7700c966b8610ccc63c3187a72e33cab53526a27b2a285a662cd4f7"}, - {file = "wrapt-2.0.1-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:df0b6d3b95932809c5b3fecc18fda0f1e07452d05e2662a0b35548985f256e28"}, - {file = "wrapt-2.0.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4da7384b0e5d4cae05c97cd6f94faaf78cc8b0f791fc63af43436d98c4ab37bb"}, - {file = "wrapt-2.0.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ec65a78fbd9d6f083a15d7613b2800d5663dbb6bb96003899c834beaa68b242c"}, - {file = "wrapt-2.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7de3cc939be0e1174969f943f3b44e0d79b6f9a82198133a5b7fc6cc92882f16"}, - {file = "wrapt-2.0.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:fb1a5b72cbd751813adc02ef01ada0b0d05d3dcbc32976ce189a1279d80ad4a2"}, - {file = "wrapt-2.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3fa272ca34332581e00bf7773e993d4f632594eb2d1b0b162a9038df0fd971dd"}, - {file = "wrapt-2.0.1-cp311-cp311-win32.whl", hash = "sha256:fc007fdf480c77301ab1afdbb6ab22a5deee8885f3b1ed7afcb7e5e84a0e27be"}, - {file = "wrapt-2.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:47434236c396d04875180171ee1f3815ca1eada05e24a1ee99546320d54d1d1b"}, - {file = "wrapt-2.0.1-cp311-cp311-win_arm64.whl", hash = "sha256:837e31620e06b16030b1d126ed78e9383815cbac914693f54926d816d35d8edf"}, - {file = "wrapt-2.0.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1fdbb34da15450f2b1d735a0e969c24bdb8d8924892380126e2a293d9902078c"}, - {file = "wrapt-2.0.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3d32794fe940b7000f0519904e247f902f0149edbe6316c710a8562fb6738841"}, - {file = "wrapt-2.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:386fb54d9cd903ee0012c09291336469eb7b244f7183d40dc3e86a16a4bace62"}, - {file = "wrapt-2.0.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7b219cb2182f230676308cdcacd428fa837987b89e4b7c5c9025088b8a6c9faf"}, - {file = "wrapt-2.0.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:641e94e789b5f6b4822bb8d8ebbdfc10f4e4eae7756d648b717d980f657a9eb9"}, - {file = "wrapt-2.0.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fe21b118b9f58859b5ebaa4b130dee18669df4bd111daad082b7beb8799ad16b"}, - {file = "wrapt-2.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:17fb85fa4abc26a5184d93b3efd2dcc14deb4b09edcdb3535a536ad34f0b4dba"}, - {file = "wrapt-2.0.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:b89ef9223d665ab255ae42cc282d27d69704d94be0deffc8b9d919179a609684"}, - {file = "wrapt-2.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a453257f19c31b31ba593c30d997d6e5be39e3b5ad9148c2af5a7314061c63eb"}, - {file = "wrapt-2.0.1-cp312-cp312-win32.whl", hash = "sha256:3e271346f01e9c8b1130a6a3b0e11908049fe5be2d365a5f402778049147e7e9"}, - {file = "wrapt-2.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:2da620b31a90cdefa9cd0c2b661882329e2e19d1d7b9b920189956b76c564d75"}, - {file = "wrapt-2.0.1-cp312-cp312-win_arm64.whl", hash = "sha256:aea9c7224c302bc8bfc892b908537f56c430802560e827b75ecbde81b604598b"}, - {file = "wrapt-2.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:47b0f8bafe90f7736151f61482c583c86b0693d80f075a58701dd1549b0010a9"}, - {file = "wrapt-2.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:cbeb0971e13b4bd81d34169ed57a6dda017328d1a22b62fda45e1d21dd06148f"}, - {file = "wrapt-2.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:eb7cffe572ad0a141a7886a1d2efa5bef0bf7fe021deeea76b3ab334d2c38218"}, - {file = "wrapt-2.0.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c8d60527d1ecfc131426b10d93ab5d53e08a09c5fa0175f6b21b3252080c70a9"}, - {file = "wrapt-2.0.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c654eafb01afac55246053d67a4b9a984a3567c3808bb7df2f8de1c1caba2e1c"}, - {file = "wrapt-2.0.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:98d873ed6c8b4ee2418f7afce666751854d6d03e3c0ec2a399bb039cd2ae89db"}, - {file = "wrapt-2.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c9e850f5b7fc67af856ff054c71690d54fa940c3ef74209ad9f935b4f66a0233"}, - {file = "wrapt-2.0.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:e505629359cb5f751e16e30cf3f91a1d3ddb4552480c205947da415d597f7ac2"}, - {file = "wrapt-2.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2879af909312d0baf35f08edeea918ee3af7ab57c37fe47cb6a373c9f2749c7b"}, - {file = "wrapt-2.0.1-cp313-cp313-win32.whl", hash = "sha256:d67956c676be5a24102c7407a71f4126d30de2a569a1c7871c9f3cabc94225d7"}, - {file = "wrapt-2.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:9ca66b38dd642bf90c59b6738af8070747b610115a39af2498535f62b5cdc1c3"}, - {file = "wrapt-2.0.1-cp313-cp313-win_arm64.whl", hash = "sha256:5a4939eae35db6b6cec8e7aa0e833dcca0acad8231672c26c2a9ab7a0f8ac9c8"}, - {file = "wrapt-2.0.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:a52f93d95c8d38fed0669da2ebdb0b0376e895d84596a976c15a9eb45e3eccb3"}, - {file = "wrapt-2.0.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4e54bbf554ee29fcceee24fa41c4d091398b911da6e7f5d7bffda963c9aed2e1"}, - {file = "wrapt-2.0.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:908f8c6c71557f4deaa280f55d0728c3bca0960e8c3dd5ceeeafb3c19942719d"}, - {file = "wrapt-2.0.1-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:e2f84e9af2060e3904a32cea9bb6db23ce3f91cfd90c6b426757cf7cc01c45c7"}, - {file = "wrapt-2.0.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e3612dc06b436968dfb9142c62e5dfa9eb5924f91120b3c8ff501ad878f90eb3"}, - {file = "wrapt-2.0.1-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6d2d947d266d99a1477cd005b23cbd09465276e302515e122df56bb9511aca1b"}, - {file = "wrapt-2.0.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:7d539241e87b650cbc4c3ac9f32c8d1ac8a54e510f6dca3f6ab60dcfd48c9b10"}, - {file = "wrapt-2.0.1-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:4811e15d88ee62dbf5c77f2c3ff3932b1e3ac92323ba3912f51fc4016ce81ecf"}, - {file = "wrapt-2.0.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c1c91405fcf1d501fa5d55df21e58ea49e6b879ae829f1039faaf7e5e509b41e"}, - {file = "wrapt-2.0.1-cp313-cp313t-win32.whl", hash = "sha256:e76e3f91f864e89db8b8d2a8311d57df93f01ad6bb1e9b9976d1f2e83e18315c"}, - {file = "wrapt-2.0.1-cp313-cp313t-win_amd64.whl", hash = "sha256:83ce30937f0ba0d28818807b303a412440c4b63e39d3d8fc036a94764b728c92"}, - {file = "wrapt-2.0.1-cp313-cp313t-win_arm64.whl", hash = "sha256:4b55cacc57e1dc2d0991dbe74c6419ffd415fb66474a02335cb10efd1aa3f84f"}, - {file = "wrapt-2.0.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:5e53b428f65ece6d9dad23cb87e64506392b720a0b45076c05354d27a13351a1"}, - {file = "wrapt-2.0.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:ad3ee9d0f254851c71780966eb417ef8e72117155cff04821ab9b60549694a55"}, - {file = "wrapt-2.0.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:d7b822c61ed04ee6ad64bc90d13368ad6eb094db54883b5dde2182f67a7f22c0"}, - {file = "wrapt-2.0.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7164a55f5e83a9a0b031d3ffab4d4e36bbec42e7025db560f225489fa929e509"}, - {file = "wrapt-2.0.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e60690ba71a57424c8d9ff28f8d006b7ad7772c22a4af432188572cd7fa004a1"}, - {file = "wrapt-2.0.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3cd1a4bd9a7a619922a8557e1318232e7269b5fb69d4ba97b04d20450a6bf970"}, - {file = "wrapt-2.0.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b4c2e3d777e38e913b8ce3a6257af72fb608f86a1df471cb1d4339755d0a807c"}, - {file = "wrapt-2.0.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:3d366aa598d69416b5afedf1faa539fac40c1d80a42f6b236c88c73a3c8f2d41"}, - {file = "wrapt-2.0.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c235095d6d090aa903f1db61f892fffb779c1eaeb2a50e566b52001f7a0f66ed"}, - {file = "wrapt-2.0.1-cp314-cp314-win32.whl", hash = "sha256:bfb5539005259f8127ea9c885bdc231978c06b7a980e63a8a61c8c4c979719d0"}, - {file = "wrapt-2.0.1-cp314-cp314-win_amd64.whl", hash = "sha256:4ae879acc449caa9ed43fc36ba08392b9412ee67941748d31d94e3cedb36628c"}, - {file = "wrapt-2.0.1-cp314-cp314-win_arm64.whl", hash = "sha256:8639b843c9efd84675f1e100ed9e99538ebea7297b62c4b45a7042edb84db03e"}, - {file = "wrapt-2.0.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:9219a1d946a9b32bb23ccae66bdb61e35c62773ce7ca6509ceea70f344656b7b"}, - {file = "wrapt-2.0.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:fa4184e74197af3adad3c889a1af95b53bb0466bced92ea99a0c014e48323eec"}, - {file = "wrapt-2.0.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c5ef2f2b8a53b7caee2f797ef166a390fef73979b15778a4a153e4b5fedce8fa"}, - {file = "wrapt-2.0.1-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:e042d653a4745be832d5aa190ff80ee4f02c34b21f4b785745eceacd0907b815"}, - {file = "wrapt-2.0.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2afa23318136709c4b23d87d543b425c399887b4057936cd20386d5b1422b6fa"}, - {file = "wrapt-2.0.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6c72328f668cf4c503ffcf9434c2b71fdd624345ced7941bc6693e61bbe36bef"}, - {file = "wrapt-2.0.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:3793ac154afb0e5b45d1233cb94d354ef7a983708cc3bb12563853b1d8d53747"}, - {file = "wrapt-2.0.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:fec0d993ecba3991645b4857837277469c8cc4c554a7e24d064d1ca291cfb81f"}, - {file = "wrapt-2.0.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:949520bccc1fa227274da7d03bf238be15389cd94e32e4297b92337df9b7a349"}, - {file = "wrapt-2.0.1-cp314-cp314t-win32.whl", hash = "sha256:be9e84e91d6497ba62594158d3d31ec0486c60055c49179edc51ee43d095f79c"}, - {file = "wrapt-2.0.1-cp314-cp314t-win_amd64.whl", hash = "sha256:61c4956171c7434634401db448371277d07032a81cc21c599c22953374781395"}, - {file = "wrapt-2.0.1-cp314-cp314t-win_arm64.whl", hash = "sha256:35cdbd478607036fee40273be8ed54a451f5f23121bd9d4be515158f9498f7ad"}, - {file = "wrapt-2.0.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:90897ea1cf0679763b62e79657958cd54eae5659f6360fc7d2ccc6f906342183"}, - {file = "wrapt-2.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:50844efc8cdf63b2d90cd3d62d4947a28311e6266ce5235a219d21b195b4ec2c"}, - {file = "wrapt-2.0.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:49989061a9977a8cbd6d20f2efa813f24bf657c6990a42967019ce779a878dbf"}, - {file = "wrapt-2.0.1-cp38-cp38-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:09c7476ab884b74dce081ad9bfd07fe5822d8600abade571cb1f66d5fc915af6"}, - {file = "wrapt-2.0.1-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d1a8a09a004ef100e614beec82862d11fc17d601092c3599afd22b1f36e4137e"}, - {file = "wrapt-2.0.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:89a82053b193837bf93c0f8a57ded6e4b6d88033a499dadff5067e912c2a41e9"}, - {file = "wrapt-2.0.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:f26f8e2ca19564e2e1fdbb6a0e47f36e0efbab1acc31e15471fad88f828c75f6"}, - {file = "wrapt-2.0.1-cp38-cp38-win32.whl", hash = "sha256:115cae4beed3542e37866469a8a1f2b9ec549b4463572b000611e9946b86e6f6"}, - {file = "wrapt-2.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:c4012a2bd37059d04f8209916aa771dfb564cccb86079072bdcd48a308b6a5c5"}, - {file = "wrapt-2.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:68424221a2dc00d634b54f92441914929c5ffb1c30b3b837343978343a3512a3"}, - {file = "wrapt-2.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6bd1a18f5a797fe740cb3d7a0e853a8ce6461cc62023b630caec80171a6b8097"}, - {file = "wrapt-2.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fb3a86e703868561c5cad155a15c36c716e1ab513b7065bd2ac8ed353c503333"}, - {file = "wrapt-2.0.1-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5dc1b852337c6792aa111ca8becff5bacf576bf4a0255b0f05eb749da6a1643e"}, - {file = "wrapt-2.0.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c046781d422f0830de6329fa4b16796096f28a92c8aef3850674442cdcb87b7f"}, - {file = "wrapt-2.0.1-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f73f9f7a0ebd0db139253d27e5fc8d2866ceaeef19c30ab5d69dcbe35e1a6981"}, - {file = "wrapt-2.0.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:b667189cf8efe008f55bbda321890bef628a67ab4147ebf90d182f2dadc78790"}, - {file = "wrapt-2.0.1-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:a9a83618c4f0757557c077ef71d708ddd9847ed66b7cc63416632af70d3e2308"}, - {file = "wrapt-2.0.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1e9b121e9aeb15df416c2c960b8255a49d44b4038016ee17af03975992d03931"}, - {file = "wrapt-2.0.1-cp39-cp39-win32.whl", hash = "sha256:1f186e26ea0a55f809f232e92cc8556a0977e00183c3ebda039a807a42be1494"}, - {file = "wrapt-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:bf4cb76f36be5de950ce13e22e7fdf462b35b04665a12b64f3ac5c1bbbcf3728"}, - {file = "wrapt-2.0.1-cp39-cp39-win_arm64.whl", hash = "sha256:d6cc985b9c8b235bd933990cdbf0f891f8e010b65a3911f7a55179cd7b0fc57b"}, - {file = "wrapt-2.0.1-py3-none-any.whl", hash = "sha256:4d2ce1bf1a48c5277d7969259232b57645aae5686dba1eaeade39442277afbca"}, - {file = "wrapt-2.0.1.tar.gz", hash = "sha256:9c9c635e78497cacb81e84f8b11b23e0aacac7a136e73b8e5b2109a1d9fc468f"}, -] - -[package.extras] -dev = ["pytest", "setuptools"] - [[package]] name = "wsproto" version = "1.2.0" @@ -3606,4 +3425,4 @@ cffi = ["cffi (>=1.17,<2.0) ; platform_python_implementation != \"PyPy\" and pyt [metadata] lock-version = "2.1" python-versions = "^3.13" -content-hash = "f04c63b0c4234a314bb92cfca4d92f11b4777e86eb7745db6f790a8de1f738ab" +content-hash = "7444318c768581455a6290f748f5e6503dda90959ab7a3a35166cacd85757927" diff --git a/catalyst-gateway/tests/api_tests/pyproject.toml b/catalyst-gateway/tests/api_tests/pyproject.toml index e24f47eec23..5628a3817f3 100644 --- a/catalyst-gateway/tests/api_tests/pyproject.toml +++ b/catalyst-gateway/tests/api_tests/pyproject.toml @@ -23,7 +23,7 @@ certifi = "^2025.1.31" cbor2 = "^5.6.5" uuid = "^1.30" httpx = "^0.28.1" -jsf = "^0.11.2" +ranjg = "^0.5.1.0" [tool.poetry.group.dev.dependencies] pytest = "^8.0.0" diff --git a/catalyst-gateway/tests/api_tests/utils/signed_doc.py b/catalyst-gateway/tests/api_tests/utils/signed_doc.py index 3d57fc0ee27..37a083de46f 100644 --- a/catalyst-gateway/tests/api_tests/utils/signed_doc.py +++ b/catalyst-gateway/tests/api_tests/utils/signed_doc.py @@ -10,7 +10,7 @@ from utils.rbac_chain import rbac_chain_factory, RoleID from utils.admin import AdminKey, admin_key from tempfile import NamedTemporaryFile -from jsf import JSF +import ranjg from enum import IntEnum @@ -202,7 +202,7 @@ def __factory__( template=template, parameters=[param] ) - content = JSF(template.content).generate() + content = ranjg.gen(template.content) rbac_chain = rbac_chain_factory() doc = SignedDocument(metadata, content) @@ -267,7 +267,7 @@ def category_parameters_doc( template=template, parameters=[param], ) - content = JSF(template.content).generate() + content = ranjg.gen(template.content) doc = SignedDocument(metadata, content) resp = document.put( @@ -323,7 +323,7 @@ def campaign_parameters_doc( parameters=[param], ) - content = JSF(template.content).generate() + content = ranjg.gen(template.content) doc = SignedDocument(metadata, content) resp = document.put( @@ -375,7 +375,7 @@ def brand_parameters_doc( content_type="application/json", template=template, ) - content = JSF(template.content).generate() + content = ranjg.gen(template.content) doc = SignedDocument(metadata, content) resp = document.put( From 59c49f8051cf317c04dba1ce03f04fc70c041b9c Mon Sep 17 00:00:00 2001 From: Mr-Leshiy Date: Thu, 27 Nov 2025 18:57:39 +0700 Subject: [PATCH 38/40] fix spelling --- .config/dictionaries/project.dic | 1 + 1 file changed, 1 insertion(+) diff --git a/.config/dictionaries/project.dic b/.config/dictionaries/project.dic index 0a2d74be760..9ee9e2524a1 100644 --- a/.config/dictionaries/project.dic +++ b/.config/dictionaries/project.dic @@ -294,6 +294,7 @@ pyproject pyright pytest qrcode +ranjg rapidoc ratelimit redoc From 2fee7931863eebfc54b336b829bc5d8afc6e2595 Mon Sep 17 00:00:00 2001 From: Apisit Ritruengroj Date: Thu, 27 Nov 2025 20:10:04 +0700 Subject: [PATCH 39/40] chore: minor cleanup --- catalyst-gateway/tests/api_tests/utils/admin.py | 6 +----- catalyst-gateway/tests/api_tests/utils/signed_doc.py | 1 - 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/catalyst-gateway/tests/api_tests/utils/admin.py b/catalyst-gateway/tests/api_tests/utils/admin.py index 1aa8019b9c5..6b973cfb6cc 100644 --- a/catalyst-gateway/tests/api_tests/utils/admin.py +++ b/catalyst-gateway/tests/api_tests/utils/admin.py @@ -1,7 +1,6 @@ import pytest -import base64 from pycardano.crypto.bip32 import BIP32ED25519PrivateKey, BIP32ED25519PublicKey -from utils.rbac_chain import ( +from rbac_chain import ( generate_rbac_auth_token, ONLY_ROLE_0_REG_JSON, RoleID, @@ -24,9 +23,6 @@ def pk(self) -> BIP32ED25519PublicKey: def pk_hex(self) -> str: return self.pk().public_key.hex() - def pk_base64url(self) -> str: - return base64.urlsafe_b64encode(self.pk().public_key).decode().rstrip("=") - def cat_id(self) -> str: return generate_cat_id( network="cardano", diff --git a/catalyst-gateway/tests/api_tests/utils/signed_doc.py b/catalyst-gateway/tests/api_tests/utils/signed_doc.py index 37a083de46f..5475fc85c12 100644 --- a/catalyst-gateway/tests/api_tests/utils/signed_doc.py +++ b/catalyst-gateway/tests/api_tests/utils/signed_doc.py @@ -11,7 +11,6 @@ from utils.admin import AdminKey, admin_key from tempfile import NamedTemporaryFile import ranjg -from enum import IntEnum DOC_TYPE = { From a464b3169696cb4aa3583dbcf3246a9ade414ce1 Mon Sep 17 00:00:00 2001 From: Mr-Leshiy Date: Thu, 27 Nov 2025 21:36:58 +0700 Subject: [PATCH 40/40] wip --- .../tests/api_tests/.python-version | 2 +- .../signed_doc/test_v1_signed_doc.py | 4 +- catalyst-gateway/tests/api_tests/poetry.lock | 290 +----------------- .../tests/api_tests/pyproject.toml | 3 +- .../{comment.json => comment.deprecated.json} | 0 .../test_data/signed_docs/proposal.json | 135 ++++++++ ...json => submission_action.deprecated.json} | 0 .../tests/api_tests/utils/admin.py | 2 +- .../tests/api_tests/utils/signed_doc.py | 15 +- 9 files changed, 152 insertions(+), 299 deletions(-) rename catalyst-gateway/tests/api_tests/test_data/signed_docs/{comment.json => comment.deprecated.json} (100%) create mode 100644 catalyst-gateway/tests/api_tests/test_data/signed_docs/proposal.json rename catalyst-gateway/tests/api_tests/test_data/signed_docs/{submission_action.json => submission_action.deprecated.json} (100%) diff --git a/catalyst-gateway/tests/api_tests/.python-version b/catalyst-gateway/tests/api_tests/.python-version index 24ee5b1be99..d62f67e9020 100644 --- a/catalyst-gateway/tests/api_tests/.python-version +++ b/catalyst-gateway/tests/api_tests/.python-version @@ -1 +1 @@ -3.13 +3.14.0rc1 diff --git a/catalyst-gateway/tests/api_tests/integration/signed_doc/test_v1_signed_doc.py b/catalyst-gateway/tests/api_tests/integration/signed_doc/test_v1_signed_doc.py index 9b2ed59f38b..38620d844e6 100644 --- a/catalyst-gateway/tests/api_tests/integration/signed_doc/test_v1_signed_doc.py +++ b/catalyst-gateway/tests/api_tests/integration/signed_doc/test_v1_signed_doc.py @@ -300,7 +300,7 @@ def deprecated_comment(rbac_chain, proposal_id): "ver": "0194d494-4402-7e0e-b8d6-171f8fea18b0", }, } - with open("./test_data/signed_docs/comment.json", "r") as json_file: + with open("./test_data/signed_docs/comment.deprecated.json", "r") as json_file: content = json.load(json_file) doc = SignedDocumentV1(comment_metadata_json, content) @@ -323,7 +323,7 @@ def deprecated_proposal_submission(rbac_chain, proposal_id): "ver": proposal_id, }, } - with open("./test_data/signed_docs/submission_action.json", "r") as json_file: + with open("./test_data/signed_docs/submission_action.deprecated.json", "r") as json_file: content = json.load(json_file) doc = SignedDocumentV1(sub_action_metadata_json, content) diff --git a/catalyst-gateway/tests/api_tests/poetry.lock b/catalyst-gateway/tests/api_tests/poetry.lock index 3fb7562d319..27bd288b0b1 100644 --- a/catalyst-gateway/tests/api_tests/poetry.lock +++ b/catalyst-gateway/tests/api_tests/poetry.lock @@ -129,10 +129,7 @@ files = [ ] [package.dependencies] -cffi = [ - {version = ">=1.0.1", markers = "python_version < \"3.14\""}, - {version = ">=2.0.0b1", markers = "python_version >= \"3.14\""}, -] +cffi = {version = ">=2.0.0b1", markers = "python_version >= \"3.14\""} [[package]] name = "asgiref" @@ -1540,43 +1537,6 @@ MarkupSafe = ">=2.0" [package.extras] i18n = ["Babel (>=2.7)"] -[[package]] -name = "jsonschema" -version = "4.25.1" -description = "An implementation of JSON Schema validation for Python" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "jsonschema-4.25.1-py3-none-any.whl", hash = "sha256:3fba0169e345c7175110351d456342c364814cfcf3b964ba4587f22915230a63"}, - {file = "jsonschema-4.25.1.tar.gz", hash = "sha256:e4a9655ce0da0c0b67a085847e00a3a51449e1157f4f75e9fb5aa545e122eb85"}, -] - -[package.dependencies] -attrs = ">=22.2.0" -jsonschema-specifications = ">=2023.03.6" -referencing = ">=0.28.4" -rpds-py = ">=0.7.1" - -[package.extras] -format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] -format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "rfc3987-syntax (>=1.1.0)", "uri-template", "webcolors (>=24.6.0)"] - -[[package]] -name = "jsonschema-specifications" -version = "2025.9.1" -description = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe"}, - {file = "jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d"}, -] - -[package.dependencies] -referencing = ">=0.31.0" - [[package]] name = "kaitaistruct" version = "0.11" @@ -2642,38 +2602,6 @@ files = [ {file = "pywin32-311-cp39-cp39-win_arm64.whl", hash = "sha256:62ea666235135fee79bb154e695f3ff67370afefd71bd7fea7512fc70ef31e3d"}, ] -[[package]] -name = "ranjg" -version = "0.5.1.0" -description = "Generate json text randomly" -optional = false -python-versions = "*" -groups = ["main"] -files = [ - {file = "ranjg-0.5.1.0-py3-none-any.whl", hash = "sha256:06be19f8dfd51294471a284e0c80c23ad2b443ec896505d69824ff07c1385848"}, - {file = "ranjg-0.5.1.0.tar.gz", hash = "sha256:7d82e879da8228e799f0bdf15ba46c7d5dfaa4df06f66eb3745475232e38fd3e"}, -] - -[package.dependencies] -jsonschema = "*" -rstr = "*" - -[[package]] -name = "referencing" -version = "0.37.0" -description = "JSON Referencing + Python" -optional = false -python-versions = ">=3.10" -groups = ["main"] -files = [ - {file = "referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231"}, - {file = "referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8"}, -] - -[package.dependencies] -attrs = ">=22.2.0" -rpds-py = ">=0.7.0" - [[package]] name = "requests" version = "2.32.5" @@ -2696,143 +2624,6 @@ urllib3 = ">=1.21.1,<3" socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] -[[package]] -name = "rpds-py" -version = "0.29.0" -description = "Python bindings to Rust's persistent data structures (rpds)" -optional = false -python-versions = ">=3.10" -groups = ["main"] -files = [ - {file = "rpds_py-0.29.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:4ae4b88c6617e1b9e5038ab3fccd7bac0842fdda2b703117b2aa99bc85379113"}, - {file = "rpds_py-0.29.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7d9128ec9d8cecda6f044001fde4fb71ea7c24325336612ef8179091eb9596b9"}, - {file = "rpds_py-0.29.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d37812c3da8e06f2bb35b3cf10e4a7b68e776a706c13058997238762b4e07f4f"}, - {file = "rpds_py-0.29.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:66786c3fb1d8de416a7fa8e1cb1ec6ba0a745b2b0eee42f9b7daa26f1a495545"}, - {file = "rpds_py-0.29.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b58f5c77f1af888b5fd1876c9a0d9858f6f88a39c9dd7c073a88e57e577da66d"}, - {file = "rpds_py-0.29.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:799156ef1f3529ed82c36eb012b5d7a4cf4b6ef556dd7cc192148991d07206ae"}, - {file = "rpds_py-0.29.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:453783477aa4f2d9104c4b59b08c871431647cb7af51b549bbf2d9eb9c827756"}, - {file = "rpds_py-0.29.0-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:24a7231493e3c4a4b30138b50cca089a598e52c34cf60b2f35cebf62f274fdea"}, - {file = "rpds_py-0.29.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7033c1010b1f57bb44d8067e8c25aa6fa2e944dbf46ccc8c92b25043839c3fd2"}, - {file = "rpds_py-0.29.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0248b19405422573621172ab8e3a1f29141362d13d9f72bafa2e28ea0cdca5a2"}, - {file = "rpds_py-0.29.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:f9f436aee28d13b9ad2c764fc273e0457e37c2e61529a07b928346b219fcde3b"}, - {file = "rpds_py-0.29.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:24a16cb7163933906c62c272de20ea3c228e4542c8c45c1d7dc2b9913e17369a"}, - {file = "rpds_py-0.29.0-cp310-cp310-win32.whl", hash = "sha256:1a409b0310a566bfd1be82119891fefbdce615ccc8aa558aff7835c27988cbef"}, - {file = "rpds_py-0.29.0-cp310-cp310-win_amd64.whl", hash = "sha256:c5523b0009e7c3c1263471b69d8da1c7d41b3ecb4cb62ef72be206b92040a950"}, - {file = "rpds_py-0.29.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:9b9c764a11fd637e0322a488560533112837f5334ffeb48b1be20f6d98a7b437"}, - {file = "rpds_py-0.29.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3fd2164d73812026ce970d44c3ebd51e019d2a26a4425a5dcbdfa93a34abc383"}, - {file = "rpds_py-0.29.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a097b7f7f7274164566ae90a221fd725363c0e9d243e2e9ed43d195ccc5495c"}, - {file = "rpds_py-0.29.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7cdc0490374e31cedefefaa1520d5fe38e82fde8748cbc926e7284574c714d6b"}, - {file = "rpds_py-0.29.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:89ca2e673ddd5bde9b386da9a0aac0cab0e76f40c8f0aaf0d6311b6bbf2aa311"}, - {file = "rpds_py-0.29.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a5d9da3ff5af1ca1249b1adb8ef0573b94c76e6ae880ba1852f033bf429d4588"}, - {file = "rpds_py-0.29.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8238d1d310283e87376c12f658b61e1ee23a14c0e54c7c0ce953efdbdc72deed"}, - {file = "rpds_py-0.29.0-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:2d6fb2ad1c36f91c4646989811e84b1ea5e0c3cf9690b826b6e32b7965853a63"}, - {file = "rpds_py-0.29.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:534dc9df211387547267ccdb42253aa30527482acb38dd9b21c5c115d66a96d2"}, - {file = "rpds_py-0.29.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d456e64724a075441e4ed648d7f154dc62e9aabff29bcdf723d0c00e9e1d352f"}, - {file = "rpds_py-0.29.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:a738f2da2f565989401bd6fd0b15990a4d1523c6d7fe83f300b7e7d17212feca"}, - {file = "rpds_py-0.29.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a110e14508fd26fd2e472bb541f37c209409876ba601cf57e739e87d8a53cf95"}, - {file = "rpds_py-0.29.0-cp311-cp311-win32.whl", hash = "sha256:923248a56dd8d158389a28934f6f69ebf89f218ef96a6b216a9be6861804d3f4"}, - {file = "rpds_py-0.29.0-cp311-cp311-win_amd64.whl", hash = "sha256:539eb77eb043afcc45314d1be09ea6d6cafb3addc73e0547c171c6d636957f60"}, - {file = "rpds_py-0.29.0-cp311-cp311-win_arm64.whl", hash = "sha256:bdb67151ea81fcf02d8f494703fb728d4d34d24556cbff5f417d74f6f5792e7c"}, - {file = "rpds_py-0.29.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a0891cfd8db43e085c0ab93ab7e9b0c8fee84780d436d3b266b113e51e79f954"}, - {file = "rpds_py-0.29.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3897924d3f9a0361472d884051f9a2460358f9a45b1d85a39a158d2f8f1ad71c"}, - {file = "rpds_py-0.29.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a21deb8e0d1571508c6491ce5ea5e25669b1dd4adf1c9d64b6314842f708b5d"}, - {file = "rpds_py-0.29.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9efe71687d6427737a0a2de9ca1c0a216510e6cd08925c44162be23ed7bed2d5"}, - {file = "rpds_py-0.29.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:40f65470919dc189c833e86b2c4bd21bd355f98436a2cef9e0a9a92aebc8e57e"}, - {file = "rpds_py-0.29.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:def48ff59f181130f1a2cb7c517d16328efac3ec03951cca40c1dc2049747e83"}, - {file = "rpds_py-0.29.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad7bd570be92695d89285a4b373006930715b78d96449f686af422debb4d3949"}, - {file = "rpds_py-0.29.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:5a572911cd053137bbff8e3a52d31c5d2dba51d3a67ad902629c70185f3f2181"}, - {file = "rpds_py-0.29.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d583d4403bcbf10cffc3ab5cee23d7643fcc960dff85973fd3c2d6c86e8dbb0c"}, - {file = "rpds_py-0.29.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:070befbb868f257d24c3bb350dbd6e2f645e83731f31264b19d7231dd5c396c7"}, - {file = "rpds_py-0.29.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:fc935f6b20b0c9f919a8ff024739174522abd331978f750a74bb68abd117bd19"}, - {file = "rpds_py-0.29.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8c5a8ecaa44ce2d8d9d20a68a2483a74c07f05d72e94a4dff88906c8807e77b0"}, - {file = "rpds_py-0.29.0-cp312-cp312-win32.whl", hash = "sha256:ba5e1aeaf8dd6d8f6caba1f5539cddda87d511331714b7b5fc908b6cfc3636b7"}, - {file = "rpds_py-0.29.0-cp312-cp312-win_amd64.whl", hash = "sha256:b5f6134faf54b3cb83375db0f113506f8b7770785be1f95a631e7e2892101977"}, - {file = "rpds_py-0.29.0-cp312-cp312-win_arm64.whl", hash = "sha256:b016eddf00dca7944721bf0cd85b6af7f6c4efaf83ee0b37c4133bd39757a8c7"}, - {file = "rpds_py-0.29.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1585648d0760b88292eecab5181f5651111a69d90eff35d6b78aa32998886a61"}, - {file = "rpds_py-0.29.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:521807963971a23996ddaf764c682b3e46459b3c58ccd79fefbe16718db43154"}, - {file = "rpds_py-0.29.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a8896986efaa243ab713c69e6491a4138410f0fe36f2f4c71e18bd5501e8014"}, - {file = "rpds_py-0.29.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1d24564a700ef41480a984c5ebed62b74e6ce5860429b98b1fede76049e953e6"}, - {file = "rpds_py-0.29.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e6596b93c010d386ae46c9fba9bfc9fc5965fa8228edeac51576299182c2e31c"}, - {file = "rpds_py-0.29.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5cc58aac218826d054c7da7f95821eba94125d88be673ff44267bb89d12a5866"}, - {file = "rpds_py-0.29.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de73e40ebc04dd5d9556f50180395322193a78ec247e637e741c1b954810f295"}, - {file = "rpds_py-0.29.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:295ce5ac7f0cf69a651ea75c8f76d02a31f98e5698e82a50a5f4d4982fbbae3b"}, - {file = "rpds_py-0.29.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1ea59b23ea931d494459c8338056fe7d93458c0bf3ecc061cd03916505369d55"}, - {file = "rpds_py-0.29.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f49d41559cebd608042fdcf54ba597a4a7555b49ad5c1c0c03e0af82692661cd"}, - {file = "rpds_py-0.29.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:05a2bd42768ea988294ca328206efbcc66e220d2d9b7836ee5712c07ad6340ea"}, - {file = "rpds_py-0.29.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:33ca7bdfedd83339ca55da3a5e1527ee5870d4b8369456b5777b197756f3ca22"}, - {file = "rpds_py-0.29.0-cp313-cp313-win32.whl", hash = "sha256:20c51ae86a0bb9accc9ad4e6cdeec58d5ebb7f1b09dd4466331fc65e1766aae7"}, - {file = "rpds_py-0.29.0-cp313-cp313-win_amd64.whl", hash = "sha256:6410e66f02803600edb0b1889541f4b5cc298a5ccda0ad789cc50ef23b54813e"}, - {file = "rpds_py-0.29.0-cp313-cp313-win_arm64.whl", hash = "sha256:56838e1cd9174dc23c5691ee29f1d1be9eab357f27efef6bded1328b23e1ced2"}, - {file = "rpds_py-0.29.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:37d94eadf764d16b9a04307f2ab1d7af6dc28774bbe0535c9323101e14877b4c"}, - {file = "rpds_py-0.29.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d472cf73efe5726a067dce63eebe8215b14beabea7c12606fd9994267b3cfe2b"}, - {file = "rpds_py-0.29.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:72fdfd5ff8992e4636621826371e3ac5f3e3b8323e9d0e48378e9c13c3dac9d0"}, - {file = "rpds_py-0.29.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2549d833abdf8275c901313b9e8ff8fba57e50f6a495035a2a4e30621a2f7cc4"}, - {file = "rpds_py-0.29.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4448dad428f28a6a767c3e3b80cde3446a22a0efbddaa2360f4bb4dc836d0688"}, - {file = "rpds_py-0.29.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:115f48170fd4296a33938d8c11f697f5f26e0472e43d28f35624764173a60e4d"}, - {file = "rpds_py-0.29.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e5bb73ffc029820f4348e9b66b3027493ae00bca6629129cd433fd7a76308ee"}, - {file = "rpds_py-0.29.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:b1581fcde18fcdf42ea2403a16a6b646f8eb1e58d7f90a0ce693da441f76942e"}, - {file = "rpds_py-0.29.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:16e9da2bda9eb17ea318b4c335ec9ac1818e88922cbe03a5743ea0da9ecf74fb"}, - {file = "rpds_py-0.29.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:28fd300326dd21198f311534bdb6d7e989dd09b3418b3a91d54a0f384c700967"}, - {file = "rpds_py-0.29.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2aba991e041d031c7939e1358f583ae405a7bf04804ca806b97a5c0e0af1ea5e"}, - {file = "rpds_py-0.29.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:7f437026dbbc3f08c99cc41a5b2570c6e1a1ddbe48ab19a9b814254128d4ea7a"}, - {file = "rpds_py-0.29.0-cp313-cp313t-win32.whl", hash = "sha256:6e97846e9800a5d0fe7be4d008f0c93d0feeb2700da7b1f7528dabafb31dfadb"}, - {file = "rpds_py-0.29.0-cp313-cp313t-win_amd64.whl", hash = "sha256:f49196aec7c4b406495f60e6f947ad71f317a765f956d74bbd83996b9edc0352"}, - {file = "rpds_py-0.29.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:394d27e4453d3b4d82bb85665dc1fcf4b0badc30fc84282defed71643b50e1a1"}, - {file = "rpds_py-0.29.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:55d827b2ae95425d3be9bc9a5838b6c29d664924f98146557f7715e331d06df8"}, - {file = "rpds_py-0.29.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc31a07ed352e5462d3ee1b22e89285f4ce97d5266f6d1169da1142e78045626"}, - {file = "rpds_py-0.29.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c4695dd224212f6105db7ea62197144230b808d6b2bba52238906a2762f1d1e7"}, - {file = "rpds_py-0.29.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcae1770b401167f8b9e1e3f566562e6966ffa9ce63639916248a9e25fa8a244"}, - {file = "rpds_py-0.29.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:90f30d15f45048448b8da21c41703b31c61119c06c216a1bf8c245812a0f0c17"}, - {file = "rpds_py-0.29.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:44a91e0ab77bdc0004b43261a4b8cd6d6b451e8d443754cfda830002b5745b32"}, - {file = "rpds_py-0.29.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:4aa195e5804d32c682e453b34474f411ca108e4291c6a0f824ebdc30a91c973c"}, - {file = "rpds_py-0.29.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7971bdb7bf4ee0f7e6f67fa4c7fbc6019d9850cc977d126904392d363f6f8318"}, - {file = "rpds_py-0.29.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8ae33ad9ce580c7a47452c3b3f7d8a9095ef6208e0a0c7e4e2384f9fc5bf8212"}, - {file = "rpds_py-0.29.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:c661132ab2fb4eeede2ef69670fd60da5235209874d001a98f1542f31f2a8a94"}, - {file = "rpds_py-0.29.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:bb78b3a0d31ac1bde132c67015a809948db751cb4e92cdb3f0b242e430b6ed0d"}, - {file = "rpds_py-0.29.0-cp314-cp314-win32.whl", hash = "sha256:f475f103488312e9bd4000bc890a95955a07b2d0b6e8884aef4be56132adbbf1"}, - {file = "rpds_py-0.29.0-cp314-cp314-win_amd64.whl", hash = "sha256:b9cf2359a4fca87cfb6801fae83a76aedf66ee1254a7a151f1341632acf67f1b"}, - {file = "rpds_py-0.29.0-cp314-cp314-win_arm64.whl", hash = "sha256:9ba8028597e824854f0f1733d8b964e914ae3003b22a10c2c664cb6927e0feb9"}, - {file = "rpds_py-0.29.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:e71136fd0612556b35c575dc2726ae04a1669e6a6c378f2240312cf5d1a2ab10"}, - {file = "rpds_py-0.29.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:76fe96632d53f3bf0ea31ede2f53bbe3540cc2736d4aec3b3801b0458499ef3a"}, - {file = "rpds_py-0.29.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9459a33f077130dbb2c7c3cea72ee9932271fb3126404ba2a2661e4fe9eb7b79"}, - {file = "rpds_py-0.29.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5c9546cfdd5d45e562cc0444b6dddc191e625c62e866bf567a2c69487c7ad28a"}, - {file = "rpds_py-0.29.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12597d11d97b8f7e376c88929a6e17acb980e234547c92992f9f7c058f1a7310"}, - {file = "rpds_py-0.29.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28de03cf48b8a9e6ec10318f2197b83946ed91e2891f651a109611be4106ac4b"}, - {file = "rpds_py-0.29.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd7951c964069039acc9d67a8ff1f0a7f34845ae180ca542b17dc1456b1f1808"}, - {file = "rpds_py-0.29.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:c07d107b7316088f1ac0177a7661ca0c6670d443f6fe72e836069025e6266761"}, - {file = "rpds_py-0.29.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1de2345af363d25696969befc0c1688a6cb5e8b1d32b515ef84fc245c6cddba3"}, - {file = "rpds_py-0.29.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:00e56b12d2199ca96068057e1ae7f9998ab6e99cda82431afafd32f3ec98cca9"}, - {file = "rpds_py-0.29.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:3919a3bbecee589300ed25000b6944174e07cd20db70552159207b3f4bbb45b8"}, - {file = "rpds_py-0.29.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e7fa2ccc312bbd91e43aa5e0869e46bc03278a3dddb8d58833150a18b0f0283a"}, - {file = "rpds_py-0.29.0-cp314-cp314t-win32.whl", hash = "sha256:97c817863ffc397f1e6a6e9d2d89fe5408c0a9922dac0329672fb0f35c867ea5"}, - {file = "rpds_py-0.29.0-cp314-cp314t-win_amd64.whl", hash = "sha256:2023473f444752f0f82a58dfcbee040d0a1b3d1b3c2ec40e884bd25db6d117d2"}, - {file = "rpds_py-0.29.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:acd82a9e39082dc5f4492d15a6b6c8599aa21db5c35aaf7d6889aea16502c07d"}, - {file = "rpds_py-0.29.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:715b67eac317bf1c7657508170a3e011a1ea6ccb1c9d5f296e20ba14196be6b3"}, - {file = "rpds_py-0.29.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3b1b87a237cb2dba4db18bcfaaa44ba4cd5936b91121b62292ff21df577fc43"}, - {file = "rpds_py-0.29.0-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1c3c3e8101bb06e337c88eb0c0ede3187131f19d97d43ea0e1c5407ea74c0cbf"}, - {file = "rpds_py-0.29.0-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2b8e54d6e61f3ecd3abe032065ce83ea63417a24f437e4a3d73d2f85ce7b7cfe"}, - {file = "rpds_py-0.29.0-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3fbd4e9aebf110473a420dea85a238b254cf8a15acb04b22a5a6b5ce8925b760"}, - {file = "rpds_py-0.29.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80fdf53d36e6c72819993e35d1ebeeb8e8fc688d0c6c2b391b55e335b3afba5a"}, - {file = "rpds_py-0.29.0-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:ea7173df5d86f625f8dde6d5929629ad811ed8decda3b60ae603903839ac9ac0"}, - {file = "rpds_py-0.29.0-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:76054d540061eda273274f3d13a21a4abdde90e13eaefdc205db37c05230efce"}, - {file = "rpds_py-0.29.0-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:9f84c549746a5be3bc7415830747a3a0312573afc9f95785eb35228bb17742ec"}, - {file = "rpds_py-0.29.0-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:0ea962671af5cb9a260489e311fa22b2e97103e3f9f0caaea6f81390af96a9ed"}, - {file = "rpds_py-0.29.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:f7728653900035fb7b8d06e1e5900545d8088efc9d5d4545782da7df03ec803f"}, - {file = "rpds_py-0.29.0.tar.gz", hash = "sha256:fe55fe686908f50154d1dc599232016e50c243b438c3b7432f24e2895b0e5359"}, -] - -[[package]] -name = "rstr" -version = "3.2.2" -description = "Generate random strings in Python" -optional = false -python-versions = ">=3.7" -groups = ["main"] -files = [ - {file = "rstr-3.2.2-py3-none-any.whl", hash = "sha256:f39195d38da1748331eeec52f1276e71eb6295e7949beea91a5e9af2340d7b3b"}, - {file = "rstr-3.2.2.tar.gz", hash = "sha256:c4a564d4dfb4472d931d145c43d1cf1ad78c24592142e7755b8866179eeac012"}, -] - [[package]] name = "ruamel-yaml" version = "0.18.16" @@ -2845,85 +2636,10 @@ files = [ {file = "ruamel.yaml-0.18.16.tar.gz", hash = "sha256:a6e587512f3c998b2225d68aa1f35111c29fad14aed561a26e73fab729ec5e5a"}, ] -[package.dependencies] -"ruamel.yaml.clib" = {version = ">=0.2.7", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.14\""} - [package.extras] docs = ["mercurial (>5.7)", "ryd"] jinja2 = ["ruamel.yaml.jinja2 (>=0.2)"] -[[package]] -name = "ruamel-yaml-clib" -version = "0.2.15" -description = "C version of reader, parser and emitter for ruamel.yaml derived from libyaml" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -markers = "platform_python_implementation == \"CPython\" and python_version == \"3.13\"" -files = [ - {file = "ruamel_yaml_clib-0.2.15-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:88eea8baf72f0ccf232c22124d122a7f26e8a24110a0273d9bcddcb0f7e1fa03"}, - {file = "ruamel_yaml_clib-0.2.15-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9b6f7d74d094d1f3a4e157278da97752f16ee230080ae331fcc219056ca54f77"}, - {file = "ruamel_yaml_clib-0.2.15-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:4be366220090d7c3424ac2b71c90d1044ea34fca8c0b88f250064fd06087e614"}, - {file = "ruamel_yaml_clib-0.2.15-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1f66f600833af58bea694d5892453f2270695b92200280ee8c625ec5a477eed3"}, - {file = "ruamel_yaml_clib-0.2.15-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:da3d6adadcf55a93c214d23941aef4abfd45652110aed6580e814152f385b862"}, - {file = "ruamel_yaml_clib-0.2.15-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e9fde97ecb7bb9c41261c2ce0da10323e9227555c674989f8d9eb7572fc2098d"}, - {file = "ruamel_yaml_clib-0.2.15-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:05c70f7f86be6f7bee53794d80050a28ae7e13e4a0087c1839dcdefd68eb36b6"}, - {file = "ruamel_yaml_clib-0.2.15-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:6f1d38cbe622039d111b69e9ca945e7e3efebb30ba998867908773183357f3ed"}, - {file = "ruamel_yaml_clib-0.2.15-cp310-cp310-win32.whl", hash = "sha256:fe239bdfdae2302e93bd6e8264bd9b71290218fff7084a9db250b55caaccf43f"}, - {file = "ruamel_yaml_clib-0.2.15-cp310-cp310-win_amd64.whl", hash = "sha256:468858e5cbde0198337e6a2a78eda8c3fb148bdf4c6498eaf4bc9ba3f8e780bd"}, - {file = "ruamel_yaml_clib-0.2.15-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c583229f336682b7212a43d2fa32c30e643d3076178fb9f7a6a14dde85a2d8bd"}, - {file = "ruamel_yaml_clib-0.2.15-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:56ea19c157ed8c74b6be51b5fa1c3aff6e289a041575f0556f66e5fb848bb137"}, - {file = "ruamel_yaml_clib-0.2.15-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5fea0932358e18293407feb921d4f4457db837b67ec1837f87074667449f9401"}, - {file = "ruamel_yaml_clib-0.2.15-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef71831bd61fbdb7aa0399d5c4da06bea37107ab5c79ff884cc07f2450910262"}, - {file = "ruamel_yaml_clib-0.2.15-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:617d35dc765715fa86f8c3ccdae1e4229055832c452d4ec20856136acc75053f"}, - {file = "ruamel_yaml_clib-0.2.15-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1b45498cc81a4724a2d42273d6cfc243c0547ad7c6b87b4f774cb7bcc131c98d"}, - {file = "ruamel_yaml_clib-0.2.15-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:def5663361f6771b18646620fca12968aae730132e104688766cf8a3b1d65922"}, - {file = "ruamel_yaml_clib-0.2.15-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:014181cdec565c8745b7cbc4de3bf2cc8ced05183d986e6d1200168e5bb59490"}, - {file = "ruamel_yaml_clib-0.2.15-cp311-cp311-win32.whl", hash = "sha256:d290eda8f6ada19e1771b54e5706b8f9807e6bb08e873900d5ba114ced13e02c"}, - {file = "ruamel_yaml_clib-0.2.15-cp311-cp311-win_amd64.whl", hash = "sha256:bdc06ad71173b915167702f55d0f3f027fc61abd975bd308a0968c02db4a4c3e"}, - {file = "ruamel_yaml_clib-0.2.15-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cb15a2e2a90c8475df45c0949793af1ff413acfb0a716b8b94e488ea95ce7cff"}, - {file = "ruamel_yaml_clib-0.2.15-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:64da03cbe93c1e91af133f5bec37fd24d0d4ba2418eaf970d7166b0a26a148a2"}, - {file = "ruamel_yaml_clib-0.2.15-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f6d3655e95a80325b84c4e14c080b2470fe4f33b6846f288379ce36154993fb1"}, - {file = "ruamel_yaml_clib-0.2.15-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:71845d377c7a47afc6592aacfea738cc8a7e876d586dfba814501d8c53c1ba60"}, - {file = "ruamel_yaml_clib-0.2.15-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11e5499db1ccbc7f4b41f0565e4f799d863ea720e01d3e99fa0b7b5fcd7802c9"}, - {file = "ruamel_yaml_clib-0.2.15-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4b293a37dc97e2b1e8a1aec62792d1e52027087c8eea4fc7b5abd2bdafdd6642"}, - {file = "ruamel_yaml_clib-0.2.15-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:512571ad41bba04eac7268fe33f7f4742210ca26a81fe0c75357fa682636c690"}, - {file = "ruamel_yaml_clib-0.2.15-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e5e9f630c73a490b758bf14d859a39f375e6999aea5ddd2e2e9da89b9953486a"}, - {file = "ruamel_yaml_clib-0.2.15-cp312-cp312-win32.whl", hash = "sha256:f4421ab780c37210a07d138e56dd4b51f8642187cdfb433eb687fe8c11de0144"}, - {file = "ruamel_yaml_clib-0.2.15-cp312-cp312-win_amd64.whl", hash = "sha256:2b216904750889133d9222b7b873c199d48ecbb12912aca78970f84a5aa1a4bc"}, - {file = "ruamel_yaml_clib-0.2.15-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4dcec721fddbb62e60c2801ba08c87010bd6b700054a09998c4d09c08147b8fb"}, - {file = "ruamel_yaml_clib-0.2.15-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:65f48245279f9bb301d1276f9679b82e4c080a1ae25e679f682ac62446fac471"}, - {file = "ruamel_yaml_clib-0.2.15-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:46895c17ead5e22bea5e576f1db7e41cb273e8d062c04a6a49013d9f60996c25"}, - {file = "ruamel_yaml_clib-0.2.15-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3eb199178b08956e5be6288ee0b05b2fb0b5c1f309725ad25d9c6ea7e27f962a"}, - {file = "ruamel_yaml_clib-0.2.15-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4d1032919280ebc04a80e4fb1e93f7a738129857eaec9448310e638c8bccefcf"}, - {file = "ruamel_yaml_clib-0.2.15-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ab0df0648d86a7ecbd9c632e8f8d6b21bb21b5fc9d9e095c796cacf32a728d2d"}, - {file = "ruamel_yaml_clib-0.2.15-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:331fb180858dd8534f0e61aa243b944f25e73a4dae9962bd44c46d1761126bbf"}, - {file = "ruamel_yaml_clib-0.2.15-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fd4c928ddf6bce586285daa6d90680b9c291cfd045fc40aad34e445d57b1bf51"}, - {file = "ruamel_yaml_clib-0.2.15-cp313-cp313-win32.whl", hash = "sha256:bf0846d629e160223805db9fe8cc7aec16aaa11a07310c50c8c7164efa440aec"}, - {file = "ruamel_yaml_clib-0.2.15-cp313-cp313-win_amd64.whl", hash = "sha256:45702dfbea1420ba3450bb3dd9a80b33f0badd57539c6aac09f42584303e0db6"}, - {file = "ruamel_yaml_clib-0.2.15-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:753faf20b3a5906faf1fc50e4ddb8c074cb9b251e00b14c18b28492f933ac8ef"}, - {file = "ruamel_yaml_clib-0.2.15-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:480894aee0b29752560a9de46c0e5f84a82602f2bc5c6cde8db9a345319acfdf"}, - {file = "ruamel_yaml_clib-0.2.15-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:4d3b58ab2454b4747442ac76fab66739c72b1e2bb9bd173d7694b9f9dbc9c000"}, - {file = "ruamel_yaml_clib-0.2.15-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bfd309b316228acecfa30670c3887dcedf9b7a44ea39e2101e75d2654522acd4"}, - {file = "ruamel_yaml_clib-0.2.15-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2812ff359ec1f30129b62372e5f22a52936fac13d5d21e70373dbca5d64bb97c"}, - {file = "ruamel_yaml_clib-0.2.15-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7e74ea87307303ba91073b63e67f2c667e93f05a8c63079ee5b7a5c8d0d7b043"}, - {file = "ruamel_yaml_clib-0.2.15-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:713cd68af9dfbe0bb588e144a61aad8dcc00ef92a82d2e87183ca662d242f524"}, - {file = "ruamel_yaml_clib-0.2.15-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:542d77b72786a35563f97069b9379ce762944e67055bea293480f7734b2c7e5e"}, - {file = "ruamel_yaml_clib-0.2.15-cp314-cp314-win32.whl", hash = "sha256:424ead8cef3939d690c4b5c85ef5b52155a231ff8b252961b6516ed7cf05f6aa"}, - {file = "ruamel_yaml_clib-0.2.15-cp314-cp314-win_amd64.whl", hash = "sha256:ac9b8d5fa4bb7fd2917ab5027f60d4234345fd366fe39aa711d5dca090aa1467"}, - {file = "ruamel_yaml_clib-0.2.15-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:923816815974425fbb1f1bf57e85eca6e14d8adc313c66db21c094927ad01815"}, - {file = "ruamel_yaml_clib-0.2.15-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:dcc7f3162d3711fd5d52e2267e44636e3e566d1e5675a5f0b30e98f2c4af7974"}, - {file = "ruamel_yaml_clib-0.2.15-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5d3c9210219cbc0f22706f19b154c9a798ff65a6beeafbf77fc9c057ec806f7d"}, - {file = "ruamel_yaml_clib-0.2.15-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1bb7b728fd9f405aa00b4a0b17ba3f3b810d0ccc5f77f7373162e9b5f0ff75d5"}, - {file = "ruamel_yaml_clib-0.2.15-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3cb75a3c14f1d6c3c2a94631e362802f70e83e20d1f2b2ef3026c05b415c4900"}, - {file = "ruamel_yaml_clib-0.2.15-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:badd1d7283f3e5894779a6ea8944cc765138b96804496c91812b2829f70e18a7"}, - {file = "ruamel_yaml_clib-0.2.15-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:0ba6604bbc3dfcef844631932d06a1a4dcac3fee904efccf582261948431628a"}, - {file = "ruamel_yaml_clib-0.2.15-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a8220fd4c6f98485e97aea65e1df76d4fed1678ede1fe1d0eed2957230d287c4"}, - {file = "ruamel_yaml_clib-0.2.15-cp39-cp39-win32.whl", hash = "sha256:04d21dc9c57d9608225da28285900762befbb0165ae48482c15d8d4989d4af14"}, - {file = "ruamel_yaml_clib-0.2.15-cp39-cp39-win_amd64.whl", hash = "sha256:27dc656e84396e6d687f97c6e65fb284d100483628f02d95464fd731743a4afe"}, - {file = "ruamel_yaml_clib-0.2.15.tar.gz", hash = "sha256:46e4cc8c43ef6a94885f72512094e482114a8a706d3c555a34ed4b0d20200600"}, -] - [[package]] name = "ruff" version = "0.14.6" @@ -3424,5 +3140,5 @@ cffi = ["cffi (>=1.17,<2.0) ; platform_python_implementation != \"PyPy\" and pyt [metadata] lock-version = "2.1" -python-versions = "^3.13" -content-hash = "7444318c768581455a6290f748f5e6503dda90959ab7a3a35166cacd85757927" +python-versions = "^3.14" +content-hash = "5407cabce2465b2970f233b0aa8047052d5780c7897e7873605a5644dcc005ef" diff --git a/catalyst-gateway/tests/api_tests/pyproject.toml b/catalyst-gateway/tests/api_tests/pyproject.toml index 5628a3817f3..2db4772ba30 100644 --- a/catalyst-gateway/tests/api_tests/pyproject.toml +++ b/catalyst-gateway/tests/api_tests/pyproject.toml @@ -13,7 +13,7 @@ packages = [ ] [tool.poetry.dependencies] -python = "^3.13" +python = "^3.14" loguru = "^0.7.2" asyncpg = "^0.30.0" requests = "^2.32.3" @@ -23,7 +23,6 @@ certifi = "^2025.1.31" cbor2 = "^5.6.5" uuid = "^1.30" httpx = "^0.28.1" -ranjg = "^0.5.1.0" [tool.poetry.group.dev.dependencies] pytest = "^8.0.0" diff --git a/catalyst-gateway/tests/api_tests/test_data/signed_docs/comment.json b/catalyst-gateway/tests/api_tests/test_data/signed_docs/comment.deprecated.json similarity index 100% rename from catalyst-gateway/tests/api_tests/test_data/signed_docs/comment.json rename to catalyst-gateway/tests/api_tests/test_data/signed_docs/comment.deprecated.json diff --git a/catalyst-gateway/tests/api_tests/test_data/signed_docs/proposal.json b/catalyst-gateway/tests/api_tests/test_data/signed_docs/proposal.json new file mode 100644 index 00000000000..e94dbe9df23 --- /dev/null +++ b/catalyst-gateway/tests/api_tests/test_data/signed_docs/proposal.json @@ -0,0 +1,135 @@ +{ + "$schema": "./0199802c-21b4-7d6c-aacd-54aa31fe1e4c.schema.json", + "setup": { + "title": { + "title": "A Compact DApp for Private KYC Verification PoC" + }, + "proposer": { + "applicant": "Jane Doe", + "type": "Individual" + } + }, + "summary": { + "budget": { + "requestedFunds": 8500 + }, + "time": { + "duration": 3 + }, + "translation": { + "isTranslated": false + }, + "problem": { + "statement": "Existing on-chain identity verification methods expose sensitive personal data publicly, which is a major barrier to enterprise adoption requiring regulatory compliance and data protection." + }, + "solution": { + "summary": "We will build a proof-of-concept Compact DApp that uses zero-knowledge proofs to perform private, selective disclosure of KYC status without revealing the underlying personal information on the blockchain." + }, + "supportingLinks": { + "links": [ + "https://github.com/janedoe/compact-kyc-repo-main", + "https://github.com/janedoe/zkp-experiments" + ] + }, + "dependencies": { + "hasDependency": false, + "dependencyDetail": "No dependencies" + }, + "open_source": { + "isOpenSource": true, + "openSourceInformation": "The entire codebase, including the Compact contract, demo UI, and test suite, will be released under the MIT License on a public GitHub repository." + } + }, + "theme": { + "theme": { + "grouped_tag": { + "group": "Identity & Security", + "tag": "Privacy" + } + } + }, + "campaign_category": { + "category_details": { + "details": {} + }, + "category_questions": { + "dapp_purpose": "The DApp is useful within the **Financial Services** vertical by demonstrating a privacy-preserving KYC mechanism. This is a foundational step for regulated DeFi and lending platforms that need to verify user compliance (necessity) without becoming data honeypots (pattern: selective disclosure). It requires Midnight + Compact because the **proof logic must be executed privately** to ensure the user's data remains off-chain and only the valid proof of KYC is submitted. This is the core novelty, demonstrating a critical compliance primitive that public ledgers cannot offer.\n\n***\n\n* **Vertical:** Financial Services (Regulated DeFi/Lending)\n* **Pattern:** Selective Disclosure (Proof of KYC without revealing ID)\n* **Necessity:** Compliance with strict data protection regulations (e.g., GDPR).\n* **Novelty:** Integration of Compact ZK proofs for an end-to-end, off-chain data verification and on-chain proof-of-compliance flow.", + "build_goal": "We will build a **Private KYC Verification Compact DApp PoC**.\n\n* **Compact Contract(s):** `KycVerifier.compact` with a single key function, `verify_kyc_proof(proof: ZKProof) -> bool`, which checks the validity of an off-chain generated proof against a known verification key.\n* **Key Functions/Proofs:** The core proof function will assert: 'I, the prover, have a valid KYC document that meets $X$ criteria (e.g., Age > 18, not on sanction list) issued by $Y$ authority.'\n* **Demo UI Flow:**\n 1. User connects Lace (Midnight) wallet.\n 2. User uploads/selects their private KYC data in the client-side DApp (simulated).\n 3. DApp generates the ZK proof using a client-side library.\n 4. User submits the proof via a Compact transaction.\n 5. DApp displays 'KYC Verified' or 'Verification Failed'.\n* **Lace (Midnight) Wallet Integration:** Used for authenticating the user and submitting the Compact transaction containing the ZK proof.\n* **Basic Test Plan:** Include unit tests for the Compact contract's `verify_kyc_proof` function using pre-generated valid/invalid proofs. End-to-end test script to simulate the UI flow from proof generation to on-chain submission.", + "developer_reuse": "The repository will be structured to serve as a **reference implementation** for integrating privacy primitives into DApps.\n\n* **Repo Structure:** `/compact-contract` (Compact source, tests), `/demo-ui` (React/TS demo), `/docs` (tutorials, architecture).\n* **README Contents:** Clear getting started guide, quick-start commands (e.g., `npm run compact:build`, `npm run ui:dev`), and a conceptual overview explaining the ZK-KYC flow.\n* **Docs/Tutorials:** A tutorial on 'How to build your own selective disclosure proof' and an API-level document for the Compact contract entrypoints.\n* **Test Instructions:** Detailed instructions on how to run both Compact unit tests and the end-to-end flow test.\n* **Extension Points:** The contract will be modular, allowing developers to easily swap out the ZK-proof generation/verification parameters to suit different compliance needs.\n* **Benefiting Personas:** **DApp Devs** (reusable contract/UI), **zk-curious developers** (easy-to-read reference code), **Integrators** (clear API/flow docs).\n* **Gauge Impact:** Track **forks** (direct code reuse), **stars** (general interest), and monitor **issues/discussions** for community-led extensions or remixes of the core logic." + } + }, + "details": { + "solution": { + "solution": "The proposed solution is a minimal, fully functional **Proof of Concept (PoC)** for a **Private KYC Verification DApp**. It utilizes Midnight's **Compact Contracts** to verify zero-knowledge proofs (ZKP) of off-chain private data. This addresses the problem of data exposure by ensuring that the only information submitted to the blockchain is a cryptographic proof that the user meets a specific compliance criterion (e.g., 'verified by ID provider $X$'), without revealing the user's name, address, or ID details. The technical approach involves: \n\n1. **Client-Side Proof Generation:** User's data is verified off-chain by an oracle/client, which then generates a ZKP stating a fact about the data. \n2. **Compact Contract Verification:** The ZKP is submitted via a Compact transaction to the `KycVerifier.compact` contract, which verifies the proof's validity privately. \n3. **On-Chain State Update:** A minimal, non-identifying state (e.g., a simple boolean 'is_kyc_verified') is updated on the DApp's state for external, public-ledger DApps to query for permissioning, if needed. \n\nThis is unique because it combines ZKP with the programmable privacy of Compact Contracts, offering a compliant and private solution essential for bridging regulated industries to decentralized finance. It will benefit any DApp requiring access control based on sensitive attributes." + }, + "impact": { + "impact": "This project will have a significant positive impact on the Midnight ecosystem by providing a **foundational, reusable, and compliant data-protection pattern**. \n\n* **In what way will the success of your project bring value?** It delivers a critical missing primitive: a practical, working example of private identity verification. This de-risks enterprise adoption by showing that regulatory requirements (like KYC) can be met while upholding privacy, attracting a wave of builders focused on compliance-heavy DApps (e.g., regulated exchanges, tokenized assets).\n* **How will you measure this impact?** \n * **Quantitative:** Number of forks/stars on the public repository, community feedback/usage in other PoCs, and formal code review requests for the core logic.\n * **Qualitative:** Testimonials from other builders who adopt the pattern, and its potential inclusion as a canonical example in Midnight's official documentation/tutorials.\n* **How will you share the outputs?** The full, working code will be open-source (MIT license). We will publish a detailed technical blog post and a **live, hosted demo** alongside the final Project Completion Video to make the PoC immediately accessible and understandable to developers." + }, + "feasibility": { + "feasibility": "Our team has **deep expertise in zero-knowledge cryptography** and a proven track record of delivering high-quality, documented open-source projects. The lead developer has contributed to multiple ZK-related libraries and has a strong GitHub portfolio. This project's scope is strictly a **3-month PoC** focusing on demonstrating the core flow, minimizing technical risk.\n\n**Capability:** \n\n* **Lead Developer:** 5 years in cryptography and Rust development, specifically in ZK circuits (link to relevant GitHub repo/project).\n* **Project Manager:** Experience managing 6+ open-source grants, ensuring timely delivery and clear reporting.\n\n**Validation/Trust:** \n\n* Our approach is feasible because we are building on established ZK principles and the provided Compact tooling, not inventing new cryptography. The 3-month timeline forces a minimal, focused scope.\n* **Trust/Accountability:** Funds will be managed via a dedicated multi-sig wallet (or equivalent in the Midnight ecosystem). Milestones are tied to tangible, verifiable outputs (public code repository, passing tests, live demo link). Progress will be communicated bi-weekly via a public status report on the project's GitHub repo." + } + }, + "milestones": { + "milestones": { + "milestone_list": [ + { + "title": "Compact Contract and ZKP Core Logic", + "outputs": "A public GitHub repository containing the complete `KycVerifier.compact` contract (Rust source) with accompanying unit tests. The core logic for proof verification is implemented and tested.", + "acceptance_criteria": "The Compact contract successfully compiles. All unit tests, including those for valid and invalid ZK proofs, pass. Repository is public, under MIT license, and contains a conceptual README.", + "evidence": "Link to the specific commit/tag of the public GitHub repository. Screenshots of all unit tests passing with code coverage report. Contract address on the Midnight testnet (if available).", + "delivery_month": 1, + "cost": 2833, + "progress": "10 %" + }, + { + "title": "Demo UI and Lace Wallet Integration", + "outputs": "A functional web-based Demo UI in TypeScript/React that integrates with the Compact contract. This includes the proof generation (simulated or real library) and transaction submission via the Lace (Midnight) wallet.", + "acceptance_criteria": "The Demo UI successfully connects to the Lace wallet and executes the end-to-end flow: proof generation, transaction submission, and state update. A detailed tutorial for setup and use is included in the repo documentation.", + "evidence": "Link to the public GitHub repository with the UI source code. Link to a deployed, live demo on a hosting service (e.g., Netlify). Detailed video walkthrough showing the full DApp flow.", + "delivery_month": 2, + "cost": 2833, + "progress": "10 %" + }, + { + "title": "Final PoC Demonstration and Project Close-out", + "outputs": "Finalized, clean, and comprehensively documented code (including docstrings and a final technical write-up). The Project Close-out Report and a Project Completion Video demonstrating the live PoC.", + "acceptance_criteria": "All code is finalized and passes linter/formatter. Documentation is complete, covering architecture, setup, and reuse. The Project Completion Video clearly demonstrates the fully functional Private KYC DApp PoC. Final Project Close-out Report submitted.", + "evidence": "Final link to the complete public repository. Link to the Project Completion Video (PCV) hosted on a public platform (e.g., YouTube). Submission of the Project Close-out Report (PCR).", + "delivery_month": 3, + "cost": 2834, + "progress": "10 %" + } + ] + } + }, + "pitch": { + "team": { + "who": "The project team consists of a single developer and a project coordinator.\n\n* **Jane Doe (Lead Developer):** Responsible for Compact Contract development, ZK logic, and core UI development. [https://linkedin.com/in/janedoe](https://linkedin.com/in/janedoe). Jane has contributed to several open-source ZK projects (see supporting links).\n* **John Smith (Project Coordinator - part-time):** Responsible for documentation, reporting, and managing the GitHub repository. [https://linkedin.com/in/johnsmith](https://linkedin.com/in/johnsmith). John ensures adherence to the timeline and clear communication of progress." + }, + "budget": { + "costs": "The total budget requested is **$8,500 USDM**, spread across three milestones over 3 months. This is a cost-effective, code-first PoC focused purely on demonstrating a vital technical primitive for the Midnight ecosystem.\n\n* **Milestone 1 (Compact Contract Core):** $2,833 USDM. (Development effort for core Rust/Compact contract, ZK logic integration, and unit tests).\n* **Milestone 2 (Demo UI & Integration):** $2,833 USDM. (Development effort for the TypeScript UI, Lace wallet integration, and documentation/tutorials).\n* **Milestone 3 (Finalization & Close-out):** $2,834 USDM. (Final code clean-up, comprehensive documentation, creation of the Project Completion Video and Close-out Report).\n\nThis breakdown covers developer time, minimal project management, and essential tooling/hosting for a high-quality reference implementation." + }, + "value": { + "note": "This project offers exceptional value for money by delivering a **canonical, production-ready reference pattern** for private identity verification, which is a key differentiator for the Midnight ecosystem. At $8,500 USDM, this PoC provides a foundational piece of infrastructure that **significantly lowers the barrier to entry for enterprise DApps** requiring regulatory compliance. The value is not just the code, but the **trust and assurance** it provides to potential builders that Midnight can solve the 'private compliance' problem. The open-source delivery ensures the entire community benefits immediately, maximizing the return on investment through reusability, learning, and attracting new builders who need this specific primitive." + } + }, + "self_assessment": { + "self_assessment_checklist": { + "strategic_fit": true, + "developer_experience": true, + "open_source_commitment": true, + "verifiable_builder_credentials": true, + "quality_documentation": true, + "realistic_scope": true + } + }, + "agreements": { + "ongoing_projects": { + "has_ongoing_projects": false, + "projects": [] + }, + "consent_confirmation": { + "terms_and_conditions": true + } + } +} \ No newline at end of file diff --git a/catalyst-gateway/tests/api_tests/test_data/signed_docs/submission_action.json b/catalyst-gateway/tests/api_tests/test_data/signed_docs/submission_action.deprecated.json similarity index 100% rename from catalyst-gateway/tests/api_tests/test_data/signed_docs/submission_action.json rename to catalyst-gateway/tests/api_tests/test_data/signed_docs/submission_action.deprecated.json diff --git a/catalyst-gateway/tests/api_tests/utils/admin.py b/catalyst-gateway/tests/api_tests/utils/admin.py index 6b973cfb6cc..6313f3cb629 100644 --- a/catalyst-gateway/tests/api_tests/utils/admin.py +++ b/catalyst-gateway/tests/api_tests/utils/admin.py @@ -1,6 +1,6 @@ import pytest from pycardano.crypto.bip32 import BIP32ED25519PrivateKey, BIP32ED25519PublicKey -from rbac_chain import ( +from utils.rbac_chain import ( generate_rbac_auth_token, ONLY_ROLE_0_REG_JSON, RoleID, diff --git a/catalyst-gateway/tests/api_tests/utils/signed_doc.py b/catalyst-gateway/tests/api_tests/utils/signed_doc.py index 5475fc85c12..ad32cda2cd8 100644 --- a/catalyst-gateway/tests/api_tests/utils/signed_doc.py +++ b/catalyst-gateway/tests/api_tests/utils/signed_doc.py @@ -8,9 +8,8 @@ from api.v1 import document from utils import signed_doc, uuid_v7 from utils.rbac_chain import rbac_chain_factory, RoleID -from utils.admin import AdminKey, admin_key +from utils.admin import admin_key from tempfile import NamedTemporaryFile -import ranjg DOC_TYPE = { @@ -201,7 +200,11 @@ def __factory__( template=template, parameters=[param] ) - content = ranjg.gen(template.content) + + with open( + "./test_data/signed_docs/proposal.json", "r" + ) as json_file: + content = json.load(json_file) rbac_chain = rbac_chain_factory() doc = SignedDocument(metadata, content) @@ -266,7 +269,7 @@ def category_parameters_doc( template=template, parameters=[param], ) - content = ranjg.gen(template.content) + content = {} doc = SignedDocument(metadata, content) resp = document.put( @@ -322,7 +325,7 @@ def campaign_parameters_doc( parameters=[param], ) - content = ranjg.gen(template.content) + content = {} doc = SignedDocument(metadata, content) resp = document.put( @@ -374,7 +377,7 @@ def brand_parameters_doc( content_type="application/json", template=template, ) - content = ranjg.gen(template.content) + content = {} doc = SignedDocument(metadata, content) resp = document.put(