From 3f005c5c29d1583544b1e55269fe71b07f165d13 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 12 Nov 2025 00:42:05 +0000 Subject: [PATCH 01/13] chore: sync repo --- .devcontainer/Dockerfile | 9 + .devcontainer/devcontainer.json | 43 + .github/workflows/ci.yml | 52 + .gitignore | 19 +- .python-version | 1 + .stats.yml | 2 + Brewfile | 2 + CONTRIBUTING.md | 129 ++ LICENSE | 201 ++ README.md | 507 +++- SECURITY.md | 27 + api.md | 368 +++ bin/publish-pypi | 9 + examples/.keep | 4 + mypy.ini | 50 + noxfile.py | 9 + postgrid/__init__.py | 879 ------- pyproject.toml | 218 +- requirements-dev.lock | 104 + requirements.lock | 45 + requirements.txt | 45 - scripts/bootstrap | 19 + scripts/format | 8 + scripts/lint | 11 + scripts/mock | 41 + scripts/test | 59 + scripts/utils/ruffen-docs.py | 167 ++ src/postgrid/__init__.py | 94 + src/postgrid/_base_client.py | 2058 +++++++++++++++++ src/postgrid/_client.py | 520 +++++ src/postgrid/_compat.py | 219 ++ src/postgrid/_constants.py | 14 + src/postgrid/_exceptions.py | 108 + src/postgrid/_files.py | 123 + src/postgrid/_models.py | 801 +++++++ src/postgrid/_qs.py | 150 ++ src/postgrid/_resource.py | 43 + src/postgrid/_response.py | 830 +++++++ src/postgrid/_streaming.py | 333 +++ src/postgrid/_types.py | 217 ++ src/postgrid/_utils/__init__.py | 57 + src/postgrid/_utils/_logs.py | 25 + src/postgrid/_utils/_proxy.py | 62 + src/postgrid/_utils/_reflection.py | 42 + src/postgrid/_utils/_streams.py | 12 + src/postgrid/_utils/_sync.py | 86 + src/postgrid/_utils/_transform.py | 402 ++++ src/postgrid/_utils/_typing.py | 149 ++ src/postgrid/_utils/_utils.py | 414 ++++ src/postgrid/_version.py | 4 + src/postgrid/lib/.keep | 4 + src/postgrid/pagination.py | 72 + src/postgrid/py.typed | 0 src/postgrid/resources/__init__.py | 47 + .../resources/address_verification.py | 339 +++ .../resources/intl_address_verification.py | 319 +++ src/postgrid/resources/print_mail/__init__.py | 215 ++ .../resources/print_mail/bank_accounts.py | 918 ++++++++ src/postgrid/resources/print_mail/boxes.py | 565 +++++ .../resources/print_mail/campaigns.py | 823 +++++++ src/postgrid/resources/print_mail/cheques.py | 861 +++++++ src/postgrid/resources/print_mail/contacts.py | 842 +++++++ src/postgrid/resources/print_mail/letters.py | 1052 +++++++++ .../print_mail/mailing_list_imports.py | 651 ++++++ .../resources/print_mail/mailing_lists.py | 743 ++++++ .../print_mail/order_profiles/__init__.py | 75 + .../print_mail/order_profiles/cheques.py | 814 +++++++ .../print_mail/order_profiles/letters.py | 823 +++++++ .../order_profiles/order_profiles.py | 198 ++ .../print_mail/order_profiles/postcards.py | 723 ++++++ .../print_mail/order_profiles/self_mailers.py | 729 ++++++ .../resources/print_mail/postcards.py | 1097 +++++++++ .../resources/print_mail/print_mail.py | 518 +++++ .../resources/print_mail/reports/__init__.py | 47 + .../resources/print_mail/reports/exports.py | 389 ++++ .../resources/print_mail/reports/reports.py | 777 +++++++ .../resources/print_mail/reports/samples.py | 204 ++ .../resources/print_mail/self_mailers.py | 1097 +++++++++ .../resources/print_mail/sub_organizations.py | 541 +++++ .../resources/print_mail/templates.py | 592 +++++ src/postgrid/types/__init__.py | 18 + .../address_verification_verify_params.py | 62 + .../address_verification_verify_response.py | 249 ++ .../contact_create_with_company_name_param.py | 68 + .../contact_create_with_first_name_param.py | 68 + src/postgrid/types/errors.py | 26 + ...intl_address_verification_verify_params.py | 65 + ...tl_address_verification_verify_response.py | 219 ++ src/postgrid/types/print_mail/__init__.py | 90 + .../types/print_mail/address_placement.py | 7 + src/postgrid/types/print_mail/attached_pdf.py | 15 + .../types/print_mail/attached_pdf_param.py | 15 + src/postgrid/types/print_mail/bank_account.py | 82 + .../print_mail/bank_account_country_code.py | 7 + .../print_mail/bank_account_create_params.py | 162 ++ .../bank_account_delete_response.py | 17 + .../print_mail/bank_account_list_params.py | 22 + src/postgrid/types/print_mail/box.py | 134 ++ .../types/print_mail/box_cheque_base.py | 35 + .../types/print_mail/box_cheque_base_param.py | 36 + .../types/print_mail/box_create_params.py | 81 + .../types/print_mail/box_list_params.py | 22 + src/postgrid/types/print_mail/campaign.py | 92 + .../print_mail/campaign_create_params.py | 48 + .../print_mail/campaign_delete_response.py | 14 + .../types/print_mail/campaign_list_params.py | 22 + .../types/print_mail/campaign_send_params.py | 20 + .../print_mail/campaign_update_params.py | 54 + src/postgrid/types/print_mail/cancellation.py | 21 + src/postgrid/types/print_mail/cheque.py | 180 ++ .../types/print_mail/cheque_create_params.py | 130 ++ .../types/print_mail/cheque_list_params.py | 22 + .../cheque_retrieve_url_response.py | 19 + src/postgrid/types/print_mail/cheque_size.py | 7 + src/postgrid/types/print_mail/contact.py | 95 + .../types/print_mail/contact_create_param.py | 13 + .../types/print_mail/contact_create_params.py | 129 ++ .../print_mail/contact_delete_response.py | 17 + .../types/print_mail/contact_list_params.py | 22 + .../types/print_mail/deleted_response.py | 15 + src/postgrid/types/print_mail/digital_only.py | 11 + .../types/print_mail/digital_only_param.py | 12 + .../types/print_mail/email_preferences.py | 17 + src/postgrid/types/print_mail/file_type.py | 7 + src/postgrid/types/print_mail/letter.py | 177 ++ .../types/print_mail/letter_create_params.py | 216 ++ .../types/print_mail/letter_list_params.py | 22 + .../letter_retrieve_url_response.py | 19 + src/postgrid/types/print_mail/letter_size.py | 7 + src/postgrid/types/print_mail/mailing_list.py | 50 + .../print_mail/mailing_list_create_params.py | 23 + .../mailing_list_delete_response.py | 14 + .../mailing_list_import_create_params.py | 45 + .../mailing_list_import_delete_response.py | 14 + .../mailing_list_import_list_params.py | 22 + .../mailing_list_import_response.py | 131 ++ .../mailing_list_import_update_params.py | 22 + .../print_mail/mailing_list_jobs_params.py | 36 + .../print_mail/mailing_list_list_params.py | 22 + .../types/print_mail/mailing_list_update.py | 18 + .../print_mail/mailing_list_update_params.py | 19 + .../types/print_mail/order_imb_status.py | 7 + .../types/print_mail/order_mailing_class.py | 34 + .../print_mail/order_profiles/__init__.py | 32 + .../order_profiles/cheque_create_params.py | 68 + .../order_profiles/cheque_delete_response.py | 17 + .../order_profiles/cheque_list_params.py | 22 + .../order_profiles/cheque_list_response.py | 74 + .../order_profiles/cheque_profile.py | 78 + .../order_profiles/cheque_retrieve_params.py | 13 + .../order_profiles/cheque_update_params.py | 68 + .../order_profiles/currency_code.py | 7 + .../order_profiles/letter_create_params.py | 61 + .../order_profiles/letter_delete_response.py | 17 + .../order_profiles/letter_list_params.py | 22 + .../order_profiles/letter_profile.py | 72 + .../order_profiles/letter_retrieve_params.py | 13 + .../order_profiles/letter_update_params.py | 57 + .../order_profiles/postcard_create_params.py | 47 + .../postcard_delete_response.py | 17 + .../order_profiles/postcard_list_params.py | 22 + .../order_profiles/postcard_profile.py | 58 + .../postcard_retrieve_params.py | 13 + .../order_profiles/postcard_size.py | 7 + .../order_profiles/postcard_update_params.py | 43 + .../self_mailer_create_params.py | 44 + .../self_mailer_delete_response.py | 17 + .../order_profiles/self_mailer_list_params.py | 22 + .../order_profiles/self_mailer_profile.py | 55 + .../self_mailer_retrieve_params.py | 13 + .../order_profiles/self_mailer_size.py | 7 + .../self_mailer_update_params.py | 44 + src/postgrid/types/print_mail/order_status.py | 7 + src/postgrid/types/print_mail/plastic_card.py | 58 + .../types/print_mail/plastic_card_param.py | 57 + src/postgrid/types/print_mail/postcard.py | 126 + .../print_mail/postcard_create_params.py | 239 ++ .../types/print_mail/postcard_list_params.py | 22 + .../postcard_retrieve_url_response.py | 19 + src/postgrid/types/print_mail/report.py | 37 + .../types/print_mail/report_create_params.py | 21 + .../types/print_mail/report_list_params.py | 22 + .../types/print_mail/report_sample_params.py | 24 + .../types/print_mail/report_update_params.py | 21 + .../types/print_mail/reports/__init__.py | 8 + .../reports/export_create_params.py | 19 + .../types/print_mail/reports/report_export.py | 66 + .../types/print_mail/reports/report_sample.py | 20 + .../reports/sample_create_params.py | 19 + src/postgrid/types/print_mail/self_mailer.py | 126 + .../print_mail/self_mailer_create_params.py | 240 ++ .../print_mail/self_mailer_list_params.py | 22 + .../self_mailer_retrieve_url_response.py | 19 + .../types/print_mail/sub_organization.py | 42 + .../sub_organization_list_params.py | 22 + .../sub_organization_retrieve_users_params.py | 22 + ...ub_organization_retrieve_users_response.py | 54 + .../sub_organization_update_params.py | 29 + .../sub_organization_update_response.py | 73 + src/postgrid/types/print_mail/template.py | 41 + .../print_mail/template_create_params.py | 22 + .../print_mail/template_delete_response.py | 17 + .../types/print_mail/template_list_params.py | 22 + .../print_mail/template_update_params.py | 22 + .../print_mail/verification_status_count.py | 19 + src/postgrid/types/status.py | 7 + tests/__init__.py | 1 + tests/api_resources/__init__.py | 1 + tests/api_resources/print_mail/__init__.py | 1 + .../print_mail/order_profiles/__init__.py | 1 + .../print_mail/order_profiles/test_cheques.py | 545 +++++ .../print_mail/order_profiles/test_letters.py | 540 +++++ .../order_profiles/test_postcards.py | 504 ++++ .../order_profiles/test_self_mailers.py | 514 ++++ .../print_mail/reports/__init__.py | 1 + .../print_mail/reports/test_exports.py | 337 +++ .../print_mail/reports/test_samples.py | 126 + .../print_mail/test_bank_accounts.py | 642 +++++ tests/api_resources/print_mail/test_boxes.py | 450 ++++ .../print_mail/test_campaigns.py | 591 +++++ .../api_resources/print_mail/test_cheques.py | 609 +++++ .../api_resources/print_mail/test_contacts.py | 519 +++++ .../api_resources/print_mail/test_letters.py | 979 ++++++++ .../print_mail/test_mailing_list_imports.py | 566 +++++ .../print_mail/test_mailing_lists.py | 559 +++++ .../print_mail/test_postcards.py | 1049 +++++++++ .../api_resources/print_mail/test_reports.py | 553 +++++ .../print_mail/test_self_mailers.py | 1139 +++++++++ .../print_mail/test_sub_organizations.py | 411 ++++ .../print_mail/test_templates.py | 452 ++++ .../test_address_verification.py | 254 ++ .../test_intl_address_verification.py | 250 ++ tests/assets/signature.png | Bin 14041 -> 0 bytes tests/conftest.py | 62 + tests/sample_file.txt | 1 + tests/test_av.py | 132 -- tests/test_cheque.py | 75 - tests/test_client.py | 1896 +++++++++++++++ tests/test_contact.py | 54 - tests/test_deepcopy.py | 58 + tests/test_extract_files.py | 64 + tests/test_files.py | 51 + tests/test_letter.py | 89 - tests/test_models.py | 856 +++++++ tests/test_postcard.py | 62 - tests/test_qs.py | 78 + tests/test_required_args.py | 111 + tests/test_response.py | 277 +++ tests/test_return_envelopes.py | 74 - tests/test_streaming.py | 248 ++ tests/test_template.py | 65 - tests/test_transform.py | 434 ++++ tests/test_util.py | 27 - tests/test_utils/test_proxy.py | 23 + tests/test_utils/test_typing.py | 73 + tests/test_webhook.py | 30 - tests/utils.py | 159 ++ 257 files changed, 46763 insertions(+), 1675 deletions(-) create mode 100644 .devcontainer/Dockerfile create mode 100644 .devcontainer/devcontainer.json create mode 100644 .github/workflows/ci.yml create mode 100644 .python-version create mode 100644 .stats.yml create mode 100644 Brewfile create mode 100644 CONTRIBUTING.md create mode 100644 LICENSE create mode 100644 SECURITY.md create mode 100644 api.md create mode 100644 bin/publish-pypi create mode 100644 examples/.keep create mode 100644 mypy.ini create mode 100644 noxfile.py delete mode 100644 postgrid/__init__.py create mode 100644 requirements-dev.lock create mode 100644 requirements.lock delete mode 100644 requirements.txt create mode 100755 scripts/bootstrap create mode 100755 scripts/format create mode 100755 scripts/lint create mode 100755 scripts/mock create mode 100755 scripts/test create mode 100644 scripts/utils/ruffen-docs.py create mode 100644 src/postgrid/__init__.py create mode 100644 src/postgrid/_base_client.py create mode 100644 src/postgrid/_client.py create mode 100644 src/postgrid/_compat.py create mode 100644 src/postgrid/_constants.py create mode 100644 src/postgrid/_exceptions.py create mode 100644 src/postgrid/_files.py create mode 100644 src/postgrid/_models.py create mode 100644 src/postgrid/_qs.py create mode 100644 src/postgrid/_resource.py create mode 100644 src/postgrid/_response.py create mode 100644 src/postgrid/_streaming.py create mode 100644 src/postgrid/_types.py create mode 100644 src/postgrid/_utils/__init__.py create mode 100644 src/postgrid/_utils/_logs.py create mode 100644 src/postgrid/_utils/_proxy.py create mode 100644 src/postgrid/_utils/_reflection.py create mode 100644 src/postgrid/_utils/_streams.py create mode 100644 src/postgrid/_utils/_sync.py create mode 100644 src/postgrid/_utils/_transform.py create mode 100644 src/postgrid/_utils/_typing.py create mode 100644 src/postgrid/_utils/_utils.py create mode 100644 src/postgrid/_version.py create mode 100644 src/postgrid/lib/.keep create mode 100644 src/postgrid/pagination.py create mode 100644 src/postgrid/py.typed create mode 100644 src/postgrid/resources/__init__.py create mode 100644 src/postgrid/resources/address_verification.py create mode 100644 src/postgrid/resources/intl_address_verification.py create mode 100644 src/postgrid/resources/print_mail/__init__.py create mode 100644 src/postgrid/resources/print_mail/bank_accounts.py create mode 100644 src/postgrid/resources/print_mail/boxes.py create mode 100644 src/postgrid/resources/print_mail/campaigns.py create mode 100644 src/postgrid/resources/print_mail/cheques.py create mode 100644 src/postgrid/resources/print_mail/contacts.py create mode 100644 src/postgrid/resources/print_mail/letters.py create mode 100644 src/postgrid/resources/print_mail/mailing_list_imports.py create mode 100644 src/postgrid/resources/print_mail/mailing_lists.py create mode 100644 src/postgrid/resources/print_mail/order_profiles/__init__.py create mode 100644 src/postgrid/resources/print_mail/order_profiles/cheques.py create mode 100644 src/postgrid/resources/print_mail/order_profiles/letters.py create mode 100644 src/postgrid/resources/print_mail/order_profiles/order_profiles.py create mode 100644 src/postgrid/resources/print_mail/order_profiles/postcards.py create mode 100644 src/postgrid/resources/print_mail/order_profiles/self_mailers.py create mode 100644 src/postgrid/resources/print_mail/postcards.py create mode 100644 src/postgrid/resources/print_mail/print_mail.py create mode 100644 src/postgrid/resources/print_mail/reports/__init__.py create mode 100644 src/postgrid/resources/print_mail/reports/exports.py create mode 100644 src/postgrid/resources/print_mail/reports/reports.py create mode 100644 src/postgrid/resources/print_mail/reports/samples.py create mode 100644 src/postgrid/resources/print_mail/self_mailers.py create mode 100644 src/postgrid/resources/print_mail/sub_organizations.py create mode 100644 src/postgrid/resources/print_mail/templates.py create mode 100644 src/postgrid/types/__init__.py create mode 100644 src/postgrid/types/address_verification_verify_params.py create mode 100644 src/postgrid/types/address_verification_verify_response.py create mode 100644 src/postgrid/types/contact_create_with_company_name_param.py create mode 100644 src/postgrid/types/contact_create_with_first_name_param.py create mode 100644 src/postgrid/types/errors.py create mode 100644 src/postgrid/types/intl_address_verification_verify_params.py create mode 100644 src/postgrid/types/intl_address_verification_verify_response.py create mode 100644 src/postgrid/types/print_mail/__init__.py create mode 100644 src/postgrid/types/print_mail/address_placement.py create mode 100644 src/postgrid/types/print_mail/attached_pdf.py create mode 100644 src/postgrid/types/print_mail/attached_pdf_param.py create mode 100644 src/postgrid/types/print_mail/bank_account.py create mode 100644 src/postgrid/types/print_mail/bank_account_country_code.py create mode 100644 src/postgrid/types/print_mail/bank_account_create_params.py create mode 100644 src/postgrid/types/print_mail/bank_account_delete_response.py create mode 100644 src/postgrid/types/print_mail/bank_account_list_params.py create mode 100644 src/postgrid/types/print_mail/box.py create mode 100644 src/postgrid/types/print_mail/box_cheque_base.py create mode 100644 src/postgrid/types/print_mail/box_cheque_base_param.py create mode 100644 src/postgrid/types/print_mail/box_create_params.py create mode 100644 src/postgrid/types/print_mail/box_list_params.py create mode 100644 src/postgrid/types/print_mail/campaign.py create mode 100644 src/postgrid/types/print_mail/campaign_create_params.py create mode 100644 src/postgrid/types/print_mail/campaign_delete_response.py create mode 100644 src/postgrid/types/print_mail/campaign_list_params.py create mode 100644 src/postgrid/types/print_mail/campaign_send_params.py create mode 100644 src/postgrid/types/print_mail/campaign_update_params.py create mode 100644 src/postgrid/types/print_mail/cancellation.py create mode 100644 src/postgrid/types/print_mail/cheque.py create mode 100644 src/postgrid/types/print_mail/cheque_create_params.py create mode 100644 src/postgrid/types/print_mail/cheque_list_params.py create mode 100644 src/postgrid/types/print_mail/cheque_retrieve_url_response.py create mode 100644 src/postgrid/types/print_mail/cheque_size.py create mode 100644 src/postgrid/types/print_mail/contact.py create mode 100644 src/postgrid/types/print_mail/contact_create_param.py create mode 100644 src/postgrid/types/print_mail/contact_create_params.py create mode 100644 src/postgrid/types/print_mail/contact_delete_response.py create mode 100644 src/postgrid/types/print_mail/contact_list_params.py create mode 100644 src/postgrid/types/print_mail/deleted_response.py create mode 100644 src/postgrid/types/print_mail/digital_only.py create mode 100644 src/postgrid/types/print_mail/digital_only_param.py create mode 100644 src/postgrid/types/print_mail/email_preferences.py create mode 100644 src/postgrid/types/print_mail/file_type.py create mode 100644 src/postgrid/types/print_mail/letter.py create mode 100644 src/postgrid/types/print_mail/letter_create_params.py create mode 100644 src/postgrid/types/print_mail/letter_list_params.py create mode 100644 src/postgrid/types/print_mail/letter_retrieve_url_response.py create mode 100644 src/postgrid/types/print_mail/letter_size.py create mode 100644 src/postgrid/types/print_mail/mailing_list.py create mode 100644 src/postgrid/types/print_mail/mailing_list_create_params.py create mode 100644 src/postgrid/types/print_mail/mailing_list_delete_response.py create mode 100644 src/postgrid/types/print_mail/mailing_list_import_create_params.py create mode 100644 src/postgrid/types/print_mail/mailing_list_import_delete_response.py create mode 100644 src/postgrid/types/print_mail/mailing_list_import_list_params.py create mode 100644 src/postgrid/types/print_mail/mailing_list_import_response.py create mode 100644 src/postgrid/types/print_mail/mailing_list_import_update_params.py create mode 100644 src/postgrid/types/print_mail/mailing_list_jobs_params.py create mode 100644 src/postgrid/types/print_mail/mailing_list_list_params.py create mode 100644 src/postgrid/types/print_mail/mailing_list_update.py create mode 100644 src/postgrid/types/print_mail/mailing_list_update_params.py create mode 100644 src/postgrid/types/print_mail/order_imb_status.py create mode 100644 src/postgrid/types/print_mail/order_mailing_class.py create mode 100644 src/postgrid/types/print_mail/order_profiles/__init__.py create mode 100644 src/postgrid/types/print_mail/order_profiles/cheque_create_params.py create mode 100644 src/postgrid/types/print_mail/order_profiles/cheque_delete_response.py create mode 100644 src/postgrid/types/print_mail/order_profiles/cheque_list_params.py create mode 100644 src/postgrid/types/print_mail/order_profiles/cheque_list_response.py create mode 100644 src/postgrid/types/print_mail/order_profiles/cheque_profile.py create mode 100644 src/postgrid/types/print_mail/order_profiles/cheque_retrieve_params.py create mode 100644 src/postgrid/types/print_mail/order_profiles/cheque_update_params.py create mode 100644 src/postgrid/types/print_mail/order_profiles/currency_code.py create mode 100644 src/postgrid/types/print_mail/order_profiles/letter_create_params.py create mode 100644 src/postgrid/types/print_mail/order_profiles/letter_delete_response.py create mode 100644 src/postgrid/types/print_mail/order_profiles/letter_list_params.py create mode 100644 src/postgrid/types/print_mail/order_profiles/letter_profile.py create mode 100644 src/postgrid/types/print_mail/order_profiles/letter_retrieve_params.py create mode 100644 src/postgrid/types/print_mail/order_profiles/letter_update_params.py create mode 100644 src/postgrid/types/print_mail/order_profiles/postcard_create_params.py create mode 100644 src/postgrid/types/print_mail/order_profiles/postcard_delete_response.py create mode 100644 src/postgrid/types/print_mail/order_profiles/postcard_list_params.py create mode 100644 src/postgrid/types/print_mail/order_profiles/postcard_profile.py create mode 100644 src/postgrid/types/print_mail/order_profiles/postcard_retrieve_params.py create mode 100644 src/postgrid/types/print_mail/order_profiles/postcard_size.py create mode 100644 src/postgrid/types/print_mail/order_profiles/postcard_update_params.py create mode 100644 src/postgrid/types/print_mail/order_profiles/self_mailer_create_params.py create mode 100644 src/postgrid/types/print_mail/order_profiles/self_mailer_delete_response.py create mode 100644 src/postgrid/types/print_mail/order_profiles/self_mailer_list_params.py create mode 100644 src/postgrid/types/print_mail/order_profiles/self_mailer_profile.py create mode 100644 src/postgrid/types/print_mail/order_profiles/self_mailer_retrieve_params.py create mode 100644 src/postgrid/types/print_mail/order_profiles/self_mailer_size.py create mode 100644 src/postgrid/types/print_mail/order_profiles/self_mailer_update_params.py create mode 100644 src/postgrid/types/print_mail/order_status.py create mode 100644 src/postgrid/types/print_mail/plastic_card.py create mode 100644 src/postgrid/types/print_mail/plastic_card_param.py create mode 100644 src/postgrid/types/print_mail/postcard.py create mode 100644 src/postgrid/types/print_mail/postcard_create_params.py create mode 100644 src/postgrid/types/print_mail/postcard_list_params.py create mode 100644 src/postgrid/types/print_mail/postcard_retrieve_url_response.py create mode 100644 src/postgrid/types/print_mail/report.py create mode 100644 src/postgrid/types/print_mail/report_create_params.py create mode 100644 src/postgrid/types/print_mail/report_list_params.py create mode 100644 src/postgrid/types/print_mail/report_sample_params.py create mode 100644 src/postgrid/types/print_mail/report_update_params.py create mode 100644 src/postgrid/types/print_mail/reports/__init__.py create mode 100644 src/postgrid/types/print_mail/reports/export_create_params.py create mode 100644 src/postgrid/types/print_mail/reports/report_export.py create mode 100644 src/postgrid/types/print_mail/reports/report_sample.py create mode 100644 src/postgrid/types/print_mail/reports/sample_create_params.py create mode 100644 src/postgrid/types/print_mail/self_mailer.py create mode 100644 src/postgrid/types/print_mail/self_mailer_create_params.py create mode 100644 src/postgrid/types/print_mail/self_mailer_list_params.py create mode 100644 src/postgrid/types/print_mail/self_mailer_retrieve_url_response.py create mode 100644 src/postgrid/types/print_mail/sub_organization.py create mode 100644 src/postgrid/types/print_mail/sub_organization_list_params.py create mode 100644 src/postgrid/types/print_mail/sub_organization_retrieve_users_params.py create mode 100644 src/postgrid/types/print_mail/sub_organization_retrieve_users_response.py create mode 100644 src/postgrid/types/print_mail/sub_organization_update_params.py create mode 100644 src/postgrid/types/print_mail/sub_organization_update_response.py create mode 100644 src/postgrid/types/print_mail/template.py create mode 100644 src/postgrid/types/print_mail/template_create_params.py create mode 100644 src/postgrid/types/print_mail/template_delete_response.py create mode 100644 src/postgrid/types/print_mail/template_list_params.py create mode 100644 src/postgrid/types/print_mail/template_update_params.py create mode 100644 src/postgrid/types/print_mail/verification_status_count.py create mode 100644 src/postgrid/types/status.py create mode 100644 tests/api_resources/__init__.py create mode 100644 tests/api_resources/print_mail/__init__.py create mode 100644 tests/api_resources/print_mail/order_profiles/__init__.py create mode 100644 tests/api_resources/print_mail/order_profiles/test_cheques.py create mode 100644 tests/api_resources/print_mail/order_profiles/test_letters.py create mode 100644 tests/api_resources/print_mail/order_profiles/test_postcards.py create mode 100644 tests/api_resources/print_mail/order_profiles/test_self_mailers.py create mode 100644 tests/api_resources/print_mail/reports/__init__.py create mode 100644 tests/api_resources/print_mail/reports/test_exports.py create mode 100644 tests/api_resources/print_mail/reports/test_samples.py create mode 100644 tests/api_resources/print_mail/test_bank_accounts.py create mode 100644 tests/api_resources/print_mail/test_boxes.py create mode 100644 tests/api_resources/print_mail/test_campaigns.py create mode 100644 tests/api_resources/print_mail/test_cheques.py create mode 100644 tests/api_resources/print_mail/test_contacts.py create mode 100644 tests/api_resources/print_mail/test_letters.py create mode 100644 tests/api_resources/print_mail/test_mailing_list_imports.py create mode 100644 tests/api_resources/print_mail/test_mailing_lists.py create mode 100644 tests/api_resources/print_mail/test_postcards.py create mode 100644 tests/api_resources/print_mail/test_reports.py create mode 100644 tests/api_resources/print_mail/test_self_mailers.py create mode 100644 tests/api_resources/print_mail/test_sub_organizations.py create mode 100644 tests/api_resources/print_mail/test_templates.py create mode 100644 tests/api_resources/test_address_verification.py create mode 100644 tests/api_resources/test_intl_address_verification.py delete mode 100644 tests/assets/signature.png create mode 100644 tests/conftest.py create mode 100644 tests/sample_file.txt delete mode 100644 tests/test_av.py delete mode 100644 tests/test_cheque.py create mode 100644 tests/test_client.py delete mode 100644 tests/test_contact.py create mode 100644 tests/test_deepcopy.py create mode 100644 tests/test_extract_files.py create mode 100644 tests/test_files.py delete mode 100644 tests/test_letter.py create mode 100644 tests/test_models.py delete mode 100644 tests/test_postcard.py create mode 100644 tests/test_qs.py create mode 100644 tests/test_required_args.py create mode 100644 tests/test_response.py delete mode 100644 tests/test_return_envelopes.py create mode 100644 tests/test_streaming.py delete mode 100644 tests/test_template.py create mode 100644 tests/test_transform.py delete mode 100644 tests/test_util.py create mode 100644 tests/test_utils/test_proxy.py create mode 100644 tests/test_utils/test_typing.py delete mode 100644 tests/test_webhook.py create mode 100644 tests/utils.py diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..55d2025 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,9 @@ +ARG VARIANT="3.9" +FROM mcr.microsoft.com/vscode/devcontainers/python:0-${VARIANT} + +USER vscode + +RUN curl -sSf https://rye.astral.sh/get | RYE_VERSION="0.35.0" RYE_INSTALL_OPTION="--yes" bash +ENV PATH=/home/vscode/.rye/shims:$PATH + +RUN echo "[[ -d .venv ]] && source .venv/bin/activate || export PATH=\$PATH" >> /home/vscode/.bashrc diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..c17fdc1 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,43 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/debian +{ + "name": "Debian", + "build": { + "dockerfile": "Dockerfile", + "context": ".." + }, + + "postStartCommand": "rye sync --all-features", + + "customizations": { + "vscode": { + "extensions": [ + "ms-python.python" + ], + "settings": { + "terminal.integrated.shell.linux": "/bin/bash", + "python.pythonPath": ".venv/bin/python", + "python.defaultInterpreterPath": ".venv/bin/python", + "python.typeChecking": "basic", + "terminal.integrated.env.linux": { + "PATH": "/home/vscode/.rye/shims:${env:PATH}" + } + } + } + }, + "features": { + "ghcr.io/devcontainers/features/node:1": {} + } + + // Features to add to the dev container. More info: https://containers.dev/features. + // "features": {}, + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [], + + // Configure tool-specific properties. + // "customizations": {}, + + // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. + // "remoteUser": "root" +} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..c8a8a4f --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,52 @@ +name: CI +on: + push: + branches: + - main + pull_request: + branches: + - main + - next + +jobs: + lint: + name: lint + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Install Rye + run: | + curl -sSf https://rye.astral.sh/get | bash + echo "$HOME/.rye/shims" >> $GITHUB_PATH + env: + RYE_VERSION: '0.35.0' + RYE_INSTALL_OPTION: '--yes' + + - name: Install dependencies + run: rye sync --all-features + + - name: Run lints + run: ./scripts/lint + + test: + name: test + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Install Rye + run: | + curl -sSf https://rye.astral.sh/get | bash + echo "$HOME/.rye/shims" >> $GITHUB_PATH + env: + RYE_VERSION: '0.35.0' + RYE_INSTALL_OPTION: '--yes' + + - name: Bootstrap + run: ./scripts/bootstrap + + - name: Run tests + run: ./scripts/test diff --git a/.gitignore b/.gitignore index 2e201f0..8779740 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,16 @@ -venv -*.pyc -*.egg-info +.prism.log +.vscode +_dev + +__pycache__ +.mypy_cache + dist -build + +.venv +.idea + +.env +.envrc +codegen.log +Brewfile.lock.json diff --git a/.python-version b/.python-version new file mode 100644 index 0000000..43077b2 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.9.18 diff --git a/.stats.yml b/.stats.yml new file mode 100644 index 0000000..044ee0a --- /dev/null +++ b/.stats.yml @@ -0,0 +1,2 @@ +configured_endpoints: 91 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/postgrid%2Fpostgrid-be6a47768faf3612d1dc9c8a108edb10a6c5a4e52b78cc7f4768e1d497e11e08.yml diff --git a/Brewfile b/Brewfile new file mode 100644 index 0000000..492ca37 --- /dev/null +++ b/Brewfile @@ -0,0 +1,2 @@ +brew "rye" + diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..29357c9 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,129 @@ +## Setting up the environment + +### With Rye + +We use [Rye](https://rye.astral.sh/) to manage dependencies because it will automatically provision a Python environment with the expected Python version. To set it up, run: + +```sh +$ ./scripts/bootstrap +``` + +Or [install Rye manually](https://rye.astral.sh/guide/installation/) and run: + +```sh +$ rye sync --all-features +``` + +You can then run scripts using `rye run python script.py` or by activating the virtual environment: + +```sh +$ rye shell +# or manually activate - https://docs.python.org/3/library/venv.html#how-venvs-work +$ source .venv/bin/activate + +# now you can omit the `rye run` prefix +$ python script.py +``` + +### Without Rye + +Alternatively if you don't want to install `Rye`, you can stick with the standard `pip` setup by ensuring you have the Python version specified in `.python-version`, create a virtual environment however you desire and then install dependencies using this command: + +```sh +$ pip install -r requirements-dev.lock +``` + +## Modifying/Adding code + +Most of the SDK is generated code. Modifications to code will be persisted between generations, but may +result in merge conflicts between manual patches and changes from the generator. The generator will never +modify the contents of the `src/postgrid/lib/` and `examples/` directories. + +## Adding and running examples + +All files in the `examples/` directory are not modified by the generator and can be freely edited or added to. + +```py +# add an example to examples/.py + +#!/usr/bin/env -S rye run python +… +``` + +```sh +$ chmod +x examples/.py +# run the example against your api +$ ./examples/.py +``` + +## Using the repository from source + +If you’d like to use the repository from source, you can either install from git or link to a cloned repository: + +To install via git: + +```sh +$ pip install git+ssh://git@github.com/stainless-sdks/postgrid-python.git +``` + +Alternatively, you can build from source and install the wheel file: + +Building this package will create two files in the `dist/` directory, a `.tar.gz` containing the source files and a `.whl` that can be used to install the package efficiently. + +To create a distributable version of the library, all you have to do is run this command: + +```sh +$ rye build +# or +$ python -m build +``` + +Then to install: + +```sh +$ pip install ./path-to-wheel-file.whl +``` + +## Running tests + +Most tests require you to [set up a mock server](https://github.com/stoplightio/prism) against the OpenAPI spec to run the tests. + +```sh +# you will need npm installed +$ npx prism mock path/to/your/openapi.yml +``` + +```sh +$ ./scripts/test +``` + +## Linting and formatting + +This repository uses [ruff](https://github.com/astral-sh/ruff) and +[black](https://github.com/psf/black) to format the code in the repository. + +To lint: + +```sh +$ ./scripts/lint +``` + +To format and fix all ruff issues automatically: + +```sh +$ ./scripts/format +``` + +## Publishing and releases + +Changes made to this repository via the automated release PR pipeline should publish to PyPI automatically. If +the changes aren't made through the automated pipeline, you may want to make releases manually. + +### Publish with a GitHub workflow + +You can release to package managers by using [the `Publish PyPI` GitHub action](https://www.github.com/stainless-sdks/postgrid-python/actions/workflows/publish-pypi.yml). This requires a setup organization or repository secret to be set up. + +### Publish manually + +If you need to manually release a package, you can run the `bin/publish-pypi` script with a `PYPI_TOKEN` set on +the environment. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..cbee448 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2025 Post Grid + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md index fd24da9..cdc910e 100644 --- a/README.md +++ b/README.md @@ -1,170 +1,429 @@ -# PostGrid Python Library +# Post Grid Python API library -The PostGrid Python library enables you to access the PostGrid Print & Mail API -conveniently from applications written in Python. It contains classes representing -every resource in the PostGrid Print & Mail API and methods which create and operate -on these resources. +[![PyPI version](https://img.shields.io/pypi/v/postgrid.svg)](https://pypi.org/project/postgrid/) -## Installation +The Post Grid Python library provides convenient access to the Post Grid REST API from any Python 3.8+ +application. The library includes type definitions for all request params and response fields, +and offers both synchronous and asynchronous clients powered by [httpx](https://github.com/encode/httpx). -In order to install this package, run +## Documentation -```bash -pip install --upgrade postgrid-python -``` +The REST API documentation can be found on [docs.postgrid.com](https://docs.postgrid.com). The full API of this library can be found in [api.md](api.md). -### Requirements +## Installation + +```sh +# install from this staging repo +pip install git+ssh://git@github.com/stainless-sdks/postgrid-python.git +``` -- Python 3.6+ +> [!NOTE] +> Once this package is [published to PyPI](https://app.stainlessapi.com/docs/guides/publish), this will become: `pip install --pre postgrid` ## Usage -To access the Print & Mail services, you must have a PostGrid Print & Mail Account (you can sign up at https://dashboard.postgrid.com/signup) and access to your API key which you can retrieve from the [settings page](https://dashboard.postgrid.com/settings). +The full API of this library can be found in [api.md](api.md). ```python -import postgrid +import os +from postgrid import PostGrid + +client = PostGrid( + address_verification_api_key=os.environ.get( + "POSTGRID_ADDRESS_VERIFICATION_API_KEY" + ), # This is the default and can be omitted + print_mail_api_key=os.environ.get( + "POSTGRID_PRINT_MAIL_API_KEY" + ), # This is the default and can be omitted +) -# Swap this out for your live API key to create live orders -postgrid.pm_key = 'test_sk_...' - -# Send a letter -letter = postgrid.Letter.create( - to={ - 'first_name': 'Apaar', - 'last_name': 'Madan', - 'address_line1': '145 Mulberry St, Apt PH D, New York NY 10013', - 'country_code': 'US' - }, - from_={ - 'first_name': 'Kevin', - 'last_name': 'Villena', - 'address_line1': '90 Canal St', - 'address_line2': '2nd Floor', - 'city': 'Boston', - 'province_or_state': 'MA', - 'postal_or_zip': '02114', - 'country_code': 'US' - }, - html=''' - - -

Hi, {{to.firstName}}

-

Welcome to PostGrid.

-

Yours Truly,

-

{{from.firstName}} {{from.lastName}}

- - - ''' +contact = client.print_mail.contacts.create( + address_line1="addressLine1", + country_code="countryCode", + first_name="firstName", ) +print(contact.id) +``` + +While you can provide a `address_verification_api_key` keyword argument, +we recommend using [python-dotenv](https://pypi.org/project/python-dotenv/) +to add `POSTGRID_ADDRESS_VERIFICATION_API_KEY="My Address Verification API Key"` to your `.env` file +so that your Address Verification API Key is not stored in source control. -# Prints 'ready' -print(letter.status) +## Async usage -# Cancel the letter -letter = postgrid.Letter.delete(letter.id) +Simply import `AsyncPostGrid` instead of `PostGrid` and use `await` with each API call: -# Prints 'cancelled' -print(letter.status) +```python +import os +import asyncio +from postgrid import AsyncPostGrid + +client = AsyncPostGrid( + address_verification_api_key=os.environ.get( + "POSTGRID_ADDRESS_VERIFICATION_API_KEY" + ), # This is the default and can be omitted + print_mail_api_key=os.environ.get( + "POSTGRID_PRINT_MAIL_API_KEY" + ), # This is the default and can be omitted +) + + +async def main() -> None: + contact = await client.print_mail.contacts.create( + address_line1="addressLine1", + country_code="countryCode", + first_name="firstName", + ) + print(contact.id) + + +asyncio.run(main()) ``` -Similarly, to access the Address Verification services, you must have a PostGrid Address Verification Account (you can sign up at https://app.postgrid.com/signup) and access to your API key which you can retrieve from the [developers page](https://app.postgrid.com/dashboard/developers). +Functionality between the synchronous and asynchronous clients is otherwise identical. + +## Using types + +Nested request parameters are [TypedDicts](https://docs.python.org/3/library/typing.html#typing.TypedDict). Responses are [Pydantic models](https://docs.pydantic.dev) which also provide helper methods for things like: + +- Serializing back into JSON, `model.to_json()` +- Converting to a dictionary, `model.to_dict()` + +Typed requests and responses provide autocomplete and documentation within your editor. If you would like to see type errors in VS Code to help catch bugs earlier, set `python.analysis.typeCheckingMode` to `basic`. + +## Pagination + +List methods in the Post Grid API are paginated. + +This library provides auto-paginating iterators with each list response, so you do not have to request successive pages manually: ```python -import postgrid +from postgrid import PostGrid + +client = PostGrid() + +all_letters = [] +# Automatically fetches more pages as needed. +for letter in client.print_mail.letters.list( + limit=100, +): + # Do something with letter here + all_letters.append(letter) +print(all_letters) +``` -# Swap this out for your live API key to create live requests -postgrid.av_key = 'test_sk_...' +Or, asynchronously: -# Verify a freeform address -verification = postgrid.Verification.verify('22-20 bay st, floor 11, toronto, on') +```python +import asyncio +from postgrid import AsyncPostGrid -# Prints 'success' -print(verification.status) +client = AsyncPostGrid() -# Prints '22-20 BAY ST FLOOR 11' -print(verification.data.line1) -# Verify a structured address -address = { - "line1": "22-20 bay st, floor 11", - "city": "toronto", - "province_or_state": "on", - "country": "ca" -} -structured_verification = postgrid.Verification.verify(address) +async def main() -> None: + all_letters = [] + # Iterate through items across all pages, issuing requests as needed. + async for letter in client.print_mail.letters.list( + limit=100, + ): + all_letters.append(letter) + print(all_letters) -# Prints '22-20 BAY ST FLOOR 11' -print(verification.data.line1) + +asyncio.run(main()) ``` -Note that API parameters map one-to-one with the underlying [REST API](https://docs.postgrid.com). -However, instead of `camelCase`, this library uses `snake_case`. For example, a `letterHTML` parameter -in the original API would be `letter_html` in this library. +Alternatively, you can use the `.has_next_page()`, `.next_page_info()`, or `.get_next_page()` methods for more granular control working with pages: + +```python +first_page = await client.print_mail.letters.list( + limit=100, +) +if first_page.has_next_page(): + print(f"will fetch next page using these details: {first_page.next_page_info()}") + next_page = await first_page.get_next_page() + print(f"number of items we just fetched: {len(next_page.data)}") -### Handling Exceptions +# Remove `await` for non-async usage. +``` -Errors produced by the Print & Mail API will raise a `PMError` exception. The `PMError` -object has a `type` field that can be used to determine what the error was. It also has a -`message` field which provides a human readable message describing the error in detail. +Or just work directly with the returned data: + +```python +first_page = await client.print_mail.letters.list( + limit=100, +) +for letter in first_page.data: + print(letter.id) + +# Remove `await` for non-async usage. +``` + +## Handling errors + +When the library is unable to connect to the API (for example, due to network connection problems or a timeout), a subclass of `postgrid.APIConnectionError` is raised. + +When the API returns a non-success status code (that is, 4xx or 5xx +response), a subclass of `postgrid.APIStatusError` is raised, containing `status_code` and `response` properties. + +All errors inherit from `postgrid.APIError`. ```python import postgrid +from postgrid import PostGrid -postgrid.pm_key = 'test_sk_...' +client = PostGrid() try: - template = postgrid.Template.create( - description='SDK Template', - html=''' - - -

Hi, {{to.firstName}}

-

Welcome to PostGrid.

-

Yours Truly,

-

{{from.firstName}} {{from.lastName}}

- - - ''', + client.address_verification.verify( + address="address", ) -except postgrid.PMError as e: +except postgrid.APIConnectionError as e: + print("The server could not be reached") + print(e.__cause__) # an underlying Exception, likely raised within httpx. +except postgrid.RateLimitError as e: + print("A 429 status code was received; we should back off a bit.") +except postgrid.APIStatusError as e: + print("Another non-200-range status code was received") print(e.status_code) - print(e.type) - print(e.message) + print(e.response) +``` + +Error codes are as follows: + +| Status Code | Error Type | +| ----------- | -------------------------- | +| 400 | `BadRequestError` | +| 401 | `AuthenticationError` | +| 403 | `PermissionDeniedError` | +| 404 | `NotFoundError` | +| 422 | `UnprocessableEntityError` | +| 429 | `RateLimitError` | +| >=500 | `InternalServerError` | +| N/A | `APIConnectionError` | - raise e +### Retries + +Certain errors are automatically retried 2 times by default, with a short exponential backoff. +Connection errors (for example, due to a network connectivity problem), 408 Request Timeout, 409 Conflict, +429 Rate Limit, and >=500 Internal errors are all retried by default. + +You can use the `max_retries` option to configure or disable retry settings: + +```python +from postgrid import PostGrid + +# Configure the default for all requests: +client = PostGrid( + # default is 2 + max_retries=0, +) + +# Or, configure per-request: +client.with_options(max_retries=5).address_verification.verify( + address="address", +) ``` -The Address Verification API will instead raise a `AVError` exception. The `AVError` -object has a `status` field that determines the result of your query. It also has a -`message` field which provides a human readable message describing the error in detail. +### Timeouts + +By default requests time out after 1 minute. You can configure this with a `timeout` option, +which accepts a float or an [`httpx.Timeout`](https://www.python-httpx.org/advanced/#fine-tuning-the-configuration) object: ```python +from postgrid import PostGrid + +# Configure the default for all requests: +client = PostGrid( + # 20 seconds (default is 1 minute) + timeout=20.0, +) + +# More granular control: +client = PostGrid( + timeout=httpx.Timeout(60.0, read=5.0, write=10.0, connect=2.0), +) + +# Override per-request: +client.with_options(timeout=5.0).address_verification.verify( + address="address", +) +``` + +On timeout, an `APITimeoutError` is thrown. + +Note that requests that time out are [retried twice by default](#retries). + +## Advanced + +### Logging + +We use the standard library [`logging`](https://docs.python.org/3/library/logging.html) module. + +You can enable logging by setting the environment variable `POST_GRID_LOG` to `info`. + +```shell +$ export POST_GRID_LOG=info +``` + +Or to `debug` for more verbose logging. + +### How to tell whether `None` means `null` or missing + +In an API response, a field may be explicitly `null`, or missing entirely; in either case, its value is `None` in this library. You can differentiate the two cases with `.model_fields_set`: + +```py +if response.my_field is None: + if 'my_field' not in response.model_fields_set: + print('Got json like {}, without a "my_field" key present at all.') + else: + print('Got json like {"my_field": null}.') +``` + +### Accessing raw response data (e.g. headers) + +The "raw" Response object can be accessed by prefixing `.with_raw_response.` to any HTTP method call, e.g., + +```py +from postgrid import PostGrid + +client = PostGrid() +response = client.address_verification.with_raw_response.verify( + address="address", +) +print(response.headers.get('X-My-Header')) + +address_verification = response.parse() # get the object that `address_verification.verify()` would have returned +print(address_verification.data) +``` + +These methods return an [`APIResponse`](https://github.com/stainless-sdks/postgrid-python/tree/main/src/postgrid/_response.py) object. + +The async client returns an [`AsyncAPIResponse`](https://github.com/stainless-sdks/postgrid-python/tree/main/src/postgrid/_response.py) with the same structure, the only difference being `await`able methods for reading the response content. + +#### `.with_streaming_response` + +The above interface eagerly reads the full response body when you make the request, which may not always be what you want. + +To stream the response body, use `.with_streaming_response` instead, which requires a context manager and only reads the response body once you call `.read()`, `.text()`, `.json()`, `.iter_bytes()`, `.iter_text()`, `.iter_lines()` or `.parse()`. In the async client, these are async methods. + +```python +with client.address_verification.with_streaming_response.verify( + address="address", +) as response: + print(response.headers.get("X-My-Header")) + + for line in response.iter_lines(): + print(line) +``` + +The context manager is required so that the response will reliably be closed. + +### Making custom/undocumented requests + +This library is typed for convenient access to the documented API. + +If you need to access undocumented endpoints, params, or response properties, the library can still be used. + +#### Undocumented endpoints + +To make requests to undocumented endpoints, you can make requests using `client.get`, `client.post`, and other +http verbs. Options on the client will be respected (such as retries) when making this request. + +```py +import httpx + +response = client.post( + "/foo", + cast_to=httpx.Response, + body={"my_param": True}, +) + +print(response.headers.get("x-foo")) +``` + +#### Undocumented request params + +If you want to explicitly send an extra param, you can do so with the `extra_query`, `extra_body`, and `extra_headers` request +options. + +#### Undocumented response properties + +To access undocumented response properties, you can access the extra fields like `response.unknown_prop`. You +can also get all the extra fields on the Pydantic model as a dict with +[`response.model_extra`](https://docs.pydantic.dev/latest/api/base_model/#pydantic.BaseModel.model_extra). + +### Configuring the HTTP client + +You can directly override the [httpx client](https://www.python-httpx.org/api/#client) to customize it for your use case, including: + +- Support for [proxies](https://www.python-httpx.org/advanced/proxies/) +- Custom [transports](https://www.python-httpx.org/advanced/transports/) +- Additional [advanced](https://www.python-httpx.org/advanced/clients/) functionality + +```python +import httpx +from postgrid import PostGrid, DefaultHttpxClient + +client = PostGrid( + # Or use the `POST_GRID_BASE_URL` env var + base_url="http://my.test.server.example.com:8083", + http_client=DefaultHttpxClient( + proxy="http://my.test.proxy.example.com", + transport=httpx.HTTPTransport(local_address="0.0.0.0"), + ), +) +``` + +You can also customize the client on a per-request basis by using `with_options()`: + +```python +client.with_options(http_client=DefaultHttpxClient(...)) +``` + +### Managing HTTP resources + +By default the library closes underlying HTTP connections whenever the client is [garbage collected](https://docs.python.org/3/reference/datamodel.html#object.__del__). You can manually close the client using the `.close()` method if desired, or with a context manager that closes when exiting. + +```py +from postgrid import PostGrid + +with PostGrid() as client: + # make requests here + ... + +# HTTP client is now closed +``` + +## Versioning + +This package generally follows [SemVer](https://semver.org/spec/v2.0.0.html) conventions, though certain backwards-incompatible changes may be released as minor versions: + +1. Changes that only affect static types, without breaking runtime behavior. +2. Changes to library internals which are technically public but not intended or documented for external use. _(Please open a GitHub issue to let us know if you are relying on such internals.)_ +3. Changes that we do not expect to impact the vast majority of users in practice. + +We take backwards-compatibility seriously and work hard to ensure you can rely on a smooth upgrade experience. + +We are keen for your feedback; please open an [issue](https://www.github.com/stainless-sdks/postgrid-python/issues) with questions, bugs, or suggestions. + +### Determining the installed version + +If you've upgraded to the latest version but aren't seeing any new features you were expecting then your python environment is likely still using an older version. + +You can determine the version that is being used at runtime with: + +```py import postgrid +print(postgrid.__version__) +``` -postgrid.av_key = 'test_sk_...' +## Requirements -try: - addresses = { - "addresses": [ - { - "line1": "145 mulberry st ph d", - "city": "new york", - "province_or_state": "new york", - "postal_or_zip": "10013", - "country": "US" - } - ] - } - verfication = postgrid.Verification.batch_verify(addresses) - -except postgrid.AVError as e: - print(e.status) - print(e.message) - - raise e -``` - -## Support - -Email `support@postgrid.com` if you face any errors with the API or create issues in the -`postgrid-python` GitHub if you face issues with the SDK itself. +Python 3.8 or higher. + +## Contributing + +See [the contributing documentation](./CONTRIBUTING.md). diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..993268d --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,27 @@ +# Security Policy + +## Reporting Security Issues + +This SDK is generated by [Stainless Software Inc](http://stainlessapi.com). Stainless takes security seriously, and encourages you to report any security vulnerability promptly so that appropriate action can be taken. + +To report a security issue, please contact the Stainless team at security@stainlessapi.com. + +## Responsible Disclosure + +We appreciate the efforts of security researchers and individuals who help us maintain the security of +SDKs we generate. If you believe you have found a security vulnerability, please adhere to responsible +disclosure practices by allowing us a reasonable amount of time to investigate and address the issue +before making any information public. + +## Reporting Non-SDK Related Security Issues + +If you encounter security issues that are not directly related to SDKs but pertain to the services +or products provided by Post Grid please follow the respective company's security reporting guidelines. + +### Post Grid Terms and Policies + +Please contact support@postgrid.com for any questions or concerns regarding security of our services. + +--- + +Thank you for helping us keep the SDKs and systems they interact with secure. diff --git a/api.md b/api.md new file mode 100644 index 0000000..64a2eb5 --- /dev/null +++ b/api.md @@ -0,0 +1,368 @@ +# AddressVerification + +Types: + +```python +from postgrid.types import Errors, Status, AddressVerificationVerifyResponse +``` + +Methods: + +- client.address_verification.verify(\*\*params) -> AddressVerificationVerifyResponse + +# IntlAddressVerification + +Types: + +```python +from postgrid.types import IntlAddressVerificationVerifyResponse +``` + +Methods: + +- client.intl_address_verification.verify(\*\*params) -> IntlAddressVerificationVerifyResponse + +# PrintMail + +Types: + +```python +from postgrid.types import ContactCreateWithCompanyName, ContactCreateWithFirstName +``` + +## BankAccounts + +Types: + +```python +from postgrid.types.print_mail import BankAccount, BankAccountCountryCode, BankAccountDeleteResponse +``` + +Methods: + +- client.print_mail.bank_accounts.create(\*\*params) -> BankAccount +- client.print_mail.bank_accounts.retrieve(id) -> BankAccount +- client.print_mail.bank_accounts.list(\*\*params) -> SyncSkipLimit[BankAccount] +- client.print_mail.bank_accounts.delete(id) -> BankAccountDeleteResponse + +## Boxes + +Types: + +```python +from postgrid.types.print_mail import ( + Box, + BoxChequeBase, + Cancellation, + OrderImbStatus, + OrderMailingClass, + OrderStatus, +) +``` + +Methods: + +- client.print_mail.boxes.create(\*\*params) -> Box +- client.print_mail.boxes.retrieve(id) -> Box +- client.print_mail.boxes.list(\*\*params) -> SyncSkipLimit[Box] +- client.print_mail.boxes.delete(id) -> Box + +## Campaigns + +Types: + +```python +from postgrid.types.print_mail import Campaign, CampaignDeleteResponse +``` + +Methods: + +- client.print_mail.campaigns.create(\*\*params) -> Campaign +- client.print_mail.campaigns.retrieve(id) -> Campaign +- client.print_mail.campaigns.update(id, \*\*params) -> Campaign +- client.print_mail.campaigns.list(\*\*params) -> SyncSkipLimit[Campaign] +- client.print_mail.campaigns.delete(id) -> CampaignDeleteResponse +- client.print_mail.campaigns.send(id, \*\*params) -> Campaign + +## Cheques + +Types: + +```python +from postgrid.types.print_mail import Cheque, ChequeSize, DigitalOnly, ChequeRetrieveURLResponse +``` + +Methods: + +- client.print_mail.cheques.create(\*\*params) -> Cheque +- client.print_mail.cheques.retrieve(id) -> Cheque +- client.print_mail.cheques.list(\*\*params) -> SyncSkipLimit[Cheque] +- client.print_mail.cheques.delete(id) -> Cheque +- client.print_mail.cheques.retrieve_url(id) -> ChequeRetrieveURLResponse +- client.print_mail.cheques.retrieve_with_deposit_ready_pdf(id) -> Cheque + +## Contacts + +Types: + +```python +from postgrid.types.print_mail import Contact, ContactCreate, ContactDeleteResponse +``` + +Methods: + +- client.print_mail.contacts.create(\*\*params) -> Contact +- client.print_mail.contacts.retrieve(id) -> Contact +- client.print_mail.contacts.list(\*\*params) -> SyncSkipLimit[Contact] +- client.print_mail.contacts.delete(id) -> ContactDeleteResponse + +## Letters + +Types: + +```python +from postgrid.types.print_mail import ( + AddressPlacement, + AttachedPdf, + Letter, + LetterSize, + PlasticCard, + LetterRetrieveURLResponse, +) +``` + +Methods: + +- client.print_mail.letters.create(\*\*params) -> Letter +- client.print_mail.letters.retrieve(id) -> Letter +- client.print_mail.letters.list(\*\*params) -> SyncSkipLimit[Letter] +- client.print_mail.letters.delete(id) -> Letter +- client.print_mail.letters.retrieve_url(id) -> LetterRetrieveURLResponse + +## MailingListImports + +Types: + +```python +from postgrid.types.print_mail import ( + FileType, + MailingListImportResponse, + VerificationStatusCount, + MailingListImportDeleteResponse, +) +``` + +Methods: + +- client.print_mail.mailing_list_imports.create(\*\*params) -> MailingListImportResponse +- client.print_mail.mailing_list_imports.retrieve(id) -> MailingListImportResponse +- client.print_mail.mailing_list_imports.update(id, \*\*params) -> MailingListImportResponse +- client.print_mail.mailing_list_imports.list(\*\*params) -> SyncSkipLimit[MailingListImportResponse] +- client.print_mail.mailing_list_imports.delete(id) -> MailingListImportDeleteResponse + +## MailingLists + +Types: + +```python +from postgrid.types.print_mail import MailingList, MailingListUpdate, MailingListDeleteResponse +``` + +Methods: + +- client.print_mail.mailing_lists.create(\*\*params) -> MailingList +- client.print_mail.mailing_lists.retrieve(id) -> MailingList +- client.print_mail.mailing_lists.update(id, \*\*params) -> MailingListUpdate +- client.print_mail.mailing_lists.list(\*\*params) -> SyncSkipLimit[MailingList] +- client.print_mail.mailing_lists.delete(id) -> MailingListDeleteResponse +- client.print_mail.mailing_lists.jobs(id, \*\*params) -> MailingList + +## OrderProfiles + +### Cheques + +Types: + +```python +from postgrid.types.print_mail.order_profiles import ( + ChequeProfile, + CurrencyCode, + ChequeListResponse, + ChequeDeleteResponse, +) +``` + +Methods: + +- client.print_mail.order_profiles.cheques.create(\*\*params) -> ChequeProfile +- client.print_mail.order_profiles.cheques.retrieve(id, \*\*params) -> ChequeProfile +- client.print_mail.order_profiles.cheques.update(id, \*\*params) -> ChequeProfile +- client.print_mail.order_profiles.cheques.list(\*\*params) -> SyncSkipLimit[ChequeListResponse] +- client.print_mail.order_profiles.cheques.delete(id) -> ChequeDeleteResponse + +### Letters + +Types: + +```python +from postgrid.types.print_mail.order_profiles import LetterProfile, LetterDeleteResponse +``` + +Methods: + +- client.print_mail.order_profiles.letters.create(\*\*params) -> LetterProfile +- client.print_mail.order_profiles.letters.retrieve(id, \*\*params) -> LetterProfile +- client.print_mail.order_profiles.letters.update(id, \*\*params) -> LetterProfile +- client.print_mail.order_profiles.letters.list(\*\*params) -> SyncSkipLimit[LetterProfile] +- client.print_mail.order_profiles.letters.delete(id) -> LetterDeleteResponse + +### Postcards + +Types: + +```python +from postgrid.types.print_mail.order_profiles import ( + PostcardProfile, + PostcardSize, + PostcardDeleteResponse, +) +``` + +Methods: + +- client.print_mail.order_profiles.postcards.create(\*\*params) -> PostcardProfile +- client.print_mail.order_profiles.postcards.retrieve(id, \*\*params) -> PostcardProfile +- client.print_mail.order_profiles.postcards.update(id, \*\*params) -> PostcardProfile +- client.print_mail.order_profiles.postcards.list(\*\*params) -> SyncSkipLimit[PostcardProfile] +- client.print_mail.order_profiles.postcards.delete(id) -> PostcardDeleteResponse + +### SelfMailers + +Types: + +```python +from postgrid.types.print_mail.order_profiles import ( + SelfMailerProfile, + SelfMailerSize, + SelfMailerDeleteResponse, +) +``` + +Methods: + +- client.print_mail.order_profiles.self_mailers.create(\*\*params) -> SelfMailerProfile +- client.print_mail.order_profiles.self_mailers.retrieve(id, \*\*params) -> SelfMailerProfile +- client.print_mail.order_profiles.self_mailers.update(id, \*\*params) -> SelfMailerProfile +- client.print_mail.order_profiles.self_mailers.list(\*\*params) -> SyncSkipLimit[SelfMailerProfile] +- client.print_mail.order_profiles.self_mailers.delete(id) -> SelfMailerDeleteResponse + +## Postcards + +Types: + +```python +from postgrid.types.print_mail import Postcard, PostcardRetrieveURLResponse +``` + +Methods: + +- client.print_mail.postcards.create(\*\*params) -> Postcard +- client.print_mail.postcards.retrieve(id) -> Postcard +- client.print_mail.postcards.list(\*\*params) -> SyncSkipLimit[Postcard] +- client.print_mail.postcards.delete(id) -> Postcard +- client.print_mail.postcards.retrieve_url(id) -> PostcardRetrieveURLResponse + +## Reports + +Types: + +```python +from postgrid.types.print_mail import DeletedResponse, Report +``` + +Methods: + +- client.print_mail.reports.create(\*\*params) -> Report +- client.print_mail.reports.retrieve(id) -> Report +- client.print_mail.reports.update(id, \*\*params) -> Report +- client.print_mail.reports.list(\*\*params) -> SyncSkipLimit[Report] +- client.print_mail.reports.delete(id) -> DeletedResponse +- client.print_mail.reports.sample(\*\*params) -> ReportSample + +### Samples + +Types: + +```python +from postgrid.types.print_mail.reports import ReportSample, ReportSampleCreateBase +``` + +Methods: + +- client.print_mail.reports.samples.create(id, \*\*params) -> ReportSample + +### Exports + +Types: + +```python +from postgrid.types.print_mail.reports import ReportExport +``` + +Methods: + +- client.print_mail.reports.exports.create(report_id, \*\*params) -> ReportExport +- client.print_mail.reports.exports.retrieve(export_id, \*, report_id) -> ReportExport +- client.print_mail.reports.exports.delete(export_id, \*, report_id) -> DeletedResponse + +## SelfMailers + +Types: + +```python +from postgrid.types.print_mail import SelfMailer, SelfMailerRetrieveURLResponse +``` + +Methods: + +- client.print_mail.self_mailers.create(\*\*params) -> SelfMailer +- client.print_mail.self_mailers.retrieve(id) -> SelfMailer +- client.print_mail.self_mailers.list(\*\*params) -> SyncSkipLimit[SelfMailer] +- client.print_mail.self_mailers.delete(id) -> SelfMailer +- client.print_mail.self_mailers.retrieve_url(id) -> SelfMailerRetrieveURLResponse + +## SubOrganizations + +Types: + +```python +from postgrid.types.print_mail import ( + EmailPreferences, + SubOrganization, + SubOrganizationUpdateResponse, + SubOrganizationRetrieveUsersResponse, +) +``` + +Methods: + +- client.print_mail.sub_organizations.retrieve(id) -> SubOrganization +- client.print_mail.sub_organizations.update(\*\*params) -> SubOrganizationUpdateResponse +- client.print_mail.sub_organizations.list(\*\*params) -> SyncSkipLimit[SubOrganization] +- client.print_mail.sub_organizations.retrieve_users(id, \*\*params) -> SubOrganizationRetrieveUsersResponse + +## Templates + +Types: + +```python +from postgrid.types.print_mail import Template, TemplateDeleteResponse +``` + +Methods: + +- client.print_mail.templates.create(\*\*params) -> Template +- client.print_mail.templates.retrieve(id) -> Template +- client.print_mail.templates.update(id, \*\*params) -> Template +- client.print_mail.templates.list(\*\*params) -> SyncSkipLimit[Template] +- client.print_mail.templates.delete(id) -> TemplateDeleteResponse diff --git a/bin/publish-pypi b/bin/publish-pypi new file mode 100644 index 0000000..05bfccb --- /dev/null +++ b/bin/publish-pypi @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +set -eux +mkdir -p dist +rye build --clean +# Patching importlib-metadata version until upstream library version is updated +# https://github.com/pypa/twine/issues/977#issuecomment-2189800841 +"$HOME/.rye/self/bin/python3" -m pip install 'importlib-metadata==7.2.1' +rye publish --yes --token=$PYPI_TOKEN diff --git a/examples/.keep b/examples/.keep new file mode 100644 index 0000000..d8c73e9 --- /dev/null +++ b/examples/.keep @@ -0,0 +1,4 @@ +File generated from our OpenAPI spec by Stainless. + +This directory can be used to store example files demonstrating usage of this SDK. +It is ignored by Stainless code generation and its content (other than this keep file) won't be touched. \ No newline at end of file diff --git a/mypy.ini b/mypy.ini new file mode 100644 index 0000000..70761d3 --- /dev/null +++ b/mypy.ini @@ -0,0 +1,50 @@ +[mypy] +pretty = True +show_error_codes = True + +# Exclude _files.py because mypy isn't smart enough to apply +# the correct type narrowing and as this is an internal module +# it's fine to just use Pyright. +# +# We also exclude our `tests` as mypy doesn't always infer +# types correctly and Pyright will still catch any type errors. +exclude = ^(src/postgrid/_files\.py|_dev/.*\.py|tests/.*)$ + +strict_equality = True +implicit_reexport = True +check_untyped_defs = True +no_implicit_optional = True + +warn_return_any = True +warn_unreachable = True +warn_unused_configs = True + +# Turn these options off as it could cause conflicts +# with the Pyright options. +warn_unused_ignores = False +warn_redundant_casts = False + +disallow_any_generics = True +disallow_untyped_defs = True +disallow_untyped_calls = True +disallow_subclassing_any = True +disallow_incomplete_defs = True +disallow_untyped_decorators = True +cache_fine_grained = True + +# By default, mypy reports an error if you assign a value to the result +# of a function call that doesn't return anything. We do this in our test +# cases: +# ``` +# result = ... +# assert result is None +# ``` +# Changing this codegen to make mypy happy would increase complexity +# and would not be worth it. +disable_error_code = func-returns-value,overload-cannot-match + +# https://github.com/python/mypy/issues/12162 +[mypy.overrides] +module = "black.files.*" +ignore_errors = true +ignore_missing_imports = true diff --git a/noxfile.py b/noxfile.py new file mode 100644 index 0000000..53bca7f --- /dev/null +++ b/noxfile.py @@ -0,0 +1,9 @@ +import nox + + +@nox.session(reuse_venv=True, name="test-pydantic-v1") +def test_pydantic_v1(session: nox.Session) -> None: + session.install("-r", "requirements-dev.lock") + session.install("pydantic<2") + + session.run("pytest", "--showlocals", "--ignore=tests/functional", *session.posargs) diff --git a/postgrid/__init__.py b/postgrid/__init__.py deleted file mode 100644 index 6f10369..0000000 --- a/postgrid/__init__.py +++ /dev/null @@ -1,879 +0,0 @@ -import requests -import jwt -import os - -pm_base = "https://api.postgrid.com/print-mail/v1/" -av_base = "https://api.postgrid.com/v1/addver/" -intl_av_base = "https://api.postgrid.com/v1/intl_addver/" - -pm_key = None -av_key = None - -MAX_LIMIT = 100 -KNOWN_ABBREVS = {"HTML", "URL", "ID", "PDF"} - - -def _is_file_like(f): - return hasattr(f, "read") - - -def _camel_to_snake(s): - new_s = "" - - # Turn keys like frontHTML to frontHtml so it converts to front_html - # instead of front_hTML - for abbrev in KNOWN_ABBREVS: - s = s.replace(abbrev, abbrev[0] + abbrev[1:].lower()) - - for i, ch in enumerate(s): - if i > 0 and ch.isupper() and s[i - 1].islower(): - new_s += "_" + ch.lower() - else: - new_s += ch - - return new_s.lower() - - -def _snake_to_camel(s): - new_s = "" - - # Convert e.g. letter_html to letter_HTML so that - # we transmit letterHTML as intended. We match on the '_' + abbrev - # so that we don't replace e.g. double_sided with double_sIDed. - for abbrev in KNOWN_ABBREVS: - lower_abbrev = f"_{abbrev.lower()}" - lower_abbrev_index = s.find(lower_abbrev) - - if lower_abbrev_index > 0: - s = s.replace(lower_abbrev, abbrev.upper()) - - for i, ch in enumerate(s): - if s[i] == "_": - continue - - if i > 0 and s[i - 1] == "_": - new_s += ch.upper() - else: - new_s += ch - - return new_s - - -def _map_keys_recursive(d, fn): - if isinstance(d, list): - return list(map(lambda v: _map_keys_recursive(v, fn), d)) - elif not isinstance(d, dict): - return d - - new_d = {} - - for key, value in d.items(): - # HACK We should not mess with the camel/snake casing of merge variables/metadata - new_d[fn(key)] = ( - value - if key == "metadata" or key == "merge_variables" - else _map_keys_recursive(value, fn) - ) - - return new_d - - -def flatten(files, array, key, value): - if _is_file_like(value): - if not files: - files = {} - - path, ext = os.path.splitext(value.name) - - if ext == ".pdf": - content_type = "application/pdf" - elif ext == ".png": - content_type = "image/png" - elif ext == ".jpeg": - content_type = "image/jpeg" - elif ext == ".jpg": - content_type = "image/jpeg" - else: - # TODO Make sure we don't expect any other file types - raise UnsupportedFileTypeError(ext) - - files[key] = (os.path.basename(path), value, content_type) - elif isinstance(value, dict): - for inner_key, inner_value in value.items(): - flatten(files, array, f"{key}[{inner_key}]", inner_value) - elif isinstance(value, list): - for inner_value in value: - flatten(files, array, f"{key}[]", inner_value) - elif isinstance(value, bool): - array.append((key, "true" if value else "false")) - elif value is not None: - array.append((key, value)) - return files - - -class UnsupportedFileTypeError(Exception): - def __init__(self, ext): - self.ext = ext - - -def _request(endpoint, method="GET", idempotency_key=None, **kwargs): - kwargs = _map_keys_recursive(kwargs, _snake_to_camel) - - headers = {"x-api-key": pm_key} - - if idempotency_key is not None: - headers["Idempotency-Key"] = idempotency_key - - if method == "GET": - res = requests.get(pm_base + endpoint, params=kwargs, headers=headers) - elif method == "POST": - data = [] - files = None - - for key, value in kwargs.items(): - files = flatten(files, data, key, value) - res = requests.post(pm_base + endpoint, data=data, files=files, headers=headers) - elif method == "DELETE": - res = requests.delete(pm_base + endpoint, params=kwargs, headers=headers) - else: - raise NotImplementedError() - - value = res.json() - - try: - res.raise_for_status() - except requests.HTTPError as e: - raise PMError( - status_code=res.status_code, - object=value["object"], - type=value["error"]["type"], - message=value["error"]["message"], - ) - - return _pm_convert_json_value(value) - - -def _av_request(endpoint, intl=False, method="GET", data=None, params=None, json=None): - request_endpoint = (intl_av_base if intl else av_base) + endpoint - - data = _map_keys_recursive(data, _snake_to_camel) - params = _map_keys_recursive(params, _snake_to_camel) - json = _map_keys_recursive(json, _snake_to_camel) - - data_to_pass = [] - params_to_pass = [] - files = None - - if data: - for key, value in data.items(): - flatten(files, data_to_pass, key, value) - if params: - for key, value in params.items(): - flatten(files, params_to_pass, key, value) - - headers = {"x-api-key": av_key} - - if method == "GET": - res = requests.get(request_endpoint, params=params_to_pass, headers=headers) - elif method == "POST": - res = requests.post( - request_endpoint, - json=json, - data=data_to_pass, - params=params_to_pass, - headers=headers, - ) - elif method == "DELETE": - res = requests.delete(request_endpoint, params=params_to_pass, headers=headers) - else: - raise NotImplementedError() - - value = res.json() - - try: - res.raise_for_status() - except requests.HTTPError as e: - raise AVError(status_code=res.status_code, message=value["message"]) - return _av_convert_json_value(f"intl/{endpoint}" if intl else endpoint, value) - - -def _pm_get(endpoint, **kwargs): - return _request(endpoint, method="GET", **kwargs) - - -def _pm_post(endpoint, **kwargs): - return _request(endpoint, method="POST", **kwargs) - - -def _pm_delete(endpoint, **kwargs): - return _request(endpoint, method="DELETE", **kwargs) - - -class PMError(Exception): - def __init__(self, status_code, object, type, message): - super().__init__(message) - - self.status_code = status_code - self.object = object - self.type = type - self.message = message - - -class AVError(Exception): - def __init__(self, status_code, message): - super().__init__(message) - - self.status_code = status_code - self.message = message - - -class CreateableResource: - @classmethod - def create(cls, locals_, parent_resource_id=None): - assert "cls" in locals_ - assert "kwargs" in locals_ - - locals_except_kwargs_and_cls = { - key: value - for key, value in locals_.items() - if key != "kwargs" and key != "cls" - } - - return _pm_post( - cls.endpoint.format(parent_resource_id), - **locals_except_kwargs_and_cls, - **locals_["kwargs"], - ) - - -class ProgressableResource: - @classmethod - def progress(cls, id): - return _pm_post(f"{cls.endpoint}/{id}/progressions") - - -class ListableResource: - @classmethod - def list(cls, skip=0, limit=10, parent_resource_id=None, search=None): - return _pm_get( - cls.endpoint.format(parent_resource_id), - skip=skip, - limit=limit, - search=search, - ) - - @classmethod - def list_autopaginate(cls, max=None, parent_resource_id=None, search=None): - # We call the derived class's list method repeatedly - - # FIXME: Wasteful because we request MAX_LIMIT always and then potentially discard - # a certain number of contacts. - data = [] - last_total_count = None - - while True: - list_ = cls.list( - skip=len(data), - limit=MAX_LIMIT, - parent_resource_id=parent_resource_id, - search=search, - ) - - data.extend(list_.data) - last_total_count = list_.total_count - - if len(data) >= list_.total_count or (max and len(data) >= max): - break - - if max: - data = data[:max] - - return List( - object="list", - total_count=last_total_count, - skip=0, - limit=max if max else len(data), - data=data, - ) - - -class RetrieveableResource: - @classmethod - def retrieve(cls, id, parent_resource_id=None, **kwargs): - return _pm_get(f"{cls.endpoint.format(parent_resource_id)}/{id}", **kwargs) - - -class UpdatableResource: - @classmethod - def update(cls, id, locals_, parent_resource_id=None, **kwargs): - # HACK This is copied from create, the only difference is the endpoint - assert "cls" in locals_ - assert "kwargs" in locals_ - - locals_except_kwargs_and_cls = { - key: value - for key, value in locals_.items() - if key != "kwargs" and key != "cls" - } - - return _pm_post( - f"{cls.endpoint.format(parent_resource_id)}/{id}", - **locals_except_kwargs_and_cls, - **locals_["kwargs"], - ) - - -class DeleteableResource: - @classmethod - def delete(cls, id, parent_resource_id=None): - return _pm_delete(f"{cls.endpoint.format(parent_resource_id)}/{id}") - - -class CancellableCollateral(DeleteableResource): - @classmethod - def delete_with_note( - cls, - id, - note, - parent_resource_id=None, - ): - return _pm_post( - f"{cls.endpoint.format(parent_resource_id)}/{id}/cancellation", note=note - ) - - -class BaseResource: - def __init__(self, **kwargs): - self.__dict__.update(kwargs) - - -class List: - def __init__(self, object, total_count, skip, limit, data): - self.object = object - self.total_count = total_count - self.skip = skip - self.limit = limit - - if len(data) > 0 and isinstance(data[0], dict): - data = list(map(_pm_convert_json_value, data)) - - self.data = data - - -class Contact( - BaseResource, - CreateableResource, - RetrieveableResource, - ListableResource, - DeleteableResource, -): - endpoint = "contacts" - - @classmethod - def create( - cls, - address_line1, - country_code, - first_name=None, - last_name=None, - company_name=None, - address_line2=None, - city=None, - province_or_state=None, - email=None, - phone_number=None, - job_title=None, - postal_or_zip=None, - **kwargs, - ): - return super().create(locals()) - - -class Template( - BaseResource, - CreateableResource, - RetrieveableResource, - ListableResource, - UpdatableResource, - DeleteableResource, -): - endpoint = "templates" - - @classmethod - def create(cls, html, **kwargs): - return super().create(locals()) - - @classmethod - def update(cls, id, html, **kwargs): - return super().update(id, locals()) - - -class BankAccount( - BaseResource, - CreateableResource, - RetrieveableResource, - ListableResource, - DeleteableResource, -): - endpoint = "bank_accounts" - - @classmethod - def create( - cls, - bank_name, - account_number, - bank_primary_line, - bank_country_code, - transit_number=None, - route_number=None, - routing_number=None, - signature_image=None, - signature_text=None, - bank_secondary_line=None, - **kwargs, - ): - return super().create(locals()) - - -class Letter( - BaseResource, - CreateableResource, - RetrieveableResource, - ListableResource, - ProgressableResource, - CancellableCollateral, -): - endpoint = "letters" - - @classmethod - def create( - cls, - to, - from_, - template=None, - html=None, - pdf=None, - extra_service=None, - express=None, - address_placement=None, - perforated_page=None, - envelope_type=None, - color=None, - double_sided=None, - return_envelope=None, - send_date=None, - merge_variables=None, - **kwargs, - ): - return super().create(locals()) - - -class Postcard( - BaseResource, - CreateableResource, - RetrieveableResource, - ListableResource, - ProgressableResource, - CancellableCollateral, -): - endpoint = "postcards" - - @classmethod - def create( - cls, - to, - size, - from_=None, - front_template=None, - back_template=None, - front_html=None, - back_html=None, - pdf=None, - express=None, - send_date=None, - merge_variables=None, - **kwargs, - ): - return super().create(locals()) - - -class Cheque( - BaseResource, - CreateableResource, - RetrieveableResource, - ListableResource, - ProgressableResource, - CancellableCollateral, -): - endpoint = "cheques" - - @classmethod - def create( - cls, - to, - from_, - bank_account, - amount, - number=None, - message=None, - memo=None, - letter_html=None, - letter_template=None, - letter_pdf=None, - extra_service=None, - express=None, - send_date=None, - redirect_to=None, - logo=None, - currency_code=None, - merge_variables=None, - **kwargs, - ): - return super().create(locals()) - - -class ReturnEnvelopeOrder( - BaseResource, - CreateableResource, - RetrieveableResource, - ListableResource, - DeleteableResource, -): - endpoint = "return_envelopes/{}/orders" - - @classmethod - def create(cls, return_envelope_id, quantity_ordered, **kwargs): - return super().create(locals(), parent_resource_id=return_envelope_id) - - @classmethod - def retrieve(cls, return_envelope_id, id): - return super().retrieve(id, return_envelope_id) - - @classmethod - def list(cls, return_envelope_id, skip=0, limit=10, search=None): - return super().list(skip, limit, return_envelope_id, search=search) - - @classmethod - def list_autopaginate(cls, return_envelope_id, max=None, search=None): - return super().list_autopaginate(max, return_envelope_id, search=search) - - @classmethod - def fill(cls, return_envelope_id, id): - return _pm_post(f"{cls.endpoint.format(return_envelope_id)}/{id}/fills") - - @classmethod - def delete(cls, return_envelope_id, id): - return super().delete(id, return_envelope_id) - - -class ReturnEnvelope( - BaseResource, CreateableResource, RetrieveableResource, ListableResource -): - endpoint = "return_envelopes" - - @classmethod - def create(cls, to, **kwargs): - return super().create(locals()) - - -class WebhookEvent: - def __init__(self, type_, data): - self.type = type_ - self.data = data - - -class SignatureVerificationError(Exception): - pass - - -class WebhookInvocation(BaseResource, ListableResource): - endpoint = "webhooks/{}/invocations" - - @classmethod - def list(cls, webhook_id, skip=0, limit=0, search=None): - return super().list(skip, limit, webhook_id, search=search) - - @classmethod - def list_autopaginate(cls, webhook_id, max=None, search=None): - return super().list_autopaginate(max, webhook_id, search=search) - - -class Webhook( - BaseResource, - CreateableResource, - RetrieveableResource, - ListableResource, - UpdatableResource, - DeleteableResource, -): - endpoint = "webhooks" - - @classmethod - def create(cls, enabled_events, url, **kwargs): - return super().create(locals()) - - @classmethod - def update(cls, id, enabled_events, url, **kwargs): - return super().update(id, locals()) - - @classmethod - def construct_event(cls, payload, secret): - try: - event = jwt.decode(payload, secret, algorithms="HS256") - - type_ = event["type"] - event["data"]["object"] = type_.split(".")[0] - - return WebhookEvent(type_=type_, data=_pm_convert_json_value(event["data"])) - except jwt.exceptions.InvalidSignatureError: - raise SignatureVerificationError() - except jwt.exceptions.InvalidTokenError: - raise ValueError() - - -class LookupInfo(BaseResource): - @classmethod - def lookup_info(cls): - return _av_request("", method="GET") - - -class Verification(BaseResource): - @classmethod - def verify(cls, address=None, include_details=None, proper_case=None, geocode=None): - return _av_request( - endpoint="verifications", - method="POST", - params={ - "include_details": include_details, - "proper_case": proper_case, - "geocode": geocode, - }, - json={"address": address}, - ) - - @classmethod - def batch_verify( - cls, raw_body=None, include_details=None, proper_case=None, geocode=None - ): - return _av_request( - endpoint="verifications/batch", - method="POST", - params={ - "include_details": include_details, - "proper_case": proper_case, - "geocode": geocode, - }, - json=raw_body, - ) - - -class Autocomplete(BaseResource): - @classmethod - def previews( - cls, - partial_street=None, - city_filter=None, - state_filter=None, - pc_filter=None, - country_filter=None, - prov_instead_of_pc=None, - proper_case=None, - ): - return _av_request("completions", method="GET", params=locals()) - - @classmethod - def address( - cls, - partial_street=None, - city_filter=None, - state_filter=None, - pc_filter=None, - country_filter=None, - index=None, - geocode=None, - ): - return _av_request( - endpoint="completions", - method="POST", - data={ - "partial_street": partial_street, - "city_filter": city_filter, - "state_filter": state_filter, - "pc_filter": pc_filter, - "country_filter": country_filter, - "geocode": geocode, - }, - params={"index": index}, - ) - - -class Suggestion(BaseResource): - @classmethod - def suggest_addresses( - cls, address=None, include_details=None, proper_case=None, geocode=None - ): - return _av_request( - endpoint="suggestions", - method="POST", - json={"address": address}, - params={ - "include_details": include_details, - "proper_case": proper_case, - "geocode": geocode, - }, - ) - - -class Parse(BaseResource): - @classmethod - def parse_address(cls, address=None): - return _av_request(endpoint="parses", method="POST", data=locals()) - - -class CityState(BaseResource): - @classmethod - def lookup_city_state(cls, postal_or_zip=None): - return _av_request(endpoint="city_states", method="POST", data=locals()) - - -class IntlVerification(BaseResource): - @classmethod - def verify( - cls, address=None, include_details=None, proper_case=None, geo_data=None - ): - return _av_request( - endpoint="verifications", - intl=True, - method="POST", - params={ - "include_details": include_details, - "proper_case": proper_case, - "geo_data": geo_data, - }, - json={"address": address}, - ) - - @classmethod - def batch_verify( - cls, raw_body=None, include_details=None, proper_case=None, geo_data=None - ): - return _av_request( - endpoint="verifications/batch", - intl=True, - method="POST", - params={ - "include_details": include_details, - "proper_case": proper_case, - "geo_data": geo_data, - }, - json=raw_body, - ) - - -class IntlAutocomplete(BaseResource): - @classmethod - def previews( - cls, - partial_street=None, - countries_filter=None, - limit=None, - language=None, - city_filter=None, - street_filter=None, - postal_or_zip_filter=None, - proper_case=None, - ): - return _av_request( - endpoint="completions", intl=True, method="GET", params=locals() - ) - - @classmethod - def advanced_previews( - cls, - container=None, - language=None, - advanced=None, - ): - return _av_request( - endpoint="completions", intl=True, method="GET", params=locals() - ) - - @classmethod - def address( - cls, - id=None, - ): - return _av_request( - endpoint="completions", intl=True, method="POST", data=locals() - ) - - -PM_OBJECT_TO_CLASS = { - "contact": Contact, - "template": Template, - "bank_account": BankAccount, - "letter": Letter, - "postcard": Postcard, - "cheque": Cheque, - "webhook": Webhook, - "webhook_invocation": WebhookInvocation, - "return_envelope": ReturnEnvelope, - "return_envelope_order": ReturnEnvelopeOrder, - "list": List, -} - -AV_OBJECT_TO_CLASS = { - "": LookupInfo, - "verifications": Verification, - "intl/verifications": IntlVerification, - "verifications/batch": Verification, - "intl/verifications/batch": IntlVerification, - "completions": Autocomplete, - "intl/completions": IntlAutocomplete, - "suggestions": Suggestion, - "parses": Parse, - "city_states": CityState, - "list": List, -} - - -def _pm_convert_json_value(value): - new_value = {} - - for key, inner_value in value.items(): - # HACK We convert mergeVariables to merge_variables but we - # do not convert the inner value - if key == "mergeVariables" and isinstance(inner_value, dict): - new_value[_camel_to_snake(key)] = inner_value - continue - - if ( - key != "metadata" - and isinstance(inner_value, dict) - and "object" in inner_value - ): - new_value[_camel_to_snake(key)] = _pm_convert_json_value(inner_value) - continue - - new_value[key] = inner_value - - return PM_OBJECT_TO_CLASS[new_value["object"]]( - **_map_keys_recursive(new_value, _camel_to_snake) - ) - - -def _av_convert_json_value(type, value): - new_value = {} - for key, inner_value in value.items(): - if isinstance(inner_value, dict): - new_value[_camel_to_snake(key)] = _av_convert_json_value(type, inner_value) - continue - elif isinstance(inner_value, list): - for i in range(len(inner_value)): - if isinstance(inner_value[i], dict): - inner_value[i] = _av_convert_json_value(type, inner_value[i]) - new_value[_camel_to_snake(key)] = inner_value - continue - new_value[key] = inner_value - - return AV_OBJECT_TO_CLASS[type](**_map_keys_recursive(new_value, _camel_to_snake)) diff --git a/pyproject.toml b/pyproject.toml index 63e1cbd..31a2aac 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,20 +1,208 @@ -[build-system] -requires = ["setuptools>=61.0"] -build-backend = "setuptools.build_meta" - [project] -name = "postgrid-python" -version = "1.0.14" -description = "A Python Library for the PostGrid Print & Mail API" -readme = "README.md" -requires-python = ">=3.6" +name = "postgrid" +version = "0.0.1-alpha.0" +description = "The official Python library for the PostGrid API" +dynamic = ["readme"] +license = "Apache-2.0" +authors = [ +{ name = "Post Grid", email = "support@postgrid.com" }, +] +dependencies = [ + "httpx>=0.23.0, <1", + "pydantic>=1.9.0, <3", + "typing-extensions>=4.10, <5", + "anyio>=3.5.0, <5", + "distro>=1.7.0, <2", + "sniffio", +] +requires-python = ">= 3.8" classifiers = [ - "Programming Language :: Python :: 3", - "License :: OSI Approved :: MIT License", - "Operating System :: OS Independent", + "Typing :: Typed", + "Intended Audience :: Developers", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Operating System :: OS Independent", + "Operating System :: POSIX", + "Operating System :: MacOS", + "Operating System :: POSIX :: Linux", + "Operating System :: Microsoft :: Windows", + "Topic :: Software Development :: Libraries :: Python Modules", + "License :: OSI Approved :: Apache Software License" ] -dependencies = ["requests", "pyjwt"] [project.urls] -"Homepage" = "https://github.com/postgrid/postgrid-python" -"Bug Tracker" = "https://github.com/postgrid/postgrid-python/issues" +Homepage = "https://github.com/stainless-sdks/postgrid-python" +Repository = "https://github.com/stainless-sdks/postgrid-python" + + + +[tool.rye] +managed = true +# version pins are in requirements-dev.lock +dev-dependencies = [ + "pyright>=1.1.359", + "mypy", + "respx", + "pytest", + "pytest-asyncio", + "ruff", + "time-machine", + "nox", + "dirty-equals>=0.6.0", + "importlib-metadata>=6.7.0", + "rich>=13.7.1", + "nest_asyncio==1.6.0", +] + +[tool.rye.scripts] +format = { chain = [ + "format:ruff", + "format:docs", + "fix:ruff", + # run formatting again to fix any inconsistencies when imports are stripped + "format:ruff", +]} +"format:docs" = "python scripts/utils/ruffen-docs.py README.md api.md" +"format:ruff" = "ruff format" + +"lint" = { chain = [ + "check:ruff", + "typecheck", + "check:importable", +]} +"check:ruff" = "ruff check ." +"fix:ruff" = "ruff check --fix ." + +"check:importable" = "python -c 'import postgrid'" + +typecheck = { chain = [ + "typecheck:pyright", + "typecheck:mypy" +]} +"typecheck:pyright" = "pyright" +"typecheck:verify-types" = "pyright --verifytypes postgrid --ignoreexternal" +"typecheck:mypy" = "mypy ." + +[build-system] +requires = ["hatchling", "hatch-fancy-pypi-readme"] +build-backend = "hatchling.build" + +[tool.hatch.build] +include = [ + "src/*" +] + +[tool.hatch.build.targets.wheel] +packages = ["src/postgrid"] + +[tool.hatch.build.targets.sdist] +# Basically everything except hidden files/directories (such as .github, .devcontainers, .python-version, etc) +include = [ + "/*.toml", + "/*.json", + "/*.lock", + "/*.md", + "/mypy.ini", + "/noxfile.py", + "bin/*", + "examples/*", + "src/*", + "tests/*", +] + +[tool.hatch.metadata.hooks.fancy-pypi-readme] +content-type = "text/markdown" + +[[tool.hatch.metadata.hooks.fancy-pypi-readme.fragments]] +path = "README.md" + +[[tool.hatch.metadata.hooks.fancy-pypi-readme.substitutions]] +# replace relative links with absolute links +pattern = '\[(.+?)\]\(((?!https?://)\S+?)\)' +replacement = '[\1](https://github.com/stainless-sdks/postgrid-python/tree/main/\g<2>)' + +[tool.pytest.ini_options] +testpaths = ["tests"] +addopts = "--tb=short" +xfail_strict = true +asyncio_mode = "auto" +asyncio_default_fixture_loop_scope = "session" +filterwarnings = [ + "error" +] + +[tool.pyright] +# this enables practically every flag given by pyright. +# there are a couple of flags that are still disabled by +# default in strict mode as they are experimental and niche. +typeCheckingMode = "strict" +pythonVersion = "3.8" + +exclude = [ + "_dev", + ".venv", + ".nox", +] + +reportImplicitOverride = true + +reportImportCycles = false +reportPrivateUsage = false + + +[tool.ruff] +line-length = 120 +output-format = "grouped" +target-version = "py37" + +[tool.ruff.format] +docstring-code-format = true + +[tool.ruff.lint] +select = [ + # isort + "I", + # bugbear rules + "B", + # remove unused imports + "F401", + # bare except statements + "E722", + # unused arguments + "ARG", + # print statements + "T201", + "T203", + # misuse of typing.TYPE_CHECKING + "TC004", + # import rules + "TID251", +] +ignore = [ + # mutable defaults + "B006", +] +unfixable = [ + # disable auto fix for print statements + "T201", + "T203", +] + +[tool.ruff.lint.flake8-tidy-imports.banned-api] +"functools.lru_cache".msg = "This function does not retain type information for the wrapped function's arguments; The `lru_cache` function from `_utils` should be used instead" + +[tool.ruff.lint.isort] +length-sort = true +length-sort-straight = true +combine-as-imports = true +extra-standard-library = ["typing_extensions"] +known-first-party = ["postgrid", "tests"] + +[tool.ruff.lint.per-file-ignores] +"bin/**.py" = ["T201", "T203"] +"scripts/**.py" = ["T201", "T203"] +"tests/**.py" = ["T201", "T203"] +"examples/**.py" = ["T201", "T203"] diff --git a/requirements-dev.lock b/requirements-dev.lock new file mode 100644 index 0000000..cca33f7 --- /dev/null +++ b/requirements-dev.lock @@ -0,0 +1,104 @@ +# generated by rye +# use `rye lock` or `rye sync` to update this lockfile +# +# last locked with the following flags: +# pre: false +# features: [] +# all-features: true +# with-sources: false +# generate-hashes: false +# universal: false + +-e file:. +annotated-types==0.6.0 + # via pydantic +anyio==4.4.0 + # via httpx + # via postgrid +argcomplete==3.1.2 + # via nox +certifi==2023.7.22 + # via httpcore + # via httpx +colorlog==6.7.0 + # via nox +dirty-equals==0.6.0 +distlib==0.3.7 + # via virtualenv +distro==1.8.0 + # via postgrid +exceptiongroup==1.2.2 + # via anyio + # via pytest +filelock==3.12.4 + # via virtualenv +h11==0.14.0 + # via httpcore +httpcore==1.0.2 + # via httpx +httpx==0.28.1 + # via postgrid + # via respx +idna==3.4 + # via anyio + # via httpx +importlib-metadata==7.0.0 +iniconfig==2.0.0 + # via pytest +markdown-it-py==3.0.0 + # via rich +mdurl==0.1.2 + # via markdown-it-py +mypy==1.14.1 +mypy-extensions==1.0.0 + # via mypy +nest-asyncio==1.6.0 +nodeenv==1.8.0 + # via pyright +nox==2023.4.22 +packaging==23.2 + # via nox + # via pytest +platformdirs==3.11.0 + # via virtualenv +pluggy==1.5.0 + # via pytest +pydantic==2.10.3 + # via postgrid +pydantic-core==2.27.1 + # via pydantic +pygments==2.18.0 + # via rich +pyright==1.1.392.post0 +pytest==8.3.3 + # via pytest-asyncio +pytest-asyncio==0.24.0 +python-dateutil==2.8.2 + # via time-machine +pytz==2023.3.post1 + # via dirty-equals +respx==0.22.0 +rich==13.7.1 +ruff==0.9.4 +setuptools==68.2.2 + # via nodeenv +six==1.16.0 + # via python-dateutil +sniffio==1.3.0 + # via anyio + # via postgrid +time-machine==2.9.0 +tomli==2.0.2 + # via mypy + # via pytest +typing-extensions==4.12.2 + # via anyio + # via mypy + # via postgrid + # via pydantic + # via pydantic-core + # via pyright +virtualenv==20.24.5 + # via nox +zipp==3.17.0 + # via importlib-metadata diff --git a/requirements.lock b/requirements.lock new file mode 100644 index 0000000..e38ecbf --- /dev/null +++ b/requirements.lock @@ -0,0 +1,45 @@ +# generated by rye +# use `rye lock` or `rye sync` to update this lockfile +# +# last locked with the following flags: +# pre: false +# features: [] +# all-features: true +# with-sources: false +# generate-hashes: false +# universal: false + +-e file:. +annotated-types==0.6.0 + # via pydantic +anyio==4.4.0 + # via httpx + # via postgrid +certifi==2023.7.22 + # via httpcore + # via httpx +distro==1.8.0 + # via postgrid +exceptiongroup==1.2.2 + # via anyio +h11==0.14.0 + # via httpcore +httpcore==1.0.2 + # via httpx +httpx==0.28.1 + # via postgrid +idna==3.4 + # via anyio + # via httpx +pydantic==2.10.3 + # via postgrid +pydantic-core==2.27.1 + # via pydantic +sniffio==1.3.0 + # via anyio + # via postgrid +typing-extensions==4.12.2 + # via anyio + # via postgrid + # via pydantic + # via pydantic-core diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 6b9ccea..0000000 --- a/requirements.txt +++ /dev/null @@ -1,45 +0,0 @@ -attrs==21.4.0 -black==23.3.0 -bleach==4.1.0 -build==0.8.0 -certifi==2021.10.8 -cffi==1.15.0 -charset-normalizer==2.0.12 -click==8.1.3 -colorama==0.4.4 -coverage==6.3.2 -cryptography==36.0.2 -docutils==0.18.1 -idna==3.3 -importlib-metadata==4.11.3 -iniconfig==1.1.1 -jeepney==0.7.1 -keyring==23.5.0 -mypy-extensions==1.0.0 -packaging==23.1 -pathspec==0.11.1 -pep517==0.13.0 -pkginfo==1.8.2 -platformdirs==3.5.1 -pluggy==1.0.0 -py==1.11.0 -pycodestyle==2.8.0 -pycparser==2.21 -Pygments==2.11.2 -PyJWT==2.3.0 -pyparsing==3.0.7 -pytest==7.0.1 -readme-renderer==34.0 -requests==2.27.1 -requests-toolbelt==0.9.1 -rfc3986==2.0.0 -SecretStorage==3.3.1 -six==1.16.0 -toml==0.10.2 -tomli==2.0.1 -tqdm==4.63.1 -twine==3.8.0 -typing-extensions==4.5.0 -urllib3==1.26.8 -webencodings==0.5.1 -zipp==3.7.0 diff --git a/scripts/bootstrap b/scripts/bootstrap new file mode 100755 index 0000000..e84fe62 --- /dev/null +++ b/scripts/bootstrap @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +set -e + +cd "$(dirname "$0")/.." + +if ! command -v rye >/dev/null 2>&1 && [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ]; then + brew bundle check >/dev/null 2>&1 || { + echo "==> Installing Homebrew dependencies…" + brew bundle + } +fi + +echo "==> Installing Python dependencies…" + +# experimental uv support makes installations significantly faster +rye config --set-bool behavior.use-uv=true + +rye sync --all-features diff --git a/scripts/format b/scripts/format new file mode 100755 index 0000000..667ec2d --- /dev/null +++ b/scripts/format @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +set -e + +cd "$(dirname "$0")/.." + +echo "==> Running formatters" +rye run format diff --git a/scripts/lint b/scripts/lint new file mode 100755 index 0000000..0512393 --- /dev/null +++ b/scripts/lint @@ -0,0 +1,11 @@ +#!/usr/bin/env bash + +set -e + +cd "$(dirname "$0")/.." + +echo "==> Running lints" +rye run lint + +echo "==> Making sure it imports" +rye run python -c 'import postgrid' diff --git a/scripts/mock b/scripts/mock new file mode 100755 index 0000000..d2814ae --- /dev/null +++ b/scripts/mock @@ -0,0 +1,41 @@ +#!/usr/bin/env bash + +set -e + +cd "$(dirname "$0")/.." + +if [[ -n "$1" && "$1" != '--'* ]]; then + URL="$1" + shift +else + URL="$(grep 'openapi_spec_url' .stats.yml | cut -d' ' -f2)" +fi + +# Check if the URL is empty +if [ -z "$URL" ]; then + echo "Error: No OpenAPI spec path/url provided or found in .stats.yml" + exit 1 +fi + +echo "==> Starting mock server with URL ${URL}" + +# Run prism mock on the given spec +if [ "$1" == "--daemon" ]; then + npm exec --package=@stainless-api/prism-cli@5.8.5 -- prism mock "$URL" &> .prism.log & + + # Wait for server to come online + echo -n "Waiting for server" + while ! grep -q "✖ fatal\|Prism is listening" ".prism.log" ; do + echo -n "." + sleep 0.1 + done + + if grep -q "✖ fatal" ".prism.log"; then + cat .prism.log + exit 1 + fi + + echo +else + npm exec --package=@stainless-api/prism-cli@5.8.5 -- prism mock "$URL" +fi diff --git a/scripts/test b/scripts/test new file mode 100755 index 0000000..4fa5698 --- /dev/null +++ b/scripts/test @@ -0,0 +1,59 @@ +#!/usr/bin/env bash + +set -e + +cd "$(dirname "$0")/.." + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[0;33m' +NC='\033[0m' # No Color + +function prism_is_running() { + curl --silent "http://localhost:4010" >/dev/null 2>&1 +} + +kill_server_on_port() { + pids=$(lsof -t -i tcp:"$1" || echo "") + if [ "$pids" != "" ]; then + kill "$pids" + echo "Stopped $pids." + fi +} + +function is_overriding_api_base_url() { + [ -n "$TEST_API_BASE_URL" ] +} + +if ! is_overriding_api_base_url && ! prism_is_running ; then + # When we exit this script, make sure to kill the background mock server process + trap 'kill_server_on_port 4010' EXIT + + # Start the dev server + ./scripts/mock --daemon +fi + +if is_overriding_api_base_url ; then + echo -e "${GREEN}✔ Running tests against ${TEST_API_BASE_URL}${NC}" + echo +elif ! prism_is_running ; then + echo -e "${RED}ERROR:${NC} The test suite will not run without a mock Prism server" + echo -e "running against your OpenAPI spec." + echo + echo -e "To run the server, pass in the path or url of your OpenAPI" + echo -e "spec to the prism command:" + echo + echo -e " \$ ${YELLOW}npm exec --package=@stoplight/prism-cli@~5.3.2 -- prism mock path/to/your.openapi.yml${NC}" + echo + + exit 1 +else + echo -e "${GREEN}✔ Mock prism server is running with your OpenAPI spec${NC}" + echo +fi + +echo "==> Running tests" +rye run pytest "$@" + +echo "==> Running Pydantic v1 tests" +rye run nox -s test-pydantic-v1 -- "$@" diff --git a/scripts/utils/ruffen-docs.py b/scripts/utils/ruffen-docs.py new file mode 100644 index 0000000..0cf2bd2 --- /dev/null +++ b/scripts/utils/ruffen-docs.py @@ -0,0 +1,167 @@ +# fork of https://github.com/asottile/blacken-docs adapted for ruff +from __future__ import annotations + +import re +import sys +import argparse +import textwrap +import contextlib +import subprocess +from typing import Match, Optional, Sequence, Generator, NamedTuple, cast + +MD_RE = re.compile( + r"(?P^(?P *)```\s*python\n)" r"(?P.*?)" r"(?P^(?P=indent)```\s*$)", + re.DOTALL | re.MULTILINE, +) +MD_PYCON_RE = re.compile( + r"(?P^(?P *)```\s*pycon\n)" r"(?P.*?)" r"(?P^(?P=indent)```.*$)", + re.DOTALL | re.MULTILINE, +) +PYCON_PREFIX = ">>> " +PYCON_CONTINUATION_PREFIX = "..." +PYCON_CONTINUATION_RE = re.compile( + rf"^{re.escape(PYCON_CONTINUATION_PREFIX)}( |$)", +) +DEFAULT_LINE_LENGTH = 100 + + +class CodeBlockError(NamedTuple): + offset: int + exc: Exception + + +def format_str( + src: str, +) -> tuple[str, Sequence[CodeBlockError]]: + errors: list[CodeBlockError] = [] + + @contextlib.contextmanager + def _collect_error(match: Match[str]) -> Generator[None, None, None]: + try: + yield + except Exception as e: + errors.append(CodeBlockError(match.start(), e)) + + def _md_match(match: Match[str]) -> str: + code = textwrap.dedent(match["code"]) + with _collect_error(match): + code = format_code_block(code) + code = textwrap.indent(code, match["indent"]) + return f"{match['before']}{code}{match['after']}" + + def _pycon_match(match: Match[str]) -> str: + code = "" + fragment = cast(Optional[str], None) + + def finish_fragment() -> None: + nonlocal code + nonlocal fragment + + if fragment is not None: + with _collect_error(match): + fragment = format_code_block(fragment) + fragment_lines = fragment.splitlines() + code += f"{PYCON_PREFIX}{fragment_lines[0]}\n" + for line in fragment_lines[1:]: + # Skip blank lines to handle Black adding a blank above + # functions within blocks. A blank line would end the REPL + # continuation prompt. + # + # >>> if True: + # ... def f(): + # ... pass + # ... + if line: + code += f"{PYCON_CONTINUATION_PREFIX} {line}\n" + if fragment_lines[-1].startswith(" "): + code += f"{PYCON_CONTINUATION_PREFIX}\n" + fragment = None + + indentation = None + for line in match["code"].splitlines(): + orig_line, line = line, line.lstrip() + if indentation is None and line: + indentation = len(orig_line) - len(line) + continuation_match = PYCON_CONTINUATION_RE.match(line) + if continuation_match and fragment is not None: + fragment += line[continuation_match.end() :] + "\n" + else: + finish_fragment() + if line.startswith(PYCON_PREFIX): + fragment = line[len(PYCON_PREFIX) :] + "\n" + else: + code += orig_line[indentation:] + "\n" + finish_fragment() + return code + + def _md_pycon_match(match: Match[str]) -> str: + code = _pycon_match(match) + code = textwrap.indent(code, match["indent"]) + return f"{match['before']}{code}{match['after']}" + + src = MD_RE.sub(_md_match, src) + src = MD_PYCON_RE.sub(_md_pycon_match, src) + return src, errors + + +def format_code_block(code: str) -> str: + return subprocess.check_output( + [ + sys.executable, + "-m", + "ruff", + "format", + "--stdin-filename=script.py", + f"--line-length={DEFAULT_LINE_LENGTH}", + ], + encoding="utf-8", + input=code, + ) + + +def format_file( + filename: str, + skip_errors: bool, +) -> int: + with open(filename, encoding="UTF-8") as f: + contents = f.read() + new_contents, errors = format_str(contents) + for error in errors: + lineno = contents[: error.offset].count("\n") + 1 + print(f"{filename}:{lineno}: code block parse error {error.exc}") + if errors and not skip_errors: + return 1 + if contents != new_contents: + print(f"{filename}: Rewriting...") + with open(filename, "w", encoding="UTF-8") as f: + f.write(new_contents) + return 0 + else: + return 0 + + +def main(argv: Sequence[str] | None = None) -> int: + parser = argparse.ArgumentParser() + parser.add_argument( + "-l", + "--line-length", + type=int, + default=DEFAULT_LINE_LENGTH, + ) + parser.add_argument( + "-S", + "--skip-string-normalization", + action="store_true", + ) + parser.add_argument("-E", "--skip-errors", action="store_true") + parser.add_argument("filenames", nargs="*") + args = parser.parse_args(argv) + + retv = 0 + for filename in args.filenames: + retv |= format_file(filename, skip_errors=args.skip_errors) + return retv + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/src/postgrid/__init__.py b/src/postgrid/__init__.py new file mode 100644 index 0000000..57e6a40 --- /dev/null +++ b/src/postgrid/__init__.py @@ -0,0 +1,94 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from . import types +from ._types import NOT_GIVEN, Omit, NoneType, NotGiven, Transport, ProxiesTypes +from ._utils import file_from_path +from ._client import ( + Client, + Stream, + Timeout, + PostGrid, + Transport, + AsyncClient, + AsyncStream, + AsyncPostGrid, + RequestOptions, +) +from ._models import BaseModel +from ._version import __title__, __version__ +from ._response import APIResponse as APIResponse, AsyncAPIResponse as AsyncAPIResponse +from ._constants import DEFAULT_TIMEOUT, DEFAULT_MAX_RETRIES, DEFAULT_CONNECTION_LIMITS +from ._exceptions import ( + APIError, + ConflictError, + NotFoundError, + PostGridError, + APIStatusError, + RateLimitError, + APITimeoutError, + BadRequestError, + APIConnectionError, + AuthenticationError, + InternalServerError, + PermissionDeniedError, + UnprocessableEntityError, + APIResponseValidationError, +) +from ._base_client import DefaultHttpxClient, DefaultAsyncHttpxClient +from ._utils._logs import setup_logging as _setup_logging + +__all__ = [ + "types", + "__version__", + "__title__", + "NoneType", + "Transport", + "ProxiesTypes", + "NotGiven", + "NOT_GIVEN", + "Omit", + "PostGridError", + "APIError", + "APIStatusError", + "APITimeoutError", + "APIConnectionError", + "APIResponseValidationError", + "BadRequestError", + "AuthenticationError", + "PermissionDeniedError", + "NotFoundError", + "ConflictError", + "UnprocessableEntityError", + "RateLimitError", + "InternalServerError", + "Timeout", + "RequestOptions", + "Client", + "AsyncClient", + "Stream", + "AsyncStream", + "PostGrid", + "AsyncPostGrid", + "file_from_path", + "BaseModel", + "DEFAULT_TIMEOUT", + "DEFAULT_MAX_RETRIES", + "DEFAULT_CONNECTION_LIMITS", + "DefaultHttpxClient", + "DefaultAsyncHttpxClient", +] + +_setup_logging() + +# Update the __module__ attribute for exported symbols so that +# error messages point to this module instead of the module +# it was originally defined in, e.g. +# postgrid._exceptions.NotFoundError -> postgrid.NotFoundError +__locals = locals() +for __name in __all__: + if not __name.startswith("__"): + try: + __locals[__name].__module__ = "postgrid" + except (TypeError, AttributeError): + # Some of our exported symbols are builtins which we can't set attributes for. + pass diff --git a/src/postgrid/_base_client.py b/src/postgrid/_base_client.py new file mode 100644 index 0000000..64fa1d5 --- /dev/null +++ b/src/postgrid/_base_client.py @@ -0,0 +1,2058 @@ +from __future__ import annotations + +import sys +import json +import time +import uuid +import email +import asyncio +import inspect +import logging +import platform +import warnings +import email.utils +from types import TracebackType +from random import random +from typing import ( + TYPE_CHECKING, + Any, + Dict, + Type, + Union, + Generic, + Mapping, + TypeVar, + Iterable, + Iterator, + Optional, + Generator, + AsyncIterator, + cast, + overload, +) +from typing_extensions import Literal, override, get_origin + +import anyio +import httpx +import distro +import pydantic +from httpx import URL, Limits +from pydantic import PrivateAttr + +from . import _exceptions +from ._qs import Querystring +from ._files import to_httpx_files, async_to_httpx_files +from ._types import ( + NOT_GIVEN, + Body, + Omit, + Query, + Headers, + Timeout, + NotGiven, + ResponseT, + Transport, + AnyMapping, + PostParser, + ProxiesTypes, + RequestFiles, + HttpxSendArgs, + AsyncTransport, + RequestOptions, + HttpxRequestFiles, + ModelBuilderProtocol, +) +from ._utils import is_dict, is_list, asyncify, is_given, lru_cache, is_mapping +from ._compat import model_copy, model_dump +from ._models import GenericModel, FinalRequestOptions, validate_type, construct_type +from ._response import ( + APIResponse, + BaseAPIResponse, + AsyncAPIResponse, + extract_response_type, +) +from ._constants import ( + DEFAULT_TIMEOUT, + MAX_RETRY_DELAY, + DEFAULT_MAX_RETRIES, + INITIAL_RETRY_DELAY, + RAW_RESPONSE_HEADER, + OVERRIDE_CAST_TO_HEADER, + DEFAULT_CONNECTION_LIMITS, +) +from ._streaming import Stream, SSEDecoder, AsyncStream, SSEBytesDecoder +from ._exceptions import ( + APIStatusError, + APITimeoutError, + APIConnectionError, + APIResponseValidationError, +) + +log: logging.Logger = logging.getLogger(__name__) + +# TODO: make base page type vars covariant +SyncPageT = TypeVar("SyncPageT", bound="BaseSyncPage[Any]") +AsyncPageT = TypeVar("AsyncPageT", bound="BaseAsyncPage[Any]") + + +_T = TypeVar("_T") +_T_co = TypeVar("_T_co", covariant=True) + +_StreamT = TypeVar("_StreamT", bound=Stream[Any]) +_AsyncStreamT = TypeVar("_AsyncStreamT", bound=AsyncStream[Any]) + +if TYPE_CHECKING: + from httpx._config import DEFAULT_TIMEOUT_CONFIG as HTTPX_DEFAULT_TIMEOUT +else: + try: + from httpx._config import DEFAULT_TIMEOUT_CONFIG as HTTPX_DEFAULT_TIMEOUT + except ImportError: + # taken from https://github.com/encode/httpx/blob/3ba5fe0d7ac70222590e759c31442b1cab263791/httpx/_config.py#L366 + HTTPX_DEFAULT_TIMEOUT = Timeout(5.0) + + +class PageInfo: + """Stores the necessary information to build the request to retrieve the next page. + + Either `url` or `params` must be set. + """ + + url: URL | NotGiven + params: Query | NotGiven + + @overload + def __init__( + self, + *, + url: URL, + ) -> None: ... + + @overload + def __init__( + self, + *, + params: Query, + ) -> None: ... + + def __init__( + self, + *, + url: URL | NotGiven = NOT_GIVEN, + params: Query | NotGiven = NOT_GIVEN, + ) -> None: + self.url = url + self.params = params + + @override + def __repr__(self) -> str: + if self.url: + return f"{self.__class__.__name__}(url={self.url})" + return f"{self.__class__.__name__}(params={self.params})" + + +class BasePage(GenericModel, Generic[_T]): + """ + Defines the core interface for pagination. + + Type Args: + ModelT: The pydantic model that represents an item in the response. + + Methods: + has_next_page(): Check if there is another page available + next_page_info(): Get the necessary information to make a request for the next page + """ + + _options: FinalRequestOptions = PrivateAttr() + _model: Type[_T] = PrivateAttr() + + def has_next_page(self) -> bool: + items = self._get_page_items() + if not items: + return False + return self.next_page_info() is not None + + def next_page_info(self) -> Optional[PageInfo]: ... + + def _get_page_items(self) -> Iterable[_T]: # type: ignore[empty-body] + ... + + def _params_from_url(self, url: URL) -> httpx.QueryParams: + # TODO: do we have to preprocess params here? + return httpx.QueryParams(cast(Any, self._options.params)).merge(url.params) + + def _info_to_options(self, info: PageInfo) -> FinalRequestOptions: + options = model_copy(self._options) + options._strip_raw_response_header() + + if not isinstance(info.params, NotGiven): + options.params = {**options.params, **info.params} + return options + + if not isinstance(info.url, NotGiven): + params = self._params_from_url(info.url) + url = info.url.copy_with(params=params) + options.params = dict(url.params) + options.url = str(url) + return options + + raise ValueError("Unexpected PageInfo state") + + +class BaseSyncPage(BasePage[_T], Generic[_T]): + _client: SyncAPIClient = pydantic.PrivateAttr() + + def _set_private_attributes( + self, + client: SyncAPIClient, + model: Type[_T], + options: FinalRequestOptions, + ) -> None: + self._model = model + self._client = client + self._options = options + + # Pydantic uses a custom `__iter__` method to support casting BaseModels + # to dictionaries. e.g. dict(model). + # As we want to support `for item in page`, this is inherently incompatible + # with the default pydantic behaviour. It is not possible to support both + # use cases at once. Fortunately, this is not a big deal as all other pydantic + # methods should continue to work as expected as there is an alternative method + # to cast a model to a dictionary, model.dict(), which is used internally + # by pydantic. + def __iter__(self) -> Iterator[_T]: # type: ignore + for page in self.iter_pages(): + for item in page._get_page_items(): + yield item + + def iter_pages(self: SyncPageT) -> Iterator[SyncPageT]: + page = self + while True: + yield page + if page.has_next_page(): + page = page.get_next_page() + else: + return + + def get_next_page(self: SyncPageT) -> SyncPageT: + info = self.next_page_info() + if not info: + raise RuntimeError( + "No next page expected; please check `.has_next_page()` before calling `.get_next_page()`." + ) + + options = self._info_to_options(info) + return self._client._request_api_list(self._model, page=self.__class__, options=options) + + +class AsyncPaginator(Generic[_T, AsyncPageT]): + def __init__( + self, + client: AsyncAPIClient, + options: FinalRequestOptions, + page_cls: Type[AsyncPageT], + model: Type[_T], + ) -> None: + self._model = model + self._client = client + self._options = options + self._page_cls = page_cls + + def __await__(self) -> Generator[Any, None, AsyncPageT]: + return self._get_page().__await__() + + async def _get_page(self) -> AsyncPageT: + def _parser(resp: AsyncPageT) -> AsyncPageT: + resp._set_private_attributes( + model=self._model, + options=self._options, + client=self._client, + ) + return resp + + self._options.post_parser = _parser + + return await self._client.request(self._page_cls, self._options) + + async def __aiter__(self) -> AsyncIterator[_T]: + # https://github.com/microsoft/pyright/issues/3464 + page = cast( + AsyncPageT, + await self, # type: ignore + ) + async for item in page: + yield item + + +class BaseAsyncPage(BasePage[_T], Generic[_T]): + _client: AsyncAPIClient = pydantic.PrivateAttr() + + def _set_private_attributes( + self, + model: Type[_T], + client: AsyncAPIClient, + options: FinalRequestOptions, + ) -> None: + self._model = model + self._client = client + self._options = options + + async def __aiter__(self) -> AsyncIterator[_T]: + async for page in self.iter_pages(): + for item in page._get_page_items(): + yield item + + async def iter_pages(self: AsyncPageT) -> AsyncIterator[AsyncPageT]: + page = self + while True: + yield page + if page.has_next_page(): + page = await page.get_next_page() + else: + return + + async def get_next_page(self: AsyncPageT) -> AsyncPageT: + info = self.next_page_info() + if not info: + raise RuntimeError( + "No next page expected; please check `.has_next_page()` before calling `.get_next_page()`." + ) + + options = self._info_to_options(info) + return await self._client._request_api_list(self._model, page=self.__class__, options=options) + + +_HttpxClientT = TypeVar("_HttpxClientT", bound=Union[httpx.Client, httpx.AsyncClient]) +_DefaultStreamT = TypeVar("_DefaultStreamT", bound=Union[Stream[Any], AsyncStream[Any]]) + + +class BaseClient(Generic[_HttpxClientT, _DefaultStreamT]): + _client: _HttpxClientT + _version: str + _base_url: URL + max_retries: int + timeout: Union[float, Timeout, None] + _limits: httpx.Limits + _proxies: ProxiesTypes | None + _transport: Transport | AsyncTransport | None + _strict_response_validation: bool + _idempotency_header: str | None + _default_stream_cls: type[_DefaultStreamT] | None = None + + def __init__( + self, + *, + version: str, + base_url: str | URL, + _strict_response_validation: bool, + max_retries: int = DEFAULT_MAX_RETRIES, + timeout: float | Timeout | None = DEFAULT_TIMEOUT, + limits: httpx.Limits, + transport: Transport | AsyncTransport | None, + proxies: ProxiesTypes | None, + custom_headers: Mapping[str, str] | None = None, + custom_query: Mapping[str, object] | None = None, + ) -> None: + self._version = version + self._base_url = self._enforce_trailing_slash(URL(base_url)) + self.max_retries = max_retries + self.timeout = timeout + self._limits = limits + self._proxies = proxies + self._transport = transport + self._custom_headers = custom_headers or {} + self._custom_query = custom_query or {} + self._strict_response_validation = _strict_response_validation + self._idempotency_header = None + self._platform: Platform | None = None + + if max_retries is None: # pyright: ignore[reportUnnecessaryComparison] + raise TypeError( + "max_retries cannot be None. If you want to disable retries, pass `0`; if you want unlimited retries, pass `math.inf` or a very high number; if you want the default behavior, pass `postgrid.DEFAULT_MAX_RETRIES`" + ) + + def _enforce_trailing_slash(self, url: URL) -> URL: + if url.raw_path.endswith(b"/"): + return url + return url.copy_with(raw_path=url.raw_path + b"/") + + def _make_status_error_from_response( + self, + response: httpx.Response, + ) -> APIStatusError: + if response.is_closed and not response.is_stream_consumed: + # We can't read the response body as it has been closed + # before it was read. This can happen if an event hook + # raises a status error. + body = None + err_msg = f"Error code: {response.status_code}" + else: + err_text = response.text.strip() + body = err_text + + try: + body = json.loads(err_text) + err_msg = f"Error code: {response.status_code} - {body}" + except Exception: + err_msg = err_text or f"Error code: {response.status_code}" + + return self._make_status_error(err_msg, body=body, response=response) + + def _make_status_error( + self, + err_msg: str, + *, + body: object, + response: httpx.Response, + ) -> _exceptions.APIStatusError: + raise NotImplementedError() + + def _build_headers(self, options: FinalRequestOptions, *, retries_taken: int = 0) -> httpx.Headers: + custom_headers = options.headers or {} + headers_dict = _merge_mappings(self.default_headers, custom_headers) + self._validate_headers(headers_dict, custom_headers) + + # headers are case-insensitive while dictionaries are not. + headers = httpx.Headers(headers_dict) + + idempotency_header = self._idempotency_header + if idempotency_header and options.method.lower() != "get" and idempotency_header not in headers: + headers[idempotency_header] = options.idempotency_key or self._idempotency_key() + + # Don't set these headers if they were already set or removed by the caller. We check + # `custom_headers`, which can contain `Omit()`, instead of `headers` to account for the removal case. + lower_custom_headers = [header.lower() for header in custom_headers] + if "x-stainless-retry-count" not in lower_custom_headers: + headers["x-stainless-retry-count"] = str(retries_taken) + if "x-stainless-read-timeout" not in lower_custom_headers: + timeout = self.timeout if isinstance(options.timeout, NotGiven) else options.timeout + if isinstance(timeout, Timeout): + timeout = timeout.read + if timeout is not None: + headers["x-stainless-read-timeout"] = str(timeout) + + return headers + + def _prepare_url(self, url: str) -> URL: + """ + Merge a URL argument together with any 'base_url' on the client, + to create the URL used for the outgoing request. + """ + # Copied from httpx's `_merge_url` method. + merge_url = URL(url) + if merge_url.is_relative_url: + merge_raw_path = self.base_url.raw_path + merge_url.raw_path.lstrip(b"/") + return self.base_url.copy_with(raw_path=merge_raw_path) + + return merge_url + + def _make_sse_decoder(self) -> SSEDecoder | SSEBytesDecoder: + return SSEDecoder() + + def _build_request( + self, + options: FinalRequestOptions, + *, + retries_taken: int = 0, + ) -> httpx.Request: + if log.isEnabledFor(logging.DEBUG): + log.debug("Request options: %s", model_dump(options, exclude_unset=True)) + + kwargs: dict[str, Any] = {} + + json_data = options.json_data + if options.extra_json is not None: + if json_data is None: + json_data = cast(Body, options.extra_json) + elif is_mapping(json_data): + json_data = _merge_mappings(json_data, options.extra_json) + else: + raise RuntimeError(f"Unexpected JSON data type, {type(json_data)}, cannot merge with `extra_body`") + + headers = self._build_headers(options, retries_taken=retries_taken) + params = _merge_mappings(self.default_query, options.params) + content_type = headers.get("Content-Type") + files = options.files + + # If the given Content-Type header is multipart/form-data then it + # has to be removed so that httpx can generate the header with + # additional information for us as it has to be in this form + # for the server to be able to correctly parse the request: + # multipart/form-data; boundary=---abc-- + if content_type is not None and content_type.startswith("multipart/form-data"): + if "boundary" not in content_type: + # only remove the header if the boundary hasn't been explicitly set + # as the caller doesn't want httpx to come up with their own boundary + headers.pop("Content-Type") + + # As we are now sending multipart/form-data instead of application/json + # we need to tell httpx to use it, https://www.python-httpx.org/advanced/clients/#multipart-file-encoding + if json_data: + if not is_dict(json_data): + raise TypeError( + f"Expected query input to be a dictionary for multipart requests but got {type(json_data)} instead." + ) + kwargs["data"] = self._serialize_multipartform(json_data) + + # httpx determines whether or not to send a "multipart/form-data" + # request based on the truthiness of the "files" argument. + # This gets around that issue by generating a dict value that + # evaluates to true. + # + # https://github.com/encode/httpx/discussions/2399#discussioncomment-3814186 + if not files: + files = cast(HttpxRequestFiles, ForceMultipartDict()) + + prepared_url = self._prepare_url(options.url) + if "_" in prepared_url.host: + # work around https://github.com/encode/httpx/discussions/2880 + kwargs["extensions"] = {"sni_hostname": prepared_url.host.replace("_", "-")} + + # TODO: report this error to httpx + return self._client.build_request( # pyright: ignore[reportUnknownMemberType] + headers=headers, + timeout=self.timeout if isinstance(options.timeout, NotGiven) else options.timeout, + method=options.method, + url=prepared_url, + # the `Query` type that we use is incompatible with qs' + # `Params` type as it needs to be typed as `Mapping[str, object]` + # so that passing a `TypedDict` doesn't cause an error. + # https://github.com/microsoft/pyright/issues/3526#event-6715453066 + params=self.qs.stringify(cast(Mapping[str, Any], params)) if params else None, + json=json_data if is_given(json_data) else None, + files=files, + **kwargs, + ) + + def _serialize_multipartform(self, data: Mapping[object, object]) -> dict[str, object]: + items = self.qs.stringify_items( + # TODO: type ignore is required as stringify_items is well typed but we can't be + # well typed without heavy validation. + data, # type: ignore + array_format="brackets", + ) + serialized: dict[str, object] = {} + for key, value in items: + existing = serialized.get(key) + + if not existing: + serialized[key] = value + continue + + # If a value has already been set for this key then that + # means we're sending data like `array[]=[1, 2, 3]` and we + # need to tell httpx that we want to send multiple values with + # the same key which is done by using a list or a tuple. + # + # Note: 2d arrays should never result in the same key at both + # levels so it's safe to assume that if the value is a list, + # it was because we changed it to be a list. + if is_list(existing): + existing.append(value) + else: + serialized[key] = [existing, value] + + return serialized + + def _maybe_override_cast_to(self, cast_to: type[ResponseT], options: FinalRequestOptions) -> type[ResponseT]: + if not is_given(options.headers): + return cast_to + + # make a copy of the headers so we don't mutate user-input + headers = dict(options.headers) + + # we internally support defining a temporary header to override the + # default `cast_to` type for use with `.with_raw_response` and `.with_streaming_response` + # see _response.py for implementation details + override_cast_to = headers.pop(OVERRIDE_CAST_TO_HEADER, NOT_GIVEN) + if is_given(override_cast_to): + options.headers = headers + return cast(Type[ResponseT], override_cast_to) + + return cast_to + + def _should_stream_response_body(self, request: httpx.Request) -> bool: + return request.headers.get(RAW_RESPONSE_HEADER) == "stream" # type: ignore[no-any-return] + + def _process_response_data( + self, + *, + data: object, + cast_to: type[ResponseT], + response: httpx.Response, + ) -> ResponseT: + if data is None: + return cast(ResponseT, None) + + if cast_to is object: + return cast(ResponseT, data) + + try: + if inspect.isclass(cast_to) and issubclass(cast_to, ModelBuilderProtocol): + return cast(ResponseT, cast_to.build(response=response, data=data)) + + if self._strict_response_validation: + return cast(ResponseT, validate_type(type_=cast_to, value=data)) + + return cast(ResponseT, construct_type(type_=cast_to, value=data)) + except pydantic.ValidationError as err: + raise APIResponseValidationError(response=response, body=data) from err + + @property + def qs(self) -> Querystring: + return Querystring() + + @property + def custom_auth(self) -> httpx.Auth | None: + return None + + @property + def auth_headers(self) -> dict[str, str]: + return {} + + @property + def default_headers(self) -> dict[str, str | Omit]: + return { + "Accept": "application/json", + "Content-Type": "application/json", + "User-Agent": self.user_agent, + **self.platform_headers(), + **self.auth_headers, + **self._custom_headers, + } + + @property + def default_query(self) -> dict[str, object]: + return { + **self._custom_query, + } + + def _validate_headers( + self, + headers: Headers, # noqa: ARG002 + custom_headers: Headers, # noqa: ARG002 + ) -> None: + """Validate the given default headers and custom headers. + + Does nothing by default. + """ + return + + @property + def user_agent(self) -> str: + return f"{self.__class__.__name__}/Python {self._version}" + + @property + def base_url(self) -> URL: + return self._base_url + + @base_url.setter + def base_url(self, url: URL | str) -> None: + self._base_url = self._enforce_trailing_slash(url if isinstance(url, URL) else URL(url)) + + def platform_headers(self) -> Dict[str, str]: + # the actual implementation is in a separate `lru_cache` decorated + # function because adding `lru_cache` to methods will leak memory + # https://github.com/python/cpython/issues/88476 + return platform_headers(self._version, platform=self._platform) + + def _parse_retry_after_header(self, response_headers: Optional[httpx.Headers] = None) -> float | None: + """Returns a float of the number of seconds (not milliseconds) to wait after retrying, or None if unspecified. + + About the Retry-After header: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After + See also https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After#syntax + """ + if response_headers is None: + return None + + # First, try the non-standard `retry-after-ms` header for milliseconds, + # which is more precise than integer-seconds `retry-after` + try: + retry_ms_header = response_headers.get("retry-after-ms", None) + return float(retry_ms_header) / 1000 + except (TypeError, ValueError): + pass + + # Next, try parsing `retry-after` header as seconds (allowing nonstandard floats). + retry_header = response_headers.get("retry-after") + try: + # note: the spec indicates that this should only ever be an integer + # but if someone sends a float there's no reason for us to not respect it + return float(retry_header) + except (TypeError, ValueError): + pass + + # Last, try parsing `retry-after` as a date. + retry_date_tuple = email.utils.parsedate_tz(retry_header) + if retry_date_tuple is None: + return None + + retry_date = email.utils.mktime_tz(retry_date_tuple) + return float(retry_date - time.time()) + + def _calculate_retry_timeout( + self, + remaining_retries: int, + options: FinalRequestOptions, + response_headers: Optional[httpx.Headers] = None, + ) -> float: + max_retries = options.get_max_retries(self.max_retries) + + # If the API asks us to wait a certain amount of time (and it's a reasonable amount), just do what it says. + retry_after = self._parse_retry_after_header(response_headers) + if retry_after is not None and 0 < retry_after <= 60: + return retry_after + + # Also cap retry count to 1000 to avoid any potential overflows with `pow` + nb_retries = min(max_retries - remaining_retries, 1000) + + # Apply exponential backoff, but not more than the max. + sleep_seconds = min(INITIAL_RETRY_DELAY * pow(2.0, nb_retries), MAX_RETRY_DELAY) + + # Apply some jitter, plus-or-minus half a second. + jitter = 1 - 0.25 * random() + timeout = sleep_seconds * jitter + return timeout if timeout >= 0 else 0 + + def _should_retry(self, response: httpx.Response) -> bool: + # Note: this is not a standard header + should_retry_header = response.headers.get("x-should-retry") + + # If the server explicitly says whether or not to retry, obey. + if should_retry_header == "true": + log.debug("Retrying as header `x-should-retry` is set to `true`") + return True + if should_retry_header == "false": + log.debug("Not retrying as header `x-should-retry` is set to `false`") + return False + + # Retry on request timeouts. + if response.status_code == 408: + log.debug("Retrying due to status code %i", response.status_code) + return True + + # Retry on lock timeouts. + if response.status_code == 409: + log.debug("Retrying due to status code %i", response.status_code) + return True + + # Retry on rate limits. + if response.status_code == 429: + log.debug("Retrying due to status code %i", response.status_code) + return True + + # Retry internal errors. + if response.status_code >= 500: + log.debug("Retrying due to status code %i", response.status_code) + return True + + log.debug("Not retrying") + return False + + def _idempotency_key(self) -> str: + return f"stainless-python-retry-{uuid.uuid4()}" + + +class _DefaultHttpxClient(httpx.Client): + def __init__(self, **kwargs: Any) -> None: + kwargs.setdefault("timeout", DEFAULT_TIMEOUT) + kwargs.setdefault("limits", DEFAULT_CONNECTION_LIMITS) + kwargs.setdefault("follow_redirects", True) + super().__init__(**kwargs) + + +if TYPE_CHECKING: + DefaultHttpxClient = httpx.Client + """An alias to `httpx.Client` that provides the same defaults that this SDK + uses internally. + + This is useful because overriding the `http_client` with your own instance of + `httpx.Client` will result in httpx's defaults being used, not ours. + """ +else: + DefaultHttpxClient = _DefaultHttpxClient + + +class SyncHttpxClientWrapper(DefaultHttpxClient): + def __del__(self) -> None: + if self.is_closed: + return + + try: + self.close() + except Exception: + pass + + +class SyncAPIClient(BaseClient[httpx.Client, Stream[Any]]): + _client: httpx.Client + _default_stream_cls: type[Stream[Any]] | None = None + + def __init__( + self, + *, + version: str, + base_url: str | URL, + max_retries: int = DEFAULT_MAX_RETRIES, + timeout: float | Timeout | None | NotGiven = NOT_GIVEN, + transport: Transport | None = None, + proxies: ProxiesTypes | None = None, + limits: Limits | None = None, + http_client: httpx.Client | None = None, + custom_headers: Mapping[str, str] | None = None, + custom_query: Mapping[str, object] | None = None, + _strict_response_validation: bool, + ) -> None: + kwargs: dict[str, Any] = {} + if limits is not None: + warnings.warn( + "The `connection_pool_limits` argument is deprecated. The `http_client` argument should be passed instead", + category=DeprecationWarning, + stacklevel=3, + ) + if http_client is not None: + raise ValueError("The `http_client` argument is mutually exclusive with `connection_pool_limits`") + else: + limits = DEFAULT_CONNECTION_LIMITS + + if transport is not None: + kwargs["transport"] = transport + warnings.warn( + "The `transport` argument is deprecated. The `http_client` argument should be passed instead", + category=DeprecationWarning, + stacklevel=3, + ) + if http_client is not None: + raise ValueError("The `http_client` argument is mutually exclusive with `transport`") + + if proxies is not None: + kwargs["proxies"] = proxies + warnings.warn( + "The `proxies` argument is deprecated. The `http_client` argument should be passed instead", + category=DeprecationWarning, + stacklevel=3, + ) + if http_client is not None: + raise ValueError("The `http_client` argument is mutually exclusive with `proxies`") + + if not is_given(timeout): + # if the user passed in a custom http client with a non-default + # timeout set then we use that timeout. + # + # note: there is an edge case here where the user passes in a client + # where they've explicitly set the timeout to match the default timeout + # as this check is structural, meaning that we'll think they didn't + # pass in a timeout and will ignore it + if http_client and http_client.timeout != HTTPX_DEFAULT_TIMEOUT: + timeout = http_client.timeout + else: + timeout = DEFAULT_TIMEOUT + + if http_client is not None and not isinstance(http_client, httpx.Client): # pyright: ignore[reportUnnecessaryIsInstance] + raise TypeError( + f"Invalid `http_client` argument; Expected an instance of `httpx.Client` but got {type(http_client)}" + ) + + super().__init__( + version=version, + limits=limits, + # cast to a valid type because mypy doesn't understand our type narrowing + timeout=cast(Timeout, timeout), + proxies=proxies, + base_url=base_url, + transport=transport, + max_retries=max_retries, + custom_query=custom_query, + custom_headers=custom_headers, + _strict_response_validation=_strict_response_validation, + ) + self._client = http_client or SyncHttpxClientWrapper( + base_url=base_url, + # cast to a valid type because mypy doesn't understand our type narrowing + timeout=cast(Timeout, timeout), + limits=limits, + follow_redirects=True, + **kwargs, # type: ignore + ) + + def is_closed(self) -> bool: + return self._client.is_closed + + def close(self) -> None: + """Close the underlying HTTPX client. + + The client will *not* be usable after this. + """ + # If an error is thrown while constructing a client, self._client + # may not be present + if hasattr(self, "_client"): + self._client.close() + + def __enter__(self: _T) -> _T: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + self.close() + + def _prepare_options( + self, + options: FinalRequestOptions, # noqa: ARG002 + ) -> FinalRequestOptions: + """Hook for mutating the given options""" + return options + + def _prepare_request( + self, + request: httpx.Request, # noqa: ARG002 + ) -> None: + """This method is used as a callback for mutating the `Request` object + after it has been constructed. + This is useful for cases where you want to add certain headers based off of + the request properties, e.g. `url`, `method` etc. + """ + return None + + @overload + def request( + self, + cast_to: Type[ResponseT], + options: FinalRequestOptions, + remaining_retries: Optional[int] = None, + *, + stream: Literal[True], + stream_cls: Type[_StreamT], + ) -> _StreamT: ... + + @overload + def request( + self, + cast_to: Type[ResponseT], + options: FinalRequestOptions, + remaining_retries: Optional[int] = None, + *, + stream: Literal[False] = False, + ) -> ResponseT: ... + + @overload + def request( + self, + cast_to: Type[ResponseT], + options: FinalRequestOptions, + remaining_retries: Optional[int] = None, + *, + stream: bool = False, + stream_cls: Type[_StreamT] | None = None, + ) -> ResponseT | _StreamT: ... + + def request( + self, + cast_to: Type[ResponseT], + options: FinalRequestOptions, + remaining_retries: Optional[int] = None, + *, + stream: bool = False, + stream_cls: type[_StreamT] | None = None, + ) -> ResponseT | _StreamT: + if remaining_retries is not None: + retries_taken = options.get_max_retries(self.max_retries) - remaining_retries + else: + retries_taken = 0 + + return self._request( + cast_to=cast_to, + options=options, + stream=stream, + stream_cls=stream_cls, + retries_taken=retries_taken, + ) + + def _request( + self, + *, + cast_to: Type[ResponseT], + options: FinalRequestOptions, + retries_taken: int, + stream: bool, + stream_cls: type[_StreamT] | None, + ) -> ResponseT | _StreamT: + # create a copy of the options we were given so that if the + # options are mutated later & we then retry, the retries are + # given the original options + input_options = model_copy(options) + + cast_to = self._maybe_override_cast_to(cast_to, options) + options = self._prepare_options(options) + + remaining_retries = options.get_max_retries(self.max_retries) - retries_taken + request = self._build_request(options, retries_taken=retries_taken) + self._prepare_request(request) + + kwargs: HttpxSendArgs = {} + if self.custom_auth is not None: + kwargs["auth"] = self.custom_auth + + log.debug("Sending HTTP Request: %s %s", request.method, request.url) + + try: + response = self._client.send( + request, + stream=stream or self._should_stream_response_body(request=request), + **kwargs, + ) + except httpx.TimeoutException as err: + log.debug("Encountered httpx.TimeoutException", exc_info=True) + + if remaining_retries > 0: + return self._retry_request( + input_options, + cast_to, + retries_taken=retries_taken, + stream=stream, + stream_cls=stream_cls, + response_headers=None, + ) + + log.debug("Raising timeout error") + raise APITimeoutError(request=request) from err + except Exception as err: + log.debug("Encountered Exception", exc_info=True) + + if remaining_retries > 0: + return self._retry_request( + input_options, + cast_to, + retries_taken=retries_taken, + stream=stream, + stream_cls=stream_cls, + response_headers=None, + ) + + log.debug("Raising connection error") + raise APIConnectionError(request=request) from err + + log.debug( + 'HTTP Response: %s %s "%i %s" %s', + request.method, + request.url, + response.status_code, + response.reason_phrase, + response.headers, + ) + + try: + response.raise_for_status() + except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code + log.debug("Encountered httpx.HTTPStatusError", exc_info=True) + + if remaining_retries > 0 and self._should_retry(err.response): + err.response.close() + return self._retry_request( + input_options, + cast_to, + retries_taken=retries_taken, + response_headers=err.response.headers, + stream=stream, + stream_cls=stream_cls, + ) + + # If the response is streamed then we need to explicitly read the response + # to completion before attempting to access the response text. + if not err.response.is_closed: + err.response.read() + + log.debug("Re-raising status error") + raise self._make_status_error_from_response(err.response) from None + + return self._process_response( + cast_to=cast_to, + options=options, + response=response, + stream=stream, + stream_cls=stream_cls, + retries_taken=retries_taken, + ) + + def _retry_request( + self, + options: FinalRequestOptions, + cast_to: Type[ResponseT], + *, + retries_taken: int, + response_headers: httpx.Headers | None, + stream: bool, + stream_cls: type[_StreamT] | None, + ) -> ResponseT | _StreamT: + remaining_retries = options.get_max_retries(self.max_retries) - retries_taken + if remaining_retries == 1: + log.debug("1 retry left") + else: + log.debug("%i retries left", remaining_retries) + + timeout = self._calculate_retry_timeout(remaining_retries, options, response_headers) + log.info("Retrying request to %s in %f seconds", options.url, timeout) + + # In a synchronous context we are blocking the entire thread. Up to the library user to run the client in a + # different thread if necessary. + time.sleep(timeout) + + return self._request( + options=options, + cast_to=cast_to, + retries_taken=retries_taken + 1, + stream=stream, + stream_cls=stream_cls, + ) + + def _process_response( + self, + *, + cast_to: Type[ResponseT], + options: FinalRequestOptions, + response: httpx.Response, + stream: bool, + stream_cls: type[Stream[Any]] | type[AsyncStream[Any]] | None, + retries_taken: int = 0, + ) -> ResponseT: + origin = get_origin(cast_to) or cast_to + + if inspect.isclass(origin) and issubclass(origin, BaseAPIResponse): + if not issubclass(origin, APIResponse): + raise TypeError(f"API Response types must subclass {APIResponse}; Received {origin}") + + response_cls = cast("type[BaseAPIResponse[Any]]", cast_to) + return cast( + ResponseT, + response_cls( + raw=response, + client=self, + cast_to=extract_response_type(response_cls), + stream=stream, + stream_cls=stream_cls, + options=options, + retries_taken=retries_taken, + ), + ) + + if cast_to == httpx.Response: + return cast(ResponseT, response) + + api_response = APIResponse( + raw=response, + client=self, + cast_to=cast("type[ResponseT]", cast_to), # pyright: ignore[reportUnnecessaryCast] + stream=stream, + stream_cls=stream_cls, + options=options, + retries_taken=retries_taken, + ) + if bool(response.request.headers.get(RAW_RESPONSE_HEADER)): + return cast(ResponseT, api_response) + + return api_response.parse() + + def _request_api_list( + self, + model: Type[object], + page: Type[SyncPageT], + options: FinalRequestOptions, + ) -> SyncPageT: + def _parser(resp: SyncPageT) -> SyncPageT: + resp._set_private_attributes( + client=self, + model=model, + options=options, + ) + return resp + + options.post_parser = _parser + + return self.request(page, options, stream=False) + + @overload + def get( + self, + path: str, + *, + cast_to: Type[ResponseT], + options: RequestOptions = {}, + stream: Literal[False] = False, + ) -> ResponseT: ... + + @overload + def get( + self, + path: str, + *, + cast_to: Type[ResponseT], + options: RequestOptions = {}, + stream: Literal[True], + stream_cls: type[_StreamT], + ) -> _StreamT: ... + + @overload + def get( + self, + path: str, + *, + cast_to: Type[ResponseT], + options: RequestOptions = {}, + stream: bool, + stream_cls: type[_StreamT] | None = None, + ) -> ResponseT | _StreamT: ... + + def get( + self, + path: str, + *, + cast_to: Type[ResponseT], + options: RequestOptions = {}, + stream: bool = False, + stream_cls: type[_StreamT] | None = None, + ) -> ResponseT | _StreamT: + opts = FinalRequestOptions.construct(method="get", url=path, **options) + # cast is required because mypy complains about returning Any even though + # it understands the type variables + return cast(ResponseT, self.request(cast_to, opts, stream=stream, stream_cls=stream_cls)) + + @overload + def post( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + options: RequestOptions = {}, + files: RequestFiles | None = None, + stream: Literal[False] = False, + ) -> ResponseT: ... + + @overload + def post( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + options: RequestOptions = {}, + files: RequestFiles | None = None, + stream: Literal[True], + stream_cls: type[_StreamT], + ) -> _StreamT: ... + + @overload + def post( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + options: RequestOptions = {}, + files: RequestFiles | None = None, + stream: bool, + stream_cls: type[_StreamT] | None = None, + ) -> ResponseT | _StreamT: ... + + def post( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + options: RequestOptions = {}, + files: RequestFiles | None = None, + stream: bool = False, + stream_cls: type[_StreamT] | None = None, + ) -> ResponseT | _StreamT: + opts = FinalRequestOptions.construct( + method="post", url=path, json_data=body, files=to_httpx_files(files), **options + ) + return cast(ResponseT, self.request(cast_to, opts, stream=stream, stream_cls=stream_cls)) + + def patch( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + options: RequestOptions = {}, + ) -> ResponseT: + opts = FinalRequestOptions.construct(method="patch", url=path, json_data=body, **options) + return self.request(cast_to, opts) + + def put( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + files: RequestFiles | None = None, + options: RequestOptions = {}, + ) -> ResponseT: + opts = FinalRequestOptions.construct( + method="put", url=path, json_data=body, files=to_httpx_files(files), **options + ) + return self.request(cast_to, opts) + + def delete( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + options: RequestOptions = {}, + ) -> ResponseT: + opts = FinalRequestOptions.construct(method="delete", url=path, json_data=body, **options) + return self.request(cast_to, opts) + + def get_api_list( + self, + path: str, + *, + model: Type[object], + page: Type[SyncPageT], + body: Body | None = None, + options: RequestOptions = {}, + method: str = "get", + ) -> SyncPageT: + opts = FinalRequestOptions.construct(method=method, url=path, json_data=body, **options) + return self._request_api_list(model, page, opts) + + +class _DefaultAsyncHttpxClient(httpx.AsyncClient): + def __init__(self, **kwargs: Any) -> None: + kwargs.setdefault("timeout", DEFAULT_TIMEOUT) + kwargs.setdefault("limits", DEFAULT_CONNECTION_LIMITS) + kwargs.setdefault("follow_redirects", True) + super().__init__(**kwargs) + + +if TYPE_CHECKING: + DefaultAsyncHttpxClient = httpx.AsyncClient + """An alias to `httpx.AsyncClient` that provides the same defaults that this SDK + uses internally. + + This is useful because overriding the `http_client` with your own instance of + `httpx.AsyncClient` will result in httpx's defaults being used, not ours. + """ +else: + DefaultAsyncHttpxClient = _DefaultAsyncHttpxClient + + +class AsyncHttpxClientWrapper(DefaultAsyncHttpxClient): + def __del__(self) -> None: + if self.is_closed: + return + + try: + # TODO(someday): support non asyncio runtimes here + asyncio.get_running_loop().create_task(self.aclose()) + except Exception: + pass + + +class AsyncAPIClient(BaseClient[httpx.AsyncClient, AsyncStream[Any]]): + _client: httpx.AsyncClient + _default_stream_cls: type[AsyncStream[Any]] | None = None + + def __init__( + self, + *, + version: str, + base_url: str | URL, + _strict_response_validation: bool, + max_retries: int = DEFAULT_MAX_RETRIES, + timeout: float | Timeout | None | NotGiven = NOT_GIVEN, + transport: AsyncTransport | None = None, + proxies: ProxiesTypes | None = None, + limits: Limits | None = None, + http_client: httpx.AsyncClient | None = None, + custom_headers: Mapping[str, str] | None = None, + custom_query: Mapping[str, object] | None = None, + ) -> None: + kwargs: dict[str, Any] = {} + if limits is not None: + warnings.warn( + "The `connection_pool_limits` argument is deprecated. The `http_client` argument should be passed instead", + category=DeprecationWarning, + stacklevel=3, + ) + if http_client is not None: + raise ValueError("The `http_client` argument is mutually exclusive with `connection_pool_limits`") + else: + limits = DEFAULT_CONNECTION_LIMITS + + if transport is not None: + kwargs["transport"] = transport + warnings.warn( + "The `transport` argument is deprecated. The `http_client` argument should be passed instead", + category=DeprecationWarning, + stacklevel=3, + ) + if http_client is not None: + raise ValueError("The `http_client` argument is mutually exclusive with `transport`") + + if proxies is not None: + kwargs["proxies"] = proxies + warnings.warn( + "The `proxies` argument is deprecated. The `http_client` argument should be passed instead", + category=DeprecationWarning, + stacklevel=3, + ) + if http_client is not None: + raise ValueError("The `http_client` argument is mutually exclusive with `proxies`") + + if not is_given(timeout): + # if the user passed in a custom http client with a non-default + # timeout set then we use that timeout. + # + # note: there is an edge case here where the user passes in a client + # where they've explicitly set the timeout to match the default timeout + # as this check is structural, meaning that we'll think they didn't + # pass in a timeout and will ignore it + if http_client and http_client.timeout != HTTPX_DEFAULT_TIMEOUT: + timeout = http_client.timeout + else: + timeout = DEFAULT_TIMEOUT + + if http_client is not None and not isinstance(http_client, httpx.AsyncClient): # pyright: ignore[reportUnnecessaryIsInstance] + raise TypeError( + f"Invalid `http_client` argument; Expected an instance of `httpx.AsyncClient` but got {type(http_client)}" + ) + + super().__init__( + version=version, + base_url=base_url, + limits=limits, + # cast to a valid type because mypy doesn't understand our type narrowing + timeout=cast(Timeout, timeout), + proxies=proxies, + transport=transport, + max_retries=max_retries, + custom_query=custom_query, + custom_headers=custom_headers, + _strict_response_validation=_strict_response_validation, + ) + self._client = http_client or AsyncHttpxClientWrapper( + base_url=base_url, + # cast to a valid type because mypy doesn't understand our type narrowing + timeout=cast(Timeout, timeout), + limits=limits, + follow_redirects=True, + **kwargs, # type: ignore + ) + + def is_closed(self) -> bool: + return self._client.is_closed + + async def close(self) -> None: + """Close the underlying HTTPX client. + + The client will *not* be usable after this. + """ + await self._client.aclose() + + async def __aenter__(self: _T) -> _T: + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + await self.close() + + async def _prepare_options( + self, + options: FinalRequestOptions, # noqa: ARG002 + ) -> FinalRequestOptions: + """Hook for mutating the given options""" + return options + + async def _prepare_request( + self, + request: httpx.Request, # noqa: ARG002 + ) -> None: + """This method is used as a callback for mutating the `Request` object + after it has been constructed. + This is useful for cases where you want to add certain headers based off of + the request properties, e.g. `url`, `method` etc. + """ + return None + + @overload + async def request( + self, + cast_to: Type[ResponseT], + options: FinalRequestOptions, + *, + stream: Literal[False] = False, + remaining_retries: Optional[int] = None, + ) -> ResponseT: ... + + @overload + async def request( + self, + cast_to: Type[ResponseT], + options: FinalRequestOptions, + *, + stream: Literal[True], + stream_cls: type[_AsyncStreamT], + remaining_retries: Optional[int] = None, + ) -> _AsyncStreamT: ... + + @overload + async def request( + self, + cast_to: Type[ResponseT], + options: FinalRequestOptions, + *, + stream: bool, + stream_cls: type[_AsyncStreamT] | None = None, + remaining_retries: Optional[int] = None, + ) -> ResponseT | _AsyncStreamT: ... + + async def request( + self, + cast_to: Type[ResponseT], + options: FinalRequestOptions, + *, + stream: bool = False, + stream_cls: type[_AsyncStreamT] | None = None, + remaining_retries: Optional[int] = None, + ) -> ResponseT | _AsyncStreamT: + if remaining_retries is not None: + retries_taken = options.get_max_retries(self.max_retries) - remaining_retries + else: + retries_taken = 0 + + return await self._request( + cast_to=cast_to, + options=options, + stream=stream, + stream_cls=stream_cls, + retries_taken=retries_taken, + ) + + async def _request( + self, + cast_to: Type[ResponseT], + options: FinalRequestOptions, + *, + stream: bool, + stream_cls: type[_AsyncStreamT] | None, + retries_taken: int, + ) -> ResponseT | _AsyncStreamT: + if self._platform is None: + # `get_platform` can make blocking IO calls so we + # execute it earlier while we are in an async context + self._platform = await asyncify(get_platform)() + + # create a copy of the options we were given so that if the + # options are mutated later & we then retry, the retries are + # given the original options + input_options = model_copy(options) + + cast_to = self._maybe_override_cast_to(cast_to, options) + options = await self._prepare_options(options) + + remaining_retries = options.get_max_retries(self.max_retries) - retries_taken + request = self._build_request(options, retries_taken=retries_taken) + await self._prepare_request(request) + + kwargs: HttpxSendArgs = {} + if self.custom_auth is not None: + kwargs["auth"] = self.custom_auth + + try: + response = await self._client.send( + request, + stream=stream or self._should_stream_response_body(request=request), + **kwargs, + ) + except httpx.TimeoutException as err: + log.debug("Encountered httpx.TimeoutException", exc_info=True) + + if remaining_retries > 0: + return await self._retry_request( + input_options, + cast_to, + retries_taken=retries_taken, + stream=stream, + stream_cls=stream_cls, + response_headers=None, + ) + + log.debug("Raising timeout error") + raise APITimeoutError(request=request) from err + except Exception as err: + log.debug("Encountered Exception", exc_info=True) + + if remaining_retries > 0: + return await self._retry_request( + input_options, + cast_to, + retries_taken=retries_taken, + stream=stream, + stream_cls=stream_cls, + response_headers=None, + ) + + log.debug("Raising connection error") + raise APIConnectionError(request=request) from err + + log.debug( + 'HTTP Request: %s %s "%i %s"', request.method, request.url, response.status_code, response.reason_phrase + ) + + try: + response.raise_for_status() + except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code + log.debug("Encountered httpx.HTTPStatusError", exc_info=True) + + if remaining_retries > 0 and self._should_retry(err.response): + await err.response.aclose() + return await self._retry_request( + input_options, + cast_to, + retries_taken=retries_taken, + response_headers=err.response.headers, + stream=stream, + stream_cls=stream_cls, + ) + + # If the response is streamed then we need to explicitly read the response + # to completion before attempting to access the response text. + if not err.response.is_closed: + await err.response.aread() + + log.debug("Re-raising status error") + raise self._make_status_error_from_response(err.response) from None + + return await self._process_response( + cast_to=cast_to, + options=options, + response=response, + stream=stream, + stream_cls=stream_cls, + retries_taken=retries_taken, + ) + + async def _retry_request( + self, + options: FinalRequestOptions, + cast_to: Type[ResponseT], + *, + retries_taken: int, + response_headers: httpx.Headers | None, + stream: bool, + stream_cls: type[_AsyncStreamT] | None, + ) -> ResponseT | _AsyncStreamT: + remaining_retries = options.get_max_retries(self.max_retries) - retries_taken + if remaining_retries == 1: + log.debug("1 retry left") + else: + log.debug("%i retries left", remaining_retries) + + timeout = self._calculate_retry_timeout(remaining_retries, options, response_headers) + log.info("Retrying request to %s in %f seconds", options.url, timeout) + + await anyio.sleep(timeout) + + return await self._request( + options=options, + cast_to=cast_to, + retries_taken=retries_taken + 1, + stream=stream, + stream_cls=stream_cls, + ) + + async def _process_response( + self, + *, + cast_to: Type[ResponseT], + options: FinalRequestOptions, + response: httpx.Response, + stream: bool, + stream_cls: type[Stream[Any]] | type[AsyncStream[Any]] | None, + retries_taken: int = 0, + ) -> ResponseT: + origin = get_origin(cast_to) or cast_to + + if inspect.isclass(origin) and issubclass(origin, BaseAPIResponse): + if not issubclass(origin, AsyncAPIResponse): + raise TypeError(f"API Response types must subclass {AsyncAPIResponse}; Received {origin}") + + response_cls = cast("type[BaseAPIResponse[Any]]", cast_to) + return cast( + "ResponseT", + response_cls( + raw=response, + client=self, + cast_to=extract_response_type(response_cls), + stream=stream, + stream_cls=stream_cls, + options=options, + retries_taken=retries_taken, + ), + ) + + if cast_to == httpx.Response: + return cast(ResponseT, response) + + api_response = AsyncAPIResponse( + raw=response, + client=self, + cast_to=cast("type[ResponseT]", cast_to), # pyright: ignore[reportUnnecessaryCast] + stream=stream, + stream_cls=stream_cls, + options=options, + retries_taken=retries_taken, + ) + if bool(response.request.headers.get(RAW_RESPONSE_HEADER)): + return cast(ResponseT, api_response) + + return await api_response.parse() + + def _request_api_list( + self, + model: Type[_T], + page: Type[AsyncPageT], + options: FinalRequestOptions, + ) -> AsyncPaginator[_T, AsyncPageT]: + return AsyncPaginator(client=self, options=options, page_cls=page, model=model) + + @overload + async def get( + self, + path: str, + *, + cast_to: Type[ResponseT], + options: RequestOptions = {}, + stream: Literal[False] = False, + ) -> ResponseT: ... + + @overload + async def get( + self, + path: str, + *, + cast_to: Type[ResponseT], + options: RequestOptions = {}, + stream: Literal[True], + stream_cls: type[_AsyncStreamT], + ) -> _AsyncStreamT: ... + + @overload + async def get( + self, + path: str, + *, + cast_to: Type[ResponseT], + options: RequestOptions = {}, + stream: bool, + stream_cls: type[_AsyncStreamT] | None = None, + ) -> ResponseT | _AsyncStreamT: ... + + async def get( + self, + path: str, + *, + cast_to: Type[ResponseT], + options: RequestOptions = {}, + stream: bool = False, + stream_cls: type[_AsyncStreamT] | None = None, + ) -> ResponseT | _AsyncStreamT: + opts = FinalRequestOptions.construct(method="get", url=path, **options) + return await self.request(cast_to, opts, stream=stream, stream_cls=stream_cls) + + @overload + async def post( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + files: RequestFiles | None = None, + options: RequestOptions = {}, + stream: Literal[False] = False, + ) -> ResponseT: ... + + @overload + async def post( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + files: RequestFiles | None = None, + options: RequestOptions = {}, + stream: Literal[True], + stream_cls: type[_AsyncStreamT], + ) -> _AsyncStreamT: ... + + @overload + async def post( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + files: RequestFiles | None = None, + options: RequestOptions = {}, + stream: bool, + stream_cls: type[_AsyncStreamT] | None = None, + ) -> ResponseT | _AsyncStreamT: ... + + async def post( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + files: RequestFiles | None = None, + options: RequestOptions = {}, + stream: bool = False, + stream_cls: type[_AsyncStreamT] | None = None, + ) -> ResponseT | _AsyncStreamT: + opts = FinalRequestOptions.construct( + method="post", url=path, json_data=body, files=await async_to_httpx_files(files), **options + ) + return await self.request(cast_to, opts, stream=stream, stream_cls=stream_cls) + + async def patch( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + options: RequestOptions = {}, + ) -> ResponseT: + opts = FinalRequestOptions.construct(method="patch", url=path, json_data=body, **options) + return await self.request(cast_to, opts) + + async def put( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + files: RequestFiles | None = None, + options: RequestOptions = {}, + ) -> ResponseT: + opts = FinalRequestOptions.construct( + method="put", url=path, json_data=body, files=await async_to_httpx_files(files), **options + ) + return await self.request(cast_to, opts) + + async def delete( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + options: RequestOptions = {}, + ) -> ResponseT: + opts = FinalRequestOptions.construct(method="delete", url=path, json_data=body, **options) + return await self.request(cast_to, opts) + + def get_api_list( + self, + path: str, + *, + model: Type[_T], + page: Type[AsyncPageT], + body: Body | None = None, + options: RequestOptions = {}, + method: str = "get", + ) -> AsyncPaginator[_T, AsyncPageT]: + opts = FinalRequestOptions.construct(method=method, url=path, json_data=body, **options) + return self._request_api_list(model, page, opts) + + +def make_request_options( + *, + query: Query | None = None, + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + idempotency_key: str | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + post_parser: PostParser | NotGiven = NOT_GIVEN, +) -> RequestOptions: + """Create a dict of type RequestOptions without keys of NotGiven values.""" + options: RequestOptions = {} + if extra_headers is not None: + options["headers"] = extra_headers + + if extra_body is not None: + options["extra_json"] = cast(AnyMapping, extra_body) + + if query is not None: + options["params"] = query + + if extra_query is not None: + options["params"] = {**options.get("params", {}), **extra_query} + + if not isinstance(timeout, NotGiven): + options["timeout"] = timeout + + if idempotency_key is not None: + options["idempotency_key"] = idempotency_key + + if is_given(post_parser): + # internal + options["post_parser"] = post_parser # type: ignore + + return options + + +class ForceMultipartDict(Dict[str, None]): + def __bool__(self) -> bool: + return True + + +class OtherPlatform: + def __init__(self, name: str) -> None: + self.name = name + + @override + def __str__(self) -> str: + return f"Other:{self.name}" + + +Platform = Union[ + OtherPlatform, + Literal[ + "MacOS", + "Linux", + "Windows", + "FreeBSD", + "OpenBSD", + "iOS", + "Android", + "Unknown", + ], +] + + +def get_platform() -> Platform: + try: + system = platform.system().lower() + platform_name = platform.platform().lower() + except Exception: + return "Unknown" + + if "iphone" in platform_name or "ipad" in platform_name: + # Tested using Python3IDE on an iPhone 11 and Pythonista on an iPad 7 + # system is Darwin and platform_name is a string like: + # - Darwin-21.6.0-iPhone12,1-64bit + # - Darwin-21.6.0-iPad7,11-64bit + return "iOS" + + if system == "darwin": + return "MacOS" + + if system == "windows": + return "Windows" + + if "android" in platform_name: + # Tested using Pydroid 3 + # system is Linux and platform_name is a string like 'Linux-5.10.81-android12-9-00001-geba40aecb3b7-ab8534902-aarch64-with-libc' + return "Android" + + if system == "linux": + # https://distro.readthedocs.io/en/latest/#distro.id + distro_id = distro.id() + if distro_id == "freebsd": + return "FreeBSD" + + if distro_id == "openbsd": + return "OpenBSD" + + return "Linux" + + if platform_name: + return OtherPlatform(platform_name) + + return "Unknown" + + +@lru_cache(maxsize=None) +def platform_headers(version: str, *, platform: Platform | None) -> Dict[str, str]: + return { + "X-Stainless-Lang": "python", + "X-Stainless-Package-Version": version, + "X-Stainless-OS": str(platform or get_platform()), + "X-Stainless-Arch": str(get_architecture()), + "X-Stainless-Runtime": get_python_runtime(), + "X-Stainless-Runtime-Version": get_python_version(), + } + + +class OtherArch: + def __init__(self, name: str) -> None: + self.name = name + + @override + def __str__(self) -> str: + return f"other:{self.name}" + + +Arch = Union[OtherArch, Literal["x32", "x64", "arm", "arm64", "unknown"]] + + +def get_python_runtime() -> str: + try: + return platform.python_implementation() + except Exception: + return "unknown" + + +def get_python_version() -> str: + try: + return platform.python_version() + except Exception: + return "unknown" + + +def get_architecture() -> Arch: + try: + machine = platform.machine().lower() + except Exception: + return "unknown" + + if machine in ("arm64", "aarch64"): + return "arm64" + + # TODO: untested + if machine == "arm": + return "arm" + + if machine == "x86_64": + return "x64" + + # TODO: untested + if sys.maxsize <= 2**32: + return "x32" + + if machine: + return OtherArch(machine) + + return "unknown" + + +def _merge_mappings( + obj1: Mapping[_T_co, Union[_T, Omit]], + obj2: Mapping[_T_co, Union[_T, Omit]], +) -> Dict[_T_co, _T]: + """Merge two mappings of the same type, removing any values that are instances of `Omit`. + + In cases with duplicate keys the second mapping takes precedence. + """ + merged = {**obj1, **obj2} + return {key: value for key, value in merged.items() if not isinstance(value, Omit)} diff --git a/src/postgrid/_client.py b/src/postgrid/_client.py new file mode 100644 index 0000000..a0f2f9a --- /dev/null +++ b/src/postgrid/_client.py @@ -0,0 +1,520 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, Union, Mapping +from typing_extensions import Self, override + +import httpx + +from . import _exceptions +from ._qs import Querystring +from ._types import ( + NOT_GIVEN, + Omit, + Headers, + Timeout, + NotGiven, + Transport, + ProxiesTypes, + RequestOptions, +) +from ._utils import ( + is_given, + get_async_library, +) +from ._version import __version__ +from .resources import address_verification, intl_address_verification +from ._streaming import Stream as Stream, AsyncStream as AsyncStream +from ._exceptions import APIStatusError +from ._base_client import ( + DEFAULT_MAX_RETRIES, + SyncAPIClient, + AsyncAPIClient, +) +from .resources.print_mail import print_mail + +__all__ = [ + "Timeout", + "Transport", + "ProxiesTypes", + "RequestOptions", + "PostGrid", + "AsyncPostGrid", + "Client", + "AsyncClient", +] + + +class PostGrid(SyncAPIClient): + address_verification: address_verification.AddressVerificationResource + intl_address_verification: intl_address_verification.IntlAddressVerificationResource + print_mail: print_mail.PrintMailResource + with_raw_response: PostGridWithRawResponse + with_streaming_response: PostGridWithStreamedResponse + + # client options + address_verification_api_key: str | None + print_mail_api_key: str | None + + def __init__( + self, + *, + address_verification_api_key: str | None = None, + print_mail_api_key: str | None = None, + base_url: str | httpx.URL | None = None, + timeout: Union[float, Timeout, None, NotGiven] = NOT_GIVEN, + max_retries: int = DEFAULT_MAX_RETRIES, + default_headers: Mapping[str, str] | None = None, + default_query: Mapping[str, object] | None = None, + # Configure a custom httpx client. + # We provide a `DefaultHttpxClient` class that you can pass to retain the default values we use for `limits`, `timeout` & `follow_redirects`. + # See the [httpx documentation](https://www.python-httpx.org/api/#client) for more details. + http_client: httpx.Client | None = None, + # Enable or disable schema validation for data returned by the API. + # When enabled an error APIResponseValidationError is raised + # if the API responds with invalid data for the expected schema. + # + # This parameter may be removed or changed in the future. + # If you rely on this feature, please open a GitHub issue + # outlining your use-case to help us decide if it should be + # part of our public interface in the future. + _strict_response_validation: bool = False, + ) -> None: + """Construct a new synchronous PostGrid client instance. + + This automatically infers the following arguments from their corresponding environment variables if they are not provided: + - `address_verification_api_key` from `POSTGRID_ADDRESS_VERIFICATION_API_KEY` + - `print_mail_api_key` from `POSTGRID_PRINT_MAIL_API_KEY` + """ + if address_verification_api_key is None: + address_verification_api_key = os.environ.get("POSTGRID_ADDRESS_VERIFICATION_API_KEY") + self.address_verification_api_key = address_verification_api_key + + if print_mail_api_key is None: + print_mail_api_key = os.environ.get("POSTGRID_PRINT_MAIL_API_KEY") + self.print_mail_api_key = print_mail_api_key + + if base_url is None: + base_url = os.environ.get("POST_GRID_BASE_URL") + if base_url is None: + base_url = f"https://api.postgrid.com" + + super().__init__( + version=__version__, + base_url=base_url, + max_retries=max_retries, + timeout=timeout, + http_client=http_client, + custom_headers=default_headers, + custom_query=default_query, + _strict_response_validation=_strict_response_validation, + ) + + self.address_verification = address_verification.AddressVerificationResource(self) + self.intl_address_verification = intl_address_verification.IntlAddressVerificationResource(self) + self.print_mail = print_mail.PrintMailResource(self) + self.with_raw_response = PostGridWithRawResponse(self) + self.with_streaming_response = PostGridWithStreamedResponse(self) + + @property + @override + def qs(self) -> Querystring: + return Querystring(array_format="comma") + + @property + @override + def auth_headers(self) -> dict[str, str]: + if self._address_verification_api_key_auth: + return self._address_verification_api_key_auth + if self._print_mail_api_key_auth: + return self._print_mail_api_key_auth + return {} + + @property + def _address_verification_api_key_auth(self) -> dict[str, str]: + address_verification_api_key = self.address_verification_api_key + if address_verification_api_key is None: + return {} + return {"X-API-Key": address_verification_api_key} + + @property + def _print_mail_api_key_auth(self) -> dict[str, str]: + print_mail_api_key = self.print_mail_api_key + if print_mail_api_key is None: + return {} + return {"X-API-Key": print_mail_api_key} + + @property + @override + def default_headers(self) -> dict[str, str | Omit]: + return { + **super().default_headers, + "X-Stainless-Async": "false", + **self._custom_headers, + } + + @override + def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None: + if self.address_verification_api_key and headers.get("X-API-Key"): + return + if isinstance(custom_headers.get("X-API-Key"), Omit): + return + + if self.print_mail_api_key and headers.get("X-API-Key"): + return + if isinstance(custom_headers.get("X-API-Key"), Omit): + return + + raise TypeError( + '"Could not resolve authentication method. Expected either address_verification_api_key or print_mail_api_key to be set. Or for one of the `X-API-Key` or `X-API-Key` headers to be explicitly omitted"' + ) + + def copy( + self, + *, + address_verification_api_key: str | None = None, + print_mail_api_key: str | None = None, + base_url: str | httpx.URL | None = None, + timeout: float | Timeout | None | NotGiven = NOT_GIVEN, + http_client: httpx.Client | None = None, + max_retries: int | NotGiven = NOT_GIVEN, + default_headers: Mapping[str, str] | None = None, + set_default_headers: Mapping[str, str] | None = None, + default_query: Mapping[str, object] | None = None, + set_default_query: Mapping[str, object] | None = None, + _extra_kwargs: Mapping[str, Any] = {}, + ) -> Self: + """ + Create a new client instance re-using the same options given to the current client with optional overriding. + """ + if default_headers is not None and set_default_headers is not None: + raise ValueError("The `default_headers` and `set_default_headers` arguments are mutually exclusive") + + if default_query is not None and set_default_query is not None: + raise ValueError("The `default_query` and `set_default_query` arguments are mutually exclusive") + + headers = self._custom_headers + if default_headers is not None: + headers = {**headers, **default_headers} + elif set_default_headers is not None: + headers = set_default_headers + + params = self._custom_query + if default_query is not None: + params = {**params, **default_query} + elif set_default_query is not None: + params = set_default_query + + http_client = http_client or self._client + return self.__class__( + address_verification_api_key=address_verification_api_key or self.address_verification_api_key, + print_mail_api_key=print_mail_api_key or self.print_mail_api_key, + base_url=base_url or self.base_url, + timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, + http_client=http_client, + max_retries=max_retries if is_given(max_retries) else self.max_retries, + default_headers=headers, + default_query=params, + **_extra_kwargs, + ) + + # Alias for `copy` for nicer inline usage, e.g. + # client.with_options(timeout=10).foo.create(...) + with_options = copy + + @override + def _make_status_error( + self, + err_msg: str, + *, + body: object, + response: httpx.Response, + ) -> APIStatusError: + if response.status_code == 400: + return _exceptions.BadRequestError(err_msg, response=response, body=body) + + if response.status_code == 401: + return _exceptions.AuthenticationError(err_msg, response=response, body=body) + + if response.status_code == 403: + return _exceptions.PermissionDeniedError(err_msg, response=response, body=body) + + if response.status_code == 404: + return _exceptions.NotFoundError(err_msg, response=response, body=body) + + if response.status_code == 409: + return _exceptions.ConflictError(err_msg, response=response, body=body) + + if response.status_code == 422: + return _exceptions.UnprocessableEntityError(err_msg, response=response, body=body) + + if response.status_code == 429: + return _exceptions.RateLimitError(err_msg, response=response, body=body) + + if response.status_code >= 500: + return _exceptions.InternalServerError(err_msg, response=response, body=body) + return APIStatusError(err_msg, response=response, body=body) + + +class AsyncPostGrid(AsyncAPIClient): + address_verification: address_verification.AsyncAddressVerificationResource + intl_address_verification: intl_address_verification.AsyncIntlAddressVerificationResource + print_mail: print_mail.AsyncPrintMailResource + with_raw_response: AsyncPostGridWithRawResponse + with_streaming_response: AsyncPostGridWithStreamedResponse + + # client options + address_verification_api_key: str | None + print_mail_api_key: str | None + + def __init__( + self, + *, + address_verification_api_key: str | None = None, + print_mail_api_key: str | None = None, + base_url: str | httpx.URL | None = None, + timeout: Union[float, Timeout, None, NotGiven] = NOT_GIVEN, + max_retries: int = DEFAULT_MAX_RETRIES, + default_headers: Mapping[str, str] | None = None, + default_query: Mapping[str, object] | None = None, + # Configure a custom httpx client. + # We provide a `DefaultAsyncHttpxClient` class that you can pass to retain the default values we use for `limits`, `timeout` & `follow_redirects`. + # See the [httpx documentation](https://www.python-httpx.org/api/#asyncclient) for more details. + http_client: httpx.AsyncClient | None = None, + # Enable or disable schema validation for data returned by the API. + # When enabled an error APIResponseValidationError is raised + # if the API responds with invalid data for the expected schema. + # + # This parameter may be removed or changed in the future. + # If you rely on this feature, please open a GitHub issue + # outlining your use-case to help us decide if it should be + # part of our public interface in the future. + _strict_response_validation: bool = False, + ) -> None: + """Construct a new async PostGrid client instance. + + This automatically infers the following arguments from their corresponding environment variables if they are not provided: + - `address_verification_api_key` from `POSTGRID_ADDRESS_VERIFICATION_API_KEY` + - `print_mail_api_key` from `POSTGRID_PRINT_MAIL_API_KEY` + """ + if address_verification_api_key is None: + address_verification_api_key = os.environ.get("POSTGRID_ADDRESS_VERIFICATION_API_KEY") + self.address_verification_api_key = address_verification_api_key + + if print_mail_api_key is None: + print_mail_api_key = os.environ.get("POSTGRID_PRINT_MAIL_API_KEY") + self.print_mail_api_key = print_mail_api_key + + if base_url is None: + base_url = os.environ.get("POST_GRID_BASE_URL") + if base_url is None: + base_url = f"https://api.postgrid.com" + + super().__init__( + version=__version__, + base_url=base_url, + max_retries=max_retries, + timeout=timeout, + http_client=http_client, + custom_headers=default_headers, + custom_query=default_query, + _strict_response_validation=_strict_response_validation, + ) + + self.address_verification = address_verification.AsyncAddressVerificationResource(self) + self.intl_address_verification = intl_address_verification.AsyncIntlAddressVerificationResource(self) + self.print_mail = print_mail.AsyncPrintMailResource(self) + self.with_raw_response = AsyncPostGridWithRawResponse(self) + self.with_streaming_response = AsyncPostGridWithStreamedResponse(self) + + @property + @override + def qs(self) -> Querystring: + return Querystring(array_format="comma") + + @property + @override + def auth_headers(self) -> dict[str, str]: + if self._address_verification_api_key_auth: + return self._address_verification_api_key_auth + if self._print_mail_api_key_auth: + return self._print_mail_api_key_auth + return {} + + @property + def _address_verification_api_key_auth(self) -> dict[str, str]: + address_verification_api_key = self.address_verification_api_key + if address_verification_api_key is None: + return {} + return {"X-API-Key": address_verification_api_key} + + @property + def _print_mail_api_key_auth(self) -> dict[str, str]: + print_mail_api_key = self.print_mail_api_key + if print_mail_api_key is None: + return {} + return {"X-API-Key": print_mail_api_key} + + @property + @override + def default_headers(self) -> dict[str, str | Omit]: + return { + **super().default_headers, + "X-Stainless-Async": f"async:{get_async_library()}", + **self._custom_headers, + } + + @override + def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None: + if self.address_verification_api_key and headers.get("X-API-Key"): + return + if isinstance(custom_headers.get("X-API-Key"), Omit): + return + + if self.print_mail_api_key and headers.get("X-API-Key"): + return + if isinstance(custom_headers.get("X-API-Key"), Omit): + return + + raise TypeError( + '"Could not resolve authentication method. Expected either address_verification_api_key or print_mail_api_key to be set. Or for one of the `X-API-Key` or `X-API-Key` headers to be explicitly omitted"' + ) + + def copy( + self, + *, + address_verification_api_key: str | None = None, + print_mail_api_key: str | None = None, + base_url: str | httpx.URL | None = None, + timeout: float | Timeout | None | NotGiven = NOT_GIVEN, + http_client: httpx.AsyncClient | None = None, + max_retries: int | NotGiven = NOT_GIVEN, + default_headers: Mapping[str, str] | None = None, + set_default_headers: Mapping[str, str] | None = None, + default_query: Mapping[str, object] | None = None, + set_default_query: Mapping[str, object] | None = None, + _extra_kwargs: Mapping[str, Any] = {}, + ) -> Self: + """ + Create a new client instance re-using the same options given to the current client with optional overriding. + """ + if default_headers is not None and set_default_headers is not None: + raise ValueError("The `default_headers` and `set_default_headers` arguments are mutually exclusive") + + if default_query is not None and set_default_query is not None: + raise ValueError("The `default_query` and `set_default_query` arguments are mutually exclusive") + + headers = self._custom_headers + if default_headers is not None: + headers = {**headers, **default_headers} + elif set_default_headers is not None: + headers = set_default_headers + + params = self._custom_query + if default_query is not None: + params = {**params, **default_query} + elif set_default_query is not None: + params = set_default_query + + http_client = http_client or self._client + return self.__class__( + address_verification_api_key=address_verification_api_key or self.address_verification_api_key, + print_mail_api_key=print_mail_api_key or self.print_mail_api_key, + base_url=base_url or self.base_url, + timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, + http_client=http_client, + max_retries=max_retries if is_given(max_retries) else self.max_retries, + default_headers=headers, + default_query=params, + **_extra_kwargs, + ) + + # Alias for `copy` for nicer inline usage, e.g. + # client.with_options(timeout=10).foo.create(...) + with_options = copy + + @override + def _make_status_error( + self, + err_msg: str, + *, + body: object, + response: httpx.Response, + ) -> APIStatusError: + if response.status_code == 400: + return _exceptions.BadRequestError(err_msg, response=response, body=body) + + if response.status_code == 401: + return _exceptions.AuthenticationError(err_msg, response=response, body=body) + + if response.status_code == 403: + return _exceptions.PermissionDeniedError(err_msg, response=response, body=body) + + if response.status_code == 404: + return _exceptions.NotFoundError(err_msg, response=response, body=body) + + if response.status_code == 409: + return _exceptions.ConflictError(err_msg, response=response, body=body) + + if response.status_code == 422: + return _exceptions.UnprocessableEntityError(err_msg, response=response, body=body) + + if response.status_code == 429: + return _exceptions.RateLimitError(err_msg, response=response, body=body) + + if response.status_code >= 500: + return _exceptions.InternalServerError(err_msg, response=response, body=body) + return APIStatusError(err_msg, response=response, body=body) + + +class PostGridWithRawResponse: + def __init__(self, client: PostGrid) -> None: + self.address_verification = address_verification.AddressVerificationResourceWithRawResponse( + client.address_verification + ) + self.intl_address_verification = intl_address_verification.IntlAddressVerificationResourceWithRawResponse( + client.intl_address_verification + ) + self.print_mail = print_mail.PrintMailResourceWithRawResponse(client.print_mail) + + +class AsyncPostGridWithRawResponse: + def __init__(self, client: AsyncPostGrid) -> None: + self.address_verification = address_verification.AsyncAddressVerificationResourceWithRawResponse( + client.address_verification + ) + self.intl_address_verification = intl_address_verification.AsyncIntlAddressVerificationResourceWithRawResponse( + client.intl_address_verification + ) + self.print_mail = print_mail.AsyncPrintMailResourceWithRawResponse(client.print_mail) + + +class PostGridWithStreamedResponse: + def __init__(self, client: PostGrid) -> None: + self.address_verification = address_verification.AddressVerificationResourceWithStreamingResponse( + client.address_verification + ) + self.intl_address_verification = intl_address_verification.IntlAddressVerificationResourceWithStreamingResponse( + client.intl_address_verification + ) + self.print_mail = print_mail.PrintMailResourceWithStreamingResponse(client.print_mail) + + +class AsyncPostGridWithStreamedResponse: + def __init__(self, client: AsyncPostGrid) -> None: + self.address_verification = address_verification.AsyncAddressVerificationResourceWithStreamingResponse( + client.address_verification + ) + self.intl_address_verification = ( + intl_address_verification.AsyncIntlAddressVerificationResourceWithStreamingResponse( + client.intl_address_verification + ) + ) + self.print_mail = print_mail.AsyncPrintMailResourceWithStreamingResponse(client.print_mail) + + +Client = PostGrid + +AsyncClient = AsyncPostGrid diff --git a/src/postgrid/_compat.py b/src/postgrid/_compat.py new file mode 100644 index 0000000..92d9ee6 --- /dev/null +++ b/src/postgrid/_compat.py @@ -0,0 +1,219 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, Union, Generic, TypeVar, Callable, cast, overload +from datetime import date, datetime +from typing_extensions import Self, Literal + +import pydantic +from pydantic.fields import FieldInfo + +from ._types import IncEx, StrBytesIntFloat + +_T = TypeVar("_T") +_ModelT = TypeVar("_ModelT", bound=pydantic.BaseModel) + +# --------------- Pydantic v2 compatibility --------------- + +# Pyright incorrectly reports some of our functions as overriding a method when they don't +# pyright: reportIncompatibleMethodOverride=false + +PYDANTIC_V2 = pydantic.VERSION.startswith("2.") + +# v1 re-exports +if TYPE_CHECKING: + + def parse_date(value: date | StrBytesIntFloat) -> date: # noqa: ARG001 + ... + + def parse_datetime(value: Union[datetime, StrBytesIntFloat]) -> datetime: # noqa: ARG001 + ... + + def get_args(t: type[Any]) -> tuple[Any, ...]: # noqa: ARG001 + ... + + def is_union(tp: type[Any] | None) -> bool: # noqa: ARG001 + ... + + def get_origin(t: type[Any]) -> type[Any] | None: # noqa: ARG001 + ... + + def is_literal_type(type_: type[Any]) -> bool: # noqa: ARG001 + ... + + def is_typeddict(type_: type[Any]) -> bool: # noqa: ARG001 + ... + +else: + if PYDANTIC_V2: + from pydantic.v1.typing import ( + get_args as get_args, + is_union as is_union, + get_origin as get_origin, + is_typeddict as is_typeddict, + is_literal_type as is_literal_type, + ) + from pydantic.v1.datetime_parse import parse_date as parse_date, parse_datetime as parse_datetime + else: + from pydantic.typing import ( + get_args as get_args, + is_union as is_union, + get_origin as get_origin, + is_typeddict as is_typeddict, + is_literal_type as is_literal_type, + ) + from pydantic.datetime_parse import parse_date as parse_date, parse_datetime as parse_datetime + + +# refactored config +if TYPE_CHECKING: + from pydantic import ConfigDict as ConfigDict +else: + if PYDANTIC_V2: + from pydantic import ConfigDict + else: + # TODO: provide an error message here? + ConfigDict = None + + +# renamed methods / properties +def parse_obj(model: type[_ModelT], value: object) -> _ModelT: + if PYDANTIC_V2: + return model.model_validate(value) + else: + return cast(_ModelT, model.parse_obj(value)) # pyright: ignore[reportDeprecated, reportUnnecessaryCast] + + +def field_is_required(field: FieldInfo) -> bool: + if PYDANTIC_V2: + return field.is_required() + return field.required # type: ignore + + +def field_get_default(field: FieldInfo) -> Any: + value = field.get_default() + if PYDANTIC_V2: + from pydantic_core import PydanticUndefined + + if value == PydanticUndefined: + return None + return value + return value + + +def field_outer_type(field: FieldInfo) -> Any: + if PYDANTIC_V2: + return field.annotation + return field.outer_type_ # type: ignore + + +def get_model_config(model: type[pydantic.BaseModel]) -> Any: + if PYDANTIC_V2: + return model.model_config + return model.__config__ # type: ignore + + +def get_model_fields(model: type[pydantic.BaseModel]) -> dict[str, FieldInfo]: + if PYDANTIC_V2: + return model.model_fields + return model.__fields__ # type: ignore + + +def model_copy(model: _ModelT, *, deep: bool = False) -> _ModelT: + if PYDANTIC_V2: + return model.model_copy(deep=deep) + return model.copy(deep=deep) # type: ignore + + +def model_json(model: pydantic.BaseModel, *, indent: int | None = None) -> str: + if PYDANTIC_V2: + return model.model_dump_json(indent=indent) + return model.json(indent=indent) # type: ignore + + +def model_dump( + model: pydantic.BaseModel, + *, + exclude: IncEx | None = None, + exclude_unset: bool = False, + exclude_defaults: bool = False, + warnings: bool = True, + mode: Literal["json", "python"] = "python", +) -> dict[str, Any]: + if PYDANTIC_V2 or hasattr(model, "model_dump"): + return model.model_dump( + mode=mode, + exclude=exclude, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + # warnings are not supported in Pydantic v1 + warnings=warnings if PYDANTIC_V2 else True, + ) + return cast( + "dict[str, Any]", + model.dict( # pyright: ignore[reportDeprecated, reportUnnecessaryCast] + exclude=exclude, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + ), + ) + + +def model_parse(model: type[_ModelT], data: Any) -> _ModelT: + if PYDANTIC_V2: + return model.model_validate(data) + return model.parse_obj(data) # pyright: ignore[reportDeprecated] + + +# generic models +if TYPE_CHECKING: + + class GenericModel(pydantic.BaseModel): ... + +else: + if PYDANTIC_V2: + # there no longer needs to be a distinction in v2 but + # we still have to create our own subclass to avoid + # inconsistent MRO ordering errors + class GenericModel(pydantic.BaseModel): ... + + else: + import pydantic.generics + + class GenericModel(pydantic.generics.GenericModel, pydantic.BaseModel): ... + + +# cached properties +if TYPE_CHECKING: + cached_property = property + + # we define a separate type (copied from typeshed) + # that represents that `cached_property` is `set`able + # at runtime, which differs from `@property`. + # + # this is a separate type as editors likely special case + # `@property` and we don't want to cause issues just to have + # more helpful internal types. + + class typed_cached_property(Generic[_T]): + func: Callable[[Any], _T] + attrname: str | None + + def __init__(self, func: Callable[[Any], _T]) -> None: ... + + @overload + def __get__(self, instance: None, owner: type[Any] | None = None) -> Self: ... + + @overload + def __get__(self, instance: object, owner: type[Any] | None = None) -> _T: ... + + def __get__(self, instance: object, owner: type[Any] | None = None) -> _T | Self: + raise NotImplementedError() + + def __set_name__(self, owner: type[Any], name: str) -> None: ... + + # __set__ is not defined at runtime, but @cached_property is designed to be settable + def __set__(self, instance: object, value: _T) -> None: ... +else: + from functools import cached_property as cached_property + + typed_cached_property = cached_property diff --git a/src/postgrid/_constants.py b/src/postgrid/_constants.py new file mode 100644 index 0000000..6ddf2c7 --- /dev/null +++ b/src/postgrid/_constants.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import httpx + +RAW_RESPONSE_HEADER = "X-Stainless-Raw-Response" +OVERRIDE_CAST_TO_HEADER = "____stainless_override_cast_to" + +# default timeout is 1 minute +DEFAULT_TIMEOUT = httpx.Timeout(timeout=60, connect=5.0) +DEFAULT_MAX_RETRIES = 2 +DEFAULT_CONNECTION_LIMITS = httpx.Limits(max_connections=100, max_keepalive_connections=20) + +INITIAL_RETRY_DELAY = 0.5 +MAX_RETRY_DELAY = 8.0 diff --git a/src/postgrid/_exceptions.py b/src/postgrid/_exceptions.py new file mode 100644 index 0000000..3fc8ade --- /dev/null +++ b/src/postgrid/_exceptions.py @@ -0,0 +1,108 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx + +__all__ = [ + "BadRequestError", + "AuthenticationError", + "PermissionDeniedError", + "NotFoundError", + "ConflictError", + "UnprocessableEntityError", + "RateLimitError", + "InternalServerError", +] + + +class PostGridError(Exception): + pass + + +class APIError(PostGridError): + message: str + request: httpx.Request + + body: object | None + """The API response body. + + If the API responded with a valid JSON structure then this property will be the + decoded result. + + If it isn't a valid JSON structure then this will be the raw response. + + If there was no response associated with this error then it will be `None`. + """ + + def __init__(self, message: str, request: httpx.Request, *, body: object | None) -> None: # noqa: ARG002 + super().__init__(message) + self.request = request + self.message = message + self.body = body + + +class APIResponseValidationError(APIError): + response: httpx.Response + status_code: int + + def __init__(self, response: httpx.Response, body: object | None, *, message: str | None = None) -> None: + super().__init__(message or "Data returned by API invalid for expected schema.", response.request, body=body) + self.response = response + self.status_code = response.status_code + + +class APIStatusError(APIError): + """Raised when an API response has a status code of 4xx or 5xx.""" + + response: httpx.Response + status_code: int + + def __init__(self, message: str, *, response: httpx.Response, body: object | None) -> None: + super().__init__(message, response.request, body=body) + self.response = response + self.status_code = response.status_code + + +class APIConnectionError(APIError): + def __init__(self, *, message: str = "Connection error.", request: httpx.Request) -> None: + super().__init__(message, request, body=None) + + +class APITimeoutError(APIConnectionError): + def __init__(self, request: httpx.Request) -> None: + super().__init__(message="Request timed out.", request=request) + + +class BadRequestError(APIStatusError): + status_code: Literal[400] = 400 # pyright: ignore[reportIncompatibleVariableOverride] + + +class AuthenticationError(APIStatusError): + status_code: Literal[401] = 401 # pyright: ignore[reportIncompatibleVariableOverride] + + +class PermissionDeniedError(APIStatusError): + status_code: Literal[403] = 403 # pyright: ignore[reportIncompatibleVariableOverride] + + +class NotFoundError(APIStatusError): + status_code: Literal[404] = 404 # pyright: ignore[reportIncompatibleVariableOverride] + + +class ConflictError(APIStatusError): + status_code: Literal[409] = 409 # pyright: ignore[reportIncompatibleVariableOverride] + + +class UnprocessableEntityError(APIStatusError): + status_code: Literal[422] = 422 # pyright: ignore[reportIncompatibleVariableOverride] + + +class RateLimitError(APIStatusError): + status_code: Literal[429] = 429 # pyright: ignore[reportIncompatibleVariableOverride] + + +class InternalServerError(APIStatusError): + pass diff --git a/src/postgrid/_files.py b/src/postgrid/_files.py new file mode 100644 index 0000000..715cc20 --- /dev/null +++ b/src/postgrid/_files.py @@ -0,0 +1,123 @@ +from __future__ import annotations + +import io +import os +import pathlib +from typing import overload +from typing_extensions import TypeGuard + +import anyio + +from ._types import ( + FileTypes, + FileContent, + RequestFiles, + HttpxFileTypes, + Base64FileInput, + HttpxFileContent, + HttpxRequestFiles, +) +from ._utils import is_tuple_t, is_mapping_t, is_sequence_t + + +def is_base64_file_input(obj: object) -> TypeGuard[Base64FileInput]: + return isinstance(obj, io.IOBase) or isinstance(obj, os.PathLike) + + +def is_file_content(obj: object) -> TypeGuard[FileContent]: + return ( + isinstance(obj, bytes) or isinstance(obj, tuple) or isinstance(obj, io.IOBase) or isinstance(obj, os.PathLike) + ) + + +def assert_is_file_content(obj: object, *, key: str | None = None) -> None: + if not is_file_content(obj): + prefix = f"Expected entry at `{key}`" if key is not None else f"Expected file input `{obj!r}`" + raise RuntimeError( + f"{prefix} to be bytes, an io.IOBase instance, PathLike or a tuple but received {type(obj)} instead." + ) from None + + +@overload +def to_httpx_files(files: None) -> None: ... + + +@overload +def to_httpx_files(files: RequestFiles) -> HttpxRequestFiles: ... + + +def to_httpx_files(files: RequestFiles | None) -> HttpxRequestFiles | None: + if files is None: + return None + + if is_mapping_t(files): + files = {key: _transform_file(file) for key, file in files.items()} + elif is_sequence_t(files): + files = [(key, _transform_file(file)) for key, file in files] + else: + raise TypeError(f"Unexpected file type input {type(files)}, expected mapping or sequence") + + return files + + +def _transform_file(file: FileTypes) -> HttpxFileTypes: + if is_file_content(file): + if isinstance(file, os.PathLike): + path = pathlib.Path(file) + return (path.name, path.read_bytes()) + + return file + + if is_tuple_t(file): + return (file[0], _read_file_content(file[1]), *file[2:]) + + raise TypeError(f"Expected file types input to be a FileContent type or to be a tuple") + + +def _read_file_content(file: FileContent) -> HttpxFileContent: + if isinstance(file, os.PathLike): + return pathlib.Path(file).read_bytes() + return file + + +@overload +async def async_to_httpx_files(files: None) -> None: ... + + +@overload +async def async_to_httpx_files(files: RequestFiles) -> HttpxRequestFiles: ... + + +async def async_to_httpx_files(files: RequestFiles | None) -> HttpxRequestFiles | None: + if files is None: + return None + + if is_mapping_t(files): + files = {key: await _async_transform_file(file) for key, file in files.items()} + elif is_sequence_t(files): + files = [(key, await _async_transform_file(file)) for key, file in files] + else: + raise TypeError("Unexpected file type input {type(files)}, expected mapping or sequence") + + return files + + +async def _async_transform_file(file: FileTypes) -> HttpxFileTypes: + if is_file_content(file): + if isinstance(file, os.PathLike): + path = anyio.Path(file) + return (path.name, await path.read_bytes()) + + return file + + if is_tuple_t(file): + return (file[0], await _async_read_file_content(file[1]), *file[2:]) + + raise TypeError(f"Expected file types input to be a FileContent type or to be a tuple") + + +async def _async_read_file_content(file: FileContent) -> HttpxFileContent: + if isinstance(file, os.PathLike): + return await anyio.Path(file).read_bytes() + + return file diff --git a/src/postgrid/_models.py b/src/postgrid/_models.py new file mode 100644 index 0000000..c4401ff --- /dev/null +++ b/src/postgrid/_models.py @@ -0,0 +1,801 @@ +from __future__ import annotations + +import os +import inspect +from typing import TYPE_CHECKING, Any, Type, Union, Generic, TypeVar, Callable, cast +from datetime import date, datetime +from typing_extensions import ( + Unpack, + Literal, + ClassVar, + Protocol, + Required, + ParamSpec, + TypedDict, + TypeGuard, + final, + override, + runtime_checkable, +) + +import pydantic +import pydantic.generics +from pydantic.fields import FieldInfo + +from ._types import ( + Body, + IncEx, + Query, + ModelT, + Headers, + Timeout, + NotGiven, + AnyMapping, + HttpxRequestFiles, +) +from ._utils import ( + PropertyInfo, + is_list, + is_given, + json_safe, + lru_cache, + is_mapping, + parse_date, + coerce_boolean, + parse_datetime, + strip_not_given, + extract_type_arg, + is_annotated_type, + is_type_alias_type, + strip_annotated_type, +) +from ._compat import ( + PYDANTIC_V2, + ConfigDict, + GenericModel as BaseGenericModel, + get_args, + is_union, + parse_obj, + get_origin, + is_literal_type, + get_model_config, + get_model_fields, + field_get_default, +) +from ._constants import RAW_RESPONSE_HEADER + +if TYPE_CHECKING: + from pydantic_core.core_schema import ModelField, LiteralSchema, ModelFieldsSchema + +__all__ = ["BaseModel", "GenericModel"] + +_T = TypeVar("_T") +_BaseModelT = TypeVar("_BaseModelT", bound="BaseModel") + +P = ParamSpec("P") + + +@runtime_checkable +class _ConfigProtocol(Protocol): + allow_population_by_field_name: bool + + +class BaseModel(pydantic.BaseModel): + if PYDANTIC_V2: + model_config: ClassVar[ConfigDict] = ConfigDict( + extra="allow", defer_build=coerce_boolean(os.environ.get("DEFER_PYDANTIC_BUILD", "true")) + ) + else: + + @property + @override + def model_fields_set(self) -> set[str]: + # a forwards-compat shim for pydantic v2 + return self.__fields_set__ # type: ignore + + class Config(pydantic.BaseConfig): # pyright: ignore[reportDeprecated] + extra: Any = pydantic.Extra.allow # type: ignore + + def to_dict( + self, + *, + mode: Literal["json", "python"] = "python", + use_api_names: bool = True, + exclude_unset: bool = True, + exclude_defaults: bool = False, + exclude_none: bool = False, + warnings: bool = True, + ) -> dict[str, object]: + """Recursively generate a dictionary representation of the model, optionally specifying which fields to include or exclude. + + By default, fields that were not set by the API will not be included, + and keys will match the API response, *not* the property names from the model. + + For example, if the API responds with `"fooBar": true` but we've defined a `foo_bar: bool` property, + the output will use the `"fooBar"` key (unless `use_api_names=False` is passed). + + Args: + mode: + If mode is 'json', the dictionary will only contain JSON serializable types. e.g. `datetime` will be turned into a string, `"2024-3-22T18:11:19.117000Z"`. + If mode is 'python', the dictionary may contain any Python objects. e.g. `datetime(2024, 3, 22)` + + use_api_names: Whether to use the key that the API responded with or the property name. Defaults to `True`. + exclude_unset: Whether to exclude fields that have not been explicitly set. + exclude_defaults: Whether to exclude fields that are set to their default value from the output. + exclude_none: Whether to exclude fields that have a value of `None` from the output. + warnings: Whether to log warnings when invalid fields are encountered. This is only supported in Pydantic v2. + """ + return self.model_dump( + mode=mode, + by_alias=use_api_names, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + exclude_none=exclude_none, + warnings=warnings, + ) + + def to_json( + self, + *, + indent: int | None = 2, + use_api_names: bool = True, + exclude_unset: bool = True, + exclude_defaults: bool = False, + exclude_none: bool = False, + warnings: bool = True, + ) -> str: + """Generates a JSON string representing this model as it would be received from or sent to the API (but with indentation). + + By default, fields that were not set by the API will not be included, + and keys will match the API response, *not* the property names from the model. + + For example, if the API responds with `"fooBar": true` but we've defined a `foo_bar: bool` property, + the output will use the `"fooBar"` key (unless `use_api_names=False` is passed). + + Args: + indent: Indentation to use in the JSON output. If `None` is passed, the output will be compact. Defaults to `2` + use_api_names: Whether to use the key that the API responded with or the property name. Defaults to `True`. + exclude_unset: Whether to exclude fields that have not been explicitly set. + exclude_defaults: Whether to exclude fields that have the default value. + exclude_none: Whether to exclude fields that have a value of `None`. + warnings: Whether to show any warnings that occurred during serialization. This is only supported in Pydantic v2. + """ + return self.model_dump_json( + indent=indent, + by_alias=use_api_names, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + exclude_none=exclude_none, + warnings=warnings, + ) + + @override + def __str__(self) -> str: + # mypy complains about an invalid self arg + return f"{self.__repr_name__()}({self.__repr_str__(', ')})" # type: ignore[misc] + + # Override the 'construct' method in a way that supports recursive parsing without validation. + # Based on https://github.com/samuelcolvin/pydantic/issues/1168#issuecomment-817742836. + @classmethod + @override + def construct( # pyright: ignore[reportIncompatibleMethodOverride] + __cls: Type[ModelT], + _fields_set: set[str] | None = None, + **values: object, + ) -> ModelT: + m = __cls.__new__(__cls) + fields_values: dict[str, object] = {} + + config = get_model_config(__cls) + populate_by_name = ( + config.allow_population_by_field_name + if isinstance(config, _ConfigProtocol) + else config.get("populate_by_name") + ) + + if _fields_set is None: + _fields_set = set() + + model_fields = get_model_fields(__cls) + for name, field in model_fields.items(): + key = field.alias + if key is None or (key not in values and populate_by_name): + key = name + + if key in values: + fields_values[name] = _construct_field(value=values[key], field=field, key=key) + _fields_set.add(name) + else: + fields_values[name] = field_get_default(field) + + _extra = {} + for key, value in values.items(): + if key not in model_fields: + if PYDANTIC_V2: + _extra[key] = value + else: + _fields_set.add(key) + fields_values[key] = value + + object.__setattr__(m, "__dict__", fields_values) + + if PYDANTIC_V2: + # these properties are copied from Pydantic's `model_construct()` method + object.__setattr__(m, "__pydantic_private__", None) + object.__setattr__(m, "__pydantic_extra__", _extra) + object.__setattr__(m, "__pydantic_fields_set__", _fields_set) + else: + # init_private_attributes() does not exist in v2 + m._init_private_attributes() # type: ignore + + # copied from Pydantic v1's `construct()` method + object.__setattr__(m, "__fields_set__", _fields_set) + + return m + + if not TYPE_CHECKING: + # type checkers incorrectly complain about this assignment + # because the type signatures are technically different + # although not in practice + model_construct = construct + + if not PYDANTIC_V2: + # we define aliases for some of the new pydantic v2 methods so + # that we can just document these methods without having to specify + # a specific pydantic version as some users may not know which + # pydantic version they are currently using + + @override + def model_dump( + self, + *, + mode: Literal["json", "python"] | str = "python", + include: IncEx | None = None, + exclude: IncEx | None = None, + by_alias: bool = False, + exclude_unset: bool = False, + exclude_defaults: bool = False, + exclude_none: bool = False, + round_trip: bool = False, + warnings: bool | Literal["none", "warn", "error"] = True, + context: dict[str, Any] | None = None, + serialize_as_any: bool = False, + ) -> dict[str, Any]: + """Usage docs: https://docs.pydantic.dev/2.4/concepts/serialization/#modelmodel_dump + + Generate a dictionary representation of the model, optionally specifying which fields to include or exclude. + + Args: + mode: The mode in which `to_python` should run. + If mode is 'json', the dictionary will only contain JSON serializable types. + If mode is 'python', the dictionary may contain any Python objects. + include: A list of fields to include in the output. + exclude: A list of fields to exclude from the output. + by_alias: Whether to use the field's alias in the dictionary key if defined. + exclude_unset: Whether to exclude fields that are unset or None from the output. + exclude_defaults: Whether to exclude fields that are set to their default value from the output. + exclude_none: Whether to exclude fields that have a value of `None` from the output. + round_trip: Whether to enable serialization and deserialization round-trip support. + warnings: Whether to log warnings when invalid fields are encountered. + + Returns: + A dictionary representation of the model. + """ + if mode not in {"json", "python"}: + raise ValueError("mode must be either 'json' or 'python'") + if round_trip != False: + raise ValueError("round_trip is only supported in Pydantic v2") + if warnings != True: + raise ValueError("warnings is only supported in Pydantic v2") + if context is not None: + raise ValueError("context is only supported in Pydantic v2") + if serialize_as_any != False: + raise ValueError("serialize_as_any is only supported in Pydantic v2") + dumped = super().dict( # pyright: ignore[reportDeprecated] + include=include, + exclude=exclude, + by_alias=by_alias, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + exclude_none=exclude_none, + ) + + return cast(dict[str, Any], json_safe(dumped)) if mode == "json" else dumped + + @override + def model_dump_json( + self, + *, + indent: int | None = None, + include: IncEx | None = None, + exclude: IncEx | None = None, + by_alias: bool = False, + exclude_unset: bool = False, + exclude_defaults: bool = False, + exclude_none: bool = False, + round_trip: bool = False, + warnings: bool | Literal["none", "warn", "error"] = True, + context: dict[str, Any] | None = None, + serialize_as_any: bool = False, + ) -> str: + """Usage docs: https://docs.pydantic.dev/2.4/concepts/serialization/#modelmodel_dump_json + + Generates a JSON representation of the model using Pydantic's `to_json` method. + + Args: + indent: Indentation to use in the JSON output. If None is passed, the output will be compact. + include: Field(s) to include in the JSON output. Can take either a string or set of strings. + exclude: Field(s) to exclude from the JSON output. Can take either a string or set of strings. + by_alias: Whether to serialize using field aliases. + exclude_unset: Whether to exclude fields that have not been explicitly set. + exclude_defaults: Whether to exclude fields that have the default value. + exclude_none: Whether to exclude fields that have a value of `None`. + round_trip: Whether to use serialization/deserialization between JSON and class instance. + warnings: Whether to show any warnings that occurred during serialization. + + Returns: + A JSON string representation of the model. + """ + if round_trip != False: + raise ValueError("round_trip is only supported in Pydantic v2") + if warnings != True: + raise ValueError("warnings is only supported in Pydantic v2") + if context is not None: + raise ValueError("context is only supported in Pydantic v2") + if serialize_as_any != False: + raise ValueError("serialize_as_any is only supported in Pydantic v2") + return super().json( # type: ignore[reportDeprecated] + indent=indent, + include=include, + exclude=exclude, + by_alias=by_alias, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + exclude_none=exclude_none, + ) + + +def _construct_field(value: object, field: FieldInfo, key: str) -> object: + if value is None: + return field_get_default(field) + + if PYDANTIC_V2: + type_ = field.annotation + else: + type_ = cast(type, field.outer_type_) # type: ignore + + if type_ is None: + raise RuntimeError(f"Unexpected field type is None for {key}") + + return construct_type(value=value, type_=type_) + + +def is_basemodel(type_: type) -> bool: + """Returns whether or not the given type is either a `BaseModel` or a union of `BaseModel`""" + if is_union(type_): + for variant in get_args(type_): + if is_basemodel(variant): + return True + + return False + + return is_basemodel_type(type_) + + +def is_basemodel_type(type_: type) -> TypeGuard[type[BaseModel] | type[GenericModel]]: + origin = get_origin(type_) or type_ + if not inspect.isclass(origin): + return False + return issubclass(origin, BaseModel) or issubclass(origin, GenericModel) + + +def build( + base_model_cls: Callable[P, _BaseModelT], + *args: P.args, + **kwargs: P.kwargs, +) -> _BaseModelT: + """Construct a BaseModel class without validation. + + This is useful for cases where you need to instantiate a `BaseModel` + from an API response as this provides type-safe params which isn't supported + by helpers like `construct_type()`. + + ```py + build(MyModel, my_field_a="foo", my_field_b=123) + ``` + """ + if args: + raise TypeError( + "Received positional arguments which are not supported; Keyword arguments must be used instead", + ) + + return cast(_BaseModelT, construct_type(type_=base_model_cls, value=kwargs)) + + +def construct_type_unchecked(*, value: object, type_: type[_T]) -> _T: + """Loose coercion to the expected type with construction of nested values. + + Note: the returned value from this function is not guaranteed to match the + given type. + """ + return cast(_T, construct_type(value=value, type_=type_)) + + +def construct_type(*, value: object, type_: object) -> object: + """Loose coercion to the expected type with construction of nested values. + + If the given value does not match the expected type then it is returned as-is. + """ + + # store a reference to the original type we were given before we extract any inner + # types so that we can properly resolve forward references in `TypeAliasType` annotations + original_type = None + + # we allow `object` as the input type because otherwise, passing things like + # `Literal['value']` will be reported as a type error by type checkers + type_ = cast("type[object]", type_) + if is_type_alias_type(type_): + original_type = type_ # type: ignore[unreachable] + type_ = type_.__value__ # type: ignore[unreachable] + + # unwrap `Annotated[T, ...]` -> `T` + if is_annotated_type(type_): + meta: tuple[Any, ...] = get_args(type_)[1:] + type_ = extract_type_arg(type_, 0) + else: + meta = tuple() + + # we need to use the origin class for any types that are subscripted generics + # e.g. Dict[str, object] + origin = get_origin(type_) or type_ + args = get_args(type_) + + if is_union(origin): + try: + return validate_type(type_=cast("type[object]", original_type or type_), value=value) + except Exception: + pass + + # if the type is a discriminated union then we want to construct the right variant + # in the union, even if the data doesn't match exactly, otherwise we'd break code + # that relies on the constructed class types, e.g. + # + # class FooType: + # kind: Literal['foo'] + # value: str + # + # class BarType: + # kind: Literal['bar'] + # value: int + # + # without this block, if the data we get is something like `{'kind': 'bar', 'value': 'foo'}` then + # we'd end up constructing `FooType` when it should be `BarType`. + discriminator = _build_discriminated_union_meta(union=type_, meta_annotations=meta) + if discriminator and is_mapping(value): + variant_value = value.get(discriminator.field_alias_from or discriminator.field_name) + if variant_value and isinstance(variant_value, str): + variant_type = discriminator.mapping.get(variant_value) + if variant_type: + return construct_type(type_=variant_type, value=value) + + # if the data is not valid, use the first variant that doesn't fail while deserializing + for variant in args: + try: + return construct_type(value=value, type_=variant) + except Exception: + continue + + raise RuntimeError(f"Could not convert data into a valid instance of {type_}") + + if origin == dict: + if not is_mapping(value): + return value + + _, items_type = get_args(type_) # Dict[_, items_type] + return {key: construct_type(value=item, type_=items_type) for key, item in value.items()} + + if ( + not is_literal_type(type_) + and inspect.isclass(origin) + and (issubclass(origin, BaseModel) or issubclass(origin, GenericModel)) + ): + if is_list(value): + return [cast(Any, type_).construct(**entry) if is_mapping(entry) else entry for entry in value] + + if is_mapping(value): + if issubclass(type_, BaseModel): + return type_.construct(**value) # type: ignore[arg-type] + + return cast(Any, type_).construct(**value) + + if origin == list: + if not is_list(value): + return value + + inner_type = args[0] # List[inner_type] + return [construct_type(value=entry, type_=inner_type) for entry in value] + + if origin == float: + if isinstance(value, int): + coerced = float(value) + if coerced != value: + return value + return coerced + + return value + + if type_ == datetime: + try: + return parse_datetime(value) # type: ignore + except Exception: + return value + + if type_ == date: + try: + return parse_date(value) # type: ignore + except Exception: + return value + + return value + + +@runtime_checkable +class CachedDiscriminatorType(Protocol): + __discriminator__: DiscriminatorDetails + + +class DiscriminatorDetails: + field_name: str + """The name of the discriminator field in the variant class, e.g. + + ```py + class Foo(BaseModel): + type: Literal['foo'] + ``` + + Will result in field_name='type' + """ + + field_alias_from: str | None + """The name of the discriminator field in the API response, e.g. + + ```py + class Foo(BaseModel): + type: Literal['foo'] = Field(alias='type_from_api') + ``` + + Will result in field_alias_from='type_from_api' + """ + + mapping: dict[str, type] + """Mapping of discriminator value to variant type, e.g. + + {'foo': FooVariant, 'bar': BarVariant} + """ + + def __init__( + self, + *, + mapping: dict[str, type], + discriminator_field: str, + discriminator_alias: str | None, + ) -> None: + self.mapping = mapping + self.field_name = discriminator_field + self.field_alias_from = discriminator_alias + + +def _build_discriminated_union_meta(*, union: type, meta_annotations: tuple[Any, ...]) -> DiscriminatorDetails | None: + if isinstance(union, CachedDiscriminatorType): + return union.__discriminator__ + + discriminator_field_name: str | None = None + + for annotation in meta_annotations: + if isinstance(annotation, PropertyInfo) and annotation.discriminator is not None: + discriminator_field_name = annotation.discriminator + break + + if not discriminator_field_name: + return None + + mapping: dict[str, type] = {} + discriminator_alias: str | None = None + + for variant in get_args(union): + variant = strip_annotated_type(variant) + if is_basemodel_type(variant): + if PYDANTIC_V2: + field = _extract_field_schema_pv2(variant, discriminator_field_name) + if not field: + continue + + # Note: if one variant defines an alias then they all should + discriminator_alias = field.get("serialization_alias") + + field_schema = field["schema"] + + if field_schema["type"] == "literal": + for entry in cast("LiteralSchema", field_schema)["expected"]: + if isinstance(entry, str): + mapping[entry] = variant + else: + field_info = cast("dict[str, FieldInfo]", variant.__fields__).get(discriminator_field_name) # pyright: ignore[reportDeprecated, reportUnnecessaryCast] + if not field_info: + continue + + # Note: if one variant defines an alias then they all should + discriminator_alias = field_info.alias + + if field_info.annotation and is_literal_type(field_info.annotation): + for entry in get_args(field_info.annotation): + if isinstance(entry, str): + mapping[entry] = variant + + if not mapping: + return None + + details = DiscriminatorDetails( + mapping=mapping, + discriminator_field=discriminator_field_name, + discriminator_alias=discriminator_alias, + ) + cast(CachedDiscriminatorType, union).__discriminator__ = details + return details + + +def _extract_field_schema_pv2(model: type[BaseModel], field_name: str) -> ModelField | None: + schema = model.__pydantic_core_schema__ + if schema["type"] != "model": + return None + + fields_schema = schema["schema"] + if fields_schema["type"] != "model-fields": + return None + + fields_schema = cast("ModelFieldsSchema", fields_schema) + + field = fields_schema["fields"].get(field_name) + if not field: + return None + + return cast("ModelField", field) # pyright: ignore[reportUnnecessaryCast] + + +def validate_type(*, type_: type[_T], value: object) -> _T: + """Strict validation that the given value matches the expected type""" + if inspect.isclass(type_) and issubclass(type_, pydantic.BaseModel): + return cast(_T, parse_obj(type_, value)) + + return cast(_T, _validate_non_model_type(type_=type_, value=value)) + + +def set_pydantic_config(typ: Any, config: pydantic.ConfigDict) -> None: + """Add a pydantic config for the given type. + + Note: this is a no-op on Pydantic v1. + """ + setattr(typ, "__pydantic_config__", config) # noqa: B010 + + +# our use of subclasssing here causes weirdness for type checkers, +# so we just pretend that we don't subclass +if TYPE_CHECKING: + GenericModel = BaseModel +else: + + class GenericModel(BaseGenericModel, BaseModel): + pass + + +if PYDANTIC_V2: + from pydantic import TypeAdapter as _TypeAdapter + + _CachedTypeAdapter = cast("TypeAdapter[object]", lru_cache(maxsize=None)(_TypeAdapter)) + + if TYPE_CHECKING: + from pydantic import TypeAdapter + else: + TypeAdapter = _CachedTypeAdapter + + def _validate_non_model_type(*, type_: type[_T], value: object) -> _T: + return TypeAdapter(type_).validate_python(value) + +elif not TYPE_CHECKING: # TODO: condition is weird + + class RootModel(GenericModel, Generic[_T]): + """Used as a placeholder to easily convert runtime types to a Pydantic format + to provide validation. + + For example: + ```py + validated = RootModel[int](__root__="5").__root__ + # validated: 5 + ``` + """ + + __root__: _T + + def _validate_non_model_type(*, type_: type[_T], value: object) -> _T: + model = _create_pydantic_model(type_).validate(value) + return cast(_T, model.__root__) + + def _create_pydantic_model(type_: _T) -> Type[RootModel[_T]]: + return RootModel[type_] # type: ignore + + +class FinalRequestOptionsInput(TypedDict, total=False): + method: Required[str] + url: Required[str] + params: Query + headers: Headers + max_retries: int + timeout: float | Timeout | None + files: HttpxRequestFiles | None + idempotency_key: str + json_data: Body + extra_json: AnyMapping + + +@final +class FinalRequestOptions(pydantic.BaseModel): + method: str + url: str + params: Query = {} + headers: Union[Headers, NotGiven] = NotGiven() + max_retries: Union[int, NotGiven] = NotGiven() + timeout: Union[float, Timeout, None, NotGiven] = NotGiven() + files: Union[HttpxRequestFiles, None] = None + idempotency_key: Union[str, None] = None + post_parser: Union[Callable[[Any], Any], NotGiven] = NotGiven() + + # It should be noted that we cannot use `json` here as that would override + # a BaseModel method in an incompatible fashion. + json_data: Union[Body, None] = None + extra_json: Union[AnyMapping, None] = None + + if PYDANTIC_V2: + model_config: ClassVar[ConfigDict] = ConfigDict(arbitrary_types_allowed=True) + else: + + class Config(pydantic.BaseConfig): # pyright: ignore[reportDeprecated] + arbitrary_types_allowed: bool = True + + def get_max_retries(self, max_retries: int) -> int: + if isinstance(self.max_retries, NotGiven): + return max_retries + return self.max_retries + + def _strip_raw_response_header(self) -> None: + if not is_given(self.headers): + return + + if self.headers.get(RAW_RESPONSE_HEADER): + self.headers = {**self.headers} + self.headers.pop(RAW_RESPONSE_HEADER) + + # override the `construct` method so that we can run custom transformations. + # this is necessary as we don't want to do any actual runtime type checking + # (which means we can't use validators) but we do want to ensure that `NotGiven` + # values are not present + # + # type ignore required because we're adding explicit types to `**values` + @classmethod + def construct( # type: ignore + cls, + _fields_set: set[str] | None = None, + **values: Unpack[FinalRequestOptionsInput], + ) -> FinalRequestOptions: + kwargs: dict[str, Any] = { + # we unconditionally call `strip_not_given` on any value + # as it will just ignore any non-mapping types + key: strip_not_given(value) + for key, value in values.items() + } + if PYDANTIC_V2: + return super().model_construct(_fields_set, **kwargs) + return cast(FinalRequestOptions, super().construct(_fields_set, **kwargs)) # pyright: ignore[reportDeprecated] + + if not TYPE_CHECKING: + # type checkers incorrectly complain about this assignment + model_construct = construct diff --git a/src/postgrid/_qs.py b/src/postgrid/_qs.py new file mode 100644 index 0000000..274320c --- /dev/null +++ b/src/postgrid/_qs.py @@ -0,0 +1,150 @@ +from __future__ import annotations + +from typing import Any, List, Tuple, Union, Mapping, TypeVar +from urllib.parse import parse_qs, urlencode +from typing_extensions import Literal, get_args + +from ._types import NOT_GIVEN, NotGiven, NotGivenOr +from ._utils import flatten + +_T = TypeVar("_T") + + +ArrayFormat = Literal["comma", "repeat", "indices", "brackets"] +NestedFormat = Literal["dots", "brackets"] + +PrimitiveData = Union[str, int, float, bool, None] +# this should be Data = Union[PrimitiveData, "List[Data]", "Tuple[Data]", "Mapping[str, Data]"] +# https://github.com/microsoft/pyright/issues/3555 +Data = Union[PrimitiveData, List[Any], Tuple[Any], "Mapping[str, Any]"] +Params = Mapping[str, Data] + + +class Querystring: + array_format: ArrayFormat + nested_format: NestedFormat + + def __init__( + self, + *, + array_format: ArrayFormat = "repeat", + nested_format: NestedFormat = "brackets", + ) -> None: + self.array_format = array_format + self.nested_format = nested_format + + def parse(self, query: str) -> Mapping[str, object]: + # Note: custom format syntax is not supported yet + return parse_qs(query) + + def stringify( + self, + params: Params, + *, + array_format: NotGivenOr[ArrayFormat] = NOT_GIVEN, + nested_format: NotGivenOr[NestedFormat] = NOT_GIVEN, + ) -> str: + return urlencode( + self.stringify_items( + params, + array_format=array_format, + nested_format=nested_format, + ) + ) + + def stringify_items( + self, + params: Params, + *, + array_format: NotGivenOr[ArrayFormat] = NOT_GIVEN, + nested_format: NotGivenOr[NestedFormat] = NOT_GIVEN, + ) -> list[tuple[str, str]]: + opts = Options( + qs=self, + array_format=array_format, + nested_format=nested_format, + ) + return flatten([self._stringify_item(key, value, opts) for key, value in params.items()]) + + def _stringify_item( + self, + key: str, + value: Data, + opts: Options, + ) -> list[tuple[str, str]]: + if isinstance(value, Mapping): + items: list[tuple[str, str]] = [] + nested_format = opts.nested_format + for subkey, subvalue in value.items(): + items.extend( + self._stringify_item( + # TODO: error if unknown format + f"{key}.{subkey}" if nested_format == "dots" else f"{key}[{subkey}]", + subvalue, + opts, + ) + ) + return items + + if isinstance(value, (list, tuple)): + array_format = opts.array_format + if array_format == "comma": + return [ + ( + key, + ",".join(self._primitive_value_to_str(item) for item in value if item is not None), + ), + ] + elif array_format == "repeat": + items = [] + for item in value: + items.extend(self._stringify_item(key, item, opts)) + return items + elif array_format == "indices": + raise NotImplementedError("The array indices format is not supported yet") + elif array_format == "brackets": + items = [] + key = key + "[]" + for item in value: + items.extend(self._stringify_item(key, item, opts)) + return items + else: + raise NotImplementedError( + f"Unknown array_format value: {array_format}, choose from {', '.join(get_args(ArrayFormat))}" + ) + + serialised = self._primitive_value_to_str(value) + if not serialised: + return [] + return [(key, serialised)] + + def _primitive_value_to_str(self, value: PrimitiveData) -> str: + # copied from httpx + if value is True: + return "true" + elif value is False: + return "false" + elif value is None: + return "" + return str(value) + + +_qs = Querystring() +parse = _qs.parse +stringify = _qs.stringify +stringify_items = _qs.stringify_items + + +class Options: + array_format: ArrayFormat + nested_format: NestedFormat + + def __init__( + self, + qs: Querystring = _qs, + *, + array_format: NotGivenOr[ArrayFormat] = NOT_GIVEN, + nested_format: NotGivenOr[NestedFormat] = NOT_GIVEN, + ) -> None: + self.array_format = qs.array_format if isinstance(array_format, NotGiven) else array_format + self.nested_format = qs.nested_format if isinstance(nested_format, NotGiven) else nested_format diff --git a/src/postgrid/_resource.py b/src/postgrid/_resource.py new file mode 100644 index 0000000..0a80c93 --- /dev/null +++ b/src/postgrid/_resource.py @@ -0,0 +1,43 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import time +from typing import TYPE_CHECKING + +import anyio + +if TYPE_CHECKING: + from ._client import PostGrid, AsyncPostGrid + + +class SyncAPIResource: + _client: PostGrid + + def __init__(self, client: PostGrid) -> None: + self._client = client + self._get = client.get + self._post = client.post + self._patch = client.patch + self._put = client.put + self._delete = client.delete + self._get_api_list = client.get_api_list + + def _sleep(self, seconds: float) -> None: + time.sleep(seconds) + + +class AsyncAPIResource: + _client: AsyncPostGrid + + def __init__(self, client: AsyncPostGrid) -> None: + self._client = client + self._get = client.get + self._post = client.post + self._patch = client.patch + self._put = client.put + self._delete = client.delete + self._get_api_list = client.get_api_list + + async def _sleep(self, seconds: float) -> None: + await anyio.sleep(seconds) diff --git a/src/postgrid/_response.py b/src/postgrid/_response.py new file mode 100644 index 0000000..2fa48d8 --- /dev/null +++ b/src/postgrid/_response.py @@ -0,0 +1,830 @@ +from __future__ import annotations + +import os +import inspect +import logging +import datetime +import functools +from types import TracebackType +from typing import ( + TYPE_CHECKING, + Any, + Union, + Generic, + TypeVar, + Callable, + Iterator, + AsyncIterator, + cast, + overload, +) +from typing_extensions import Awaitable, ParamSpec, override, get_origin + +import anyio +import httpx +import pydantic + +from ._types import NoneType +from ._utils import is_given, extract_type_arg, is_annotated_type, is_type_alias_type, extract_type_var_from_base +from ._models import BaseModel, is_basemodel +from ._constants import RAW_RESPONSE_HEADER, OVERRIDE_CAST_TO_HEADER +from ._streaming import Stream, AsyncStream, is_stream_class_type, extract_stream_chunk_type +from ._exceptions import PostGridError, APIResponseValidationError + +if TYPE_CHECKING: + from ._models import FinalRequestOptions + from ._base_client import BaseClient + + +P = ParamSpec("P") +R = TypeVar("R") +_T = TypeVar("_T") +_APIResponseT = TypeVar("_APIResponseT", bound="APIResponse[Any]") +_AsyncAPIResponseT = TypeVar("_AsyncAPIResponseT", bound="AsyncAPIResponse[Any]") + +log: logging.Logger = logging.getLogger(__name__) + + +class BaseAPIResponse(Generic[R]): + _cast_to: type[R] + _client: BaseClient[Any, Any] + _parsed_by_type: dict[type[Any], Any] + _is_sse_stream: bool + _stream_cls: type[Stream[Any]] | type[AsyncStream[Any]] | None + _options: FinalRequestOptions + + http_response: httpx.Response + + retries_taken: int + """The number of retries made. If no retries happened this will be `0`""" + + def __init__( + self, + *, + raw: httpx.Response, + cast_to: type[R], + client: BaseClient[Any, Any], + stream: bool, + stream_cls: type[Stream[Any]] | type[AsyncStream[Any]] | None, + options: FinalRequestOptions, + retries_taken: int = 0, + ) -> None: + self._cast_to = cast_to + self._client = client + self._parsed_by_type = {} + self._is_sse_stream = stream + self._stream_cls = stream_cls + self._options = options + self.http_response = raw + self.retries_taken = retries_taken + + @property + def headers(self) -> httpx.Headers: + return self.http_response.headers + + @property + def http_request(self) -> httpx.Request: + """Returns the httpx Request instance associated with the current response.""" + return self.http_response.request + + @property + def status_code(self) -> int: + return self.http_response.status_code + + @property + def url(self) -> httpx.URL: + """Returns the URL for which the request was made.""" + return self.http_response.url + + @property + def method(self) -> str: + return self.http_request.method + + @property + def http_version(self) -> str: + return self.http_response.http_version + + @property + def elapsed(self) -> datetime.timedelta: + """The time taken for the complete request/response cycle to complete.""" + return self.http_response.elapsed + + @property + def is_closed(self) -> bool: + """Whether or not the response body has been closed. + + If this is False then there is response data that has not been read yet. + You must either fully consume the response body or call `.close()` + before discarding the response to prevent resource leaks. + """ + return self.http_response.is_closed + + @override + def __repr__(self) -> str: + return ( + f"<{self.__class__.__name__} [{self.status_code} {self.http_response.reason_phrase}] type={self._cast_to}>" + ) + + def _parse(self, *, to: type[_T] | None = None) -> R | _T: + cast_to = to if to is not None else self._cast_to + + # unwrap `TypeAlias('Name', T)` -> `T` + if is_type_alias_type(cast_to): + cast_to = cast_to.__value__ # type: ignore[unreachable] + + # unwrap `Annotated[T, ...]` -> `T` + if cast_to and is_annotated_type(cast_to): + cast_to = extract_type_arg(cast_to, 0) + + origin = get_origin(cast_to) or cast_to + + if self._is_sse_stream: + if to: + if not is_stream_class_type(to): + raise TypeError(f"Expected custom parse type to be a subclass of {Stream} or {AsyncStream}") + + return cast( + _T, + to( + cast_to=extract_stream_chunk_type( + to, + failure_message="Expected custom stream type to be passed with a type argument, e.g. Stream[ChunkType]", + ), + response=self.http_response, + client=cast(Any, self._client), + ), + ) + + if self._stream_cls: + return cast( + R, + self._stream_cls( + cast_to=extract_stream_chunk_type(self._stream_cls), + response=self.http_response, + client=cast(Any, self._client), + ), + ) + + stream_cls = cast("type[Stream[Any]] | type[AsyncStream[Any]] | None", self._client._default_stream_cls) + if stream_cls is None: + raise MissingStreamClassError() + + return cast( + R, + stream_cls( + cast_to=cast_to, + response=self.http_response, + client=cast(Any, self._client), + ), + ) + + if cast_to is NoneType: + return cast(R, None) + + response = self.http_response + if cast_to == str: + return cast(R, response.text) + + if cast_to == bytes: + return cast(R, response.content) + + if cast_to == int: + return cast(R, int(response.text)) + + if cast_to == float: + return cast(R, float(response.text)) + + if cast_to == bool: + return cast(R, response.text.lower() == "true") + + if origin == APIResponse: + raise RuntimeError("Unexpected state - cast_to is `APIResponse`") + + if inspect.isclass(origin) and issubclass(origin, httpx.Response): + # Because of the invariance of our ResponseT TypeVar, users can subclass httpx.Response + # and pass that class to our request functions. We cannot change the variance to be either + # covariant or contravariant as that makes our usage of ResponseT illegal. We could construct + # the response class ourselves but that is something that should be supported directly in httpx + # as it would be easy to incorrectly construct the Response object due to the multitude of arguments. + if cast_to != httpx.Response: + raise ValueError(f"Subclasses of httpx.Response cannot be passed to `cast_to`") + return cast(R, response) + + if ( + inspect.isclass( + origin # pyright: ignore[reportUnknownArgumentType] + ) + and not issubclass(origin, BaseModel) + and issubclass(origin, pydantic.BaseModel) + ): + raise TypeError("Pydantic models must subclass our base model type, e.g. `from postgrid import BaseModel`") + + if ( + cast_to is not object + and not origin is list + and not origin is dict + and not origin is Union + and not issubclass(origin, BaseModel) + ): + raise RuntimeError( + f"Unsupported type, expected {cast_to} to be a subclass of {BaseModel}, {dict}, {list}, {Union}, {NoneType}, {str} or {httpx.Response}." + ) + + # split is required to handle cases where additional information is included + # in the response, e.g. application/json; charset=utf-8 + content_type, *_ = response.headers.get("content-type", "*").split(";") + if content_type != "application/json": + if is_basemodel(cast_to): + try: + data = response.json() + except Exception as exc: + log.debug("Could not read JSON from response data due to %s - %s", type(exc), exc) + else: + return self._client._process_response_data( + data=data, + cast_to=cast_to, # type: ignore + response=response, + ) + + if self._client._strict_response_validation: + raise APIResponseValidationError( + response=response, + message=f"Expected Content-Type response header to be `application/json` but received `{content_type}` instead.", + body=response.text, + ) + + # If the API responds with content that isn't JSON then we just return + # the (decoded) text without performing any parsing so that you can still + # handle the response however you need to. + return response.text # type: ignore + + data = response.json() + + return self._client._process_response_data( + data=data, + cast_to=cast_to, # type: ignore + response=response, + ) + + +class APIResponse(BaseAPIResponse[R]): + @overload + def parse(self, *, to: type[_T]) -> _T: ... + + @overload + def parse(self) -> R: ... + + def parse(self, *, to: type[_T] | None = None) -> R | _T: + """Returns the rich python representation of this response's data. + + For lower-level control, see `.read()`, `.json()`, `.iter_bytes()`. + + You can customise the type that the response is parsed into through + the `to` argument, e.g. + + ```py + from postgrid import BaseModel + + + class MyModel(BaseModel): + foo: str + + + obj = response.parse(to=MyModel) + print(obj.foo) + ``` + + We support parsing: + - `BaseModel` + - `dict` + - `list` + - `Union` + - `str` + - `int` + - `float` + - `httpx.Response` + """ + cache_key = to if to is not None else self._cast_to + cached = self._parsed_by_type.get(cache_key) + if cached is not None: + return cached # type: ignore[no-any-return] + + if not self._is_sse_stream: + self.read() + + parsed = self._parse(to=to) + if is_given(self._options.post_parser): + parsed = self._options.post_parser(parsed) + + self._parsed_by_type[cache_key] = parsed + return parsed + + def read(self) -> bytes: + """Read and return the binary response content.""" + try: + return self.http_response.read() + except httpx.StreamConsumed as exc: + # The default error raised by httpx isn't very + # helpful in our case so we re-raise it with + # a different error message. + raise StreamAlreadyConsumed() from exc + + def text(self) -> str: + """Read and decode the response content into a string.""" + self.read() + return self.http_response.text + + def json(self) -> object: + """Read and decode the JSON response content.""" + self.read() + return self.http_response.json() + + def close(self) -> None: + """Close the response and release the connection. + + Automatically called if the response body is read to completion. + """ + self.http_response.close() + + def iter_bytes(self, chunk_size: int | None = None) -> Iterator[bytes]: + """ + A byte-iterator over the decoded response content. + + This automatically handles gzip, deflate and brotli encoded responses. + """ + for chunk in self.http_response.iter_bytes(chunk_size): + yield chunk + + def iter_text(self, chunk_size: int | None = None) -> Iterator[str]: + """A str-iterator over the decoded response content + that handles both gzip, deflate, etc but also detects the content's + string encoding. + """ + for chunk in self.http_response.iter_text(chunk_size): + yield chunk + + def iter_lines(self) -> Iterator[str]: + """Like `iter_text()` but will only yield chunks for each line""" + for chunk in self.http_response.iter_lines(): + yield chunk + + +class AsyncAPIResponse(BaseAPIResponse[R]): + @overload + async def parse(self, *, to: type[_T]) -> _T: ... + + @overload + async def parse(self) -> R: ... + + async def parse(self, *, to: type[_T] | None = None) -> R | _T: + """Returns the rich python representation of this response's data. + + For lower-level control, see `.read()`, `.json()`, `.iter_bytes()`. + + You can customise the type that the response is parsed into through + the `to` argument, e.g. + + ```py + from postgrid import BaseModel + + + class MyModel(BaseModel): + foo: str + + + obj = response.parse(to=MyModel) + print(obj.foo) + ``` + + We support parsing: + - `BaseModel` + - `dict` + - `list` + - `Union` + - `str` + - `httpx.Response` + """ + cache_key = to if to is not None else self._cast_to + cached = self._parsed_by_type.get(cache_key) + if cached is not None: + return cached # type: ignore[no-any-return] + + if not self._is_sse_stream: + await self.read() + + parsed = self._parse(to=to) + if is_given(self._options.post_parser): + parsed = self._options.post_parser(parsed) + + self._parsed_by_type[cache_key] = parsed + return parsed + + async def read(self) -> bytes: + """Read and return the binary response content.""" + try: + return await self.http_response.aread() + except httpx.StreamConsumed as exc: + # the default error raised by httpx isn't very + # helpful in our case so we re-raise it with + # a different error message + raise StreamAlreadyConsumed() from exc + + async def text(self) -> str: + """Read and decode the response content into a string.""" + await self.read() + return self.http_response.text + + async def json(self) -> object: + """Read and decode the JSON response content.""" + await self.read() + return self.http_response.json() + + async def close(self) -> None: + """Close the response and release the connection. + + Automatically called if the response body is read to completion. + """ + await self.http_response.aclose() + + async def iter_bytes(self, chunk_size: int | None = None) -> AsyncIterator[bytes]: + """ + A byte-iterator over the decoded response content. + + This automatically handles gzip, deflate and brotli encoded responses. + """ + async for chunk in self.http_response.aiter_bytes(chunk_size): + yield chunk + + async def iter_text(self, chunk_size: int | None = None) -> AsyncIterator[str]: + """A str-iterator over the decoded response content + that handles both gzip, deflate, etc but also detects the content's + string encoding. + """ + async for chunk in self.http_response.aiter_text(chunk_size): + yield chunk + + async def iter_lines(self) -> AsyncIterator[str]: + """Like `iter_text()` but will only yield chunks for each line""" + async for chunk in self.http_response.aiter_lines(): + yield chunk + + +class BinaryAPIResponse(APIResponse[bytes]): + """Subclass of APIResponse providing helpers for dealing with binary data. + + Note: If you want to stream the response data instead of eagerly reading it + all at once then you should use `.with_streaming_response` when making + the API request, e.g. `.with_streaming_response.get_binary_response()` + """ + + def write_to_file( + self, + file: str | os.PathLike[str], + ) -> None: + """Write the output to the given file. + + Accepts a filename or any path-like object, e.g. pathlib.Path + + Note: if you want to stream the data to the file instead of writing + all at once then you should use `.with_streaming_response` when making + the API request, e.g. `.with_streaming_response.get_binary_response()` + """ + with open(file, mode="wb") as f: + for data in self.iter_bytes(): + f.write(data) + + +class AsyncBinaryAPIResponse(AsyncAPIResponse[bytes]): + """Subclass of APIResponse providing helpers for dealing with binary data. + + Note: If you want to stream the response data instead of eagerly reading it + all at once then you should use `.with_streaming_response` when making + the API request, e.g. `.with_streaming_response.get_binary_response()` + """ + + async def write_to_file( + self, + file: str | os.PathLike[str], + ) -> None: + """Write the output to the given file. + + Accepts a filename or any path-like object, e.g. pathlib.Path + + Note: if you want to stream the data to the file instead of writing + all at once then you should use `.with_streaming_response` when making + the API request, e.g. `.with_streaming_response.get_binary_response()` + """ + path = anyio.Path(file) + async with await path.open(mode="wb") as f: + async for data in self.iter_bytes(): + await f.write(data) + + +class StreamedBinaryAPIResponse(APIResponse[bytes]): + def stream_to_file( + self, + file: str | os.PathLike[str], + *, + chunk_size: int | None = None, + ) -> None: + """Streams the output to the given file. + + Accepts a filename or any path-like object, e.g. pathlib.Path + """ + with open(file, mode="wb") as f: + for data in self.iter_bytes(chunk_size): + f.write(data) + + +class AsyncStreamedBinaryAPIResponse(AsyncAPIResponse[bytes]): + async def stream_to_file( + self, + file: str | os.PathLike[str], + *, + chunk_size: int | None = None, + ) -> None: + """Streams the output to the given file. + + Accepts a filename or any path-like object, e.g. pathlib.Path + """ + path = anyio.Path(file) + async with await path.open(mode="wb") as f: + async for data in self.iter_bytes(chunk_size): + await f.write(data) + + +class MissingStreamClassError(TypeError): + def __init__(self) -> None: + super().__init__( + "The `stream` argument was set to `True` but the `stream_cls` argument was not given. See `postgrid._streaming` for reference", + ) + + +class StreamAlreadyConsumed(PostGridError): + """ + Attempted to read or stream content, but the content has already + been streamed. + + This can happen if you use a method like `.iter_lines()` and then attempt + to read th entire response body afterwards, e.g. + + ```py + response = await client.post(...) + async for line in response.iter_lines(): + ... # do something with `line` + + content = await response.read() + # ^ error + ``` + + If you want this behaviour you'll need to either manually accumulate the response + content or call `await response.read()` before iterating over the stream. + """ + + def __init__(self) -> None: + message = ( + "Attempted to read or stream some content, but the content has " + "already been streamed. " + "This could be due to attempting to stream the response " + "content more than once." + "\n\n" + "You can fix this by manually accumulating the response content while streaming " + "or by calling `.read()` before starting to stream." + ) + super().__init__(message) + + +class ResponseContextManager(Generic[_APIResponseT]): + """Context manager for ensuring that a request is not made + until it is entered and that the response will always be closed + when the context manager exits + """ + + def __init__(self, request_func: Callable[[], _APIResponseT]) -> None: + self._request_func = request_func + self.__response: _APIResponseT | None = None + + def __enter__(self) -> _APIResponseT: + self.__response = self._request_func() + return self.__response + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + if self.__response is not None: + self.__response.close() + + +class AsyncResponseContextManager(Generic[_AsyncAPIResponseT]): + """Context manager for ensuring that a request is not made + until it is entered and that the response will always be closed + when the context manager exits + """ + + def __init__(self, api_request: Awaitable[_AsyncAPIResponseT]) -> None: + self._api_request = api_request + self.__response: _AsyncAPIResponseT | None = None + + async def __aenter__(self) -> _AsyncAPIResponseT: + self.__response = await self._api_request + return self.__response + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + if self.__response is not None: + await self.__response.close() + + +def to_streamed_response_wrapper(func: Callable[P, R]) -> Callable[P, ResponseContextManager[APIResponse[R]]]: + """Higher order function that takes one of our bound API methods and wraps it + to support streaming and returning the raw `APIResponse` object directly. + """ + + @functools.wraps(func) + def wrapped(*args: P.args, **kwargs: P.kwargs) -> ResponseContextManager[APIResponse[R]]: + extra_headers: dict[str, str] = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers[RAW_RESPONSE_HEADER] = "stream" + + kwargs["extra_headers"] = extra_headers + + make_request = functools.partial(func, *args, **kwargs) + + return ResponseContextManager(cast(Callable[[], APIResponse[R]], make_request)) + + return wrapped + + +def async_to_streamed_response_wrapper( + func: Callable[P, Awaitable[R]], +) -> Callable[P, AsyncResponseContextManager[AsyncAPIResponse[R]]]: + """Higher order function that takes one of our bound API methods and wraps it + to support streaming and returning the raw `APIResponse` object directly. + """ + + @functools.wraps(func) + def wrapped(*args: P.args, **kwargs: P.kwargs) -> AsyncResponseContextManager[AsyncAPIResponse[R]]: + extra_headers: dict[str, str] = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers[RAW_RESPONSE_HEADER] = "stream" + + kwargs["extra_headers"] = extra_headers + + make_request = func(*args, **kwargs) + + return AsyncResponseContextManager(cast(Awaitable[AsyncAPIResponse[R]], make_request)) + + return wrapped + + +def to_custom_streamed_response_wrapper( + func: Callable[P, object], + response_cls: type[_APIResponseT], +) -> Callable[P, ResponseContextManager[_APIResponseT]]: + """Higher order function that takes one of our bound API methods and an `APIResponse` class + and wraps the method to support streaming and returning the given response class directly. + + Note: the given `response_cls` *must* be concrete, e.g. `class BinaryAPIResponse(APIResponse[bytes])` + """ + + @functools.wraps(func) + def wrapped(*args: P.args, **kwargs: P.kwargs) -> ResponseContextManager[_APIResponseT]: + extra_headers: dict[str, Any] = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers[RAW_RESPONSE_HEADER] = "stream" + extra_headers[OVERRIDE_CAST_TO_HEADER] = response_cls + + kwargs["extra_headers"] = extra_headers + + make_request = functools.partial(func, *args, **kwargs) + + return ResponseContextManager(cast(Callable[[], _APIResponseT], make_request)) + + return wrapped + + +def async_to_custom_streamed_response_wrapper( + func: Callable[P, Awaitable[object]], + response_cls: type[_AsyncAPIResponseT], +) -> Callable[P, AsyncResponseContextManager[_AsyncAPIResponseT]]: + """Higher order function that takes one of our bound API methods and an `APIResponse` class + and wraps the method to support streaming and returning the given response class directly. + + Note: the given `response_cls` *must* be concrete, e.g. `class BinaryAPIResponse(APIResponse[bytes])` + """ + + @functools.wraps(func) + def wrapped(*args: P.args, **kwargs: P.kwargs) -> AsyncResponseContextManager[_AsyncAPIResponseT]: + extra_headers: dict[str, Any] = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers[RAW_RESPONSE_HEADER] = "stream" + extra_headers[OVERRIDE_CAST_TO_HEADER] = response_cls + + kwargs["extra_headers"] = extra_headers + + make_request = func(*args, **kwargs) + + return AsyncResponseContextManager(cast(Awaitable[_AsyncAPIResponseT], make_request)) + + return wrapped + + +def to_raw_response_wrapper(func: Callable[P, R]) -> Callable[P, APIResponse[R]]: + """Higher order function that takes one of our bound API methods and wraps it + to support returning the raw `APIResponse` object directly. + """ + + @functools.wraps(func) + def wrapped(*args: P.args, **kwargs: P.kwargs) -> APIResponse[R]: + extra_headers: dict[str, str] = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers[RAW_RESPONSE_HEADER] = "raw" + + kwargs["extra_headers"] = extra_headers + + return cast(APIResponse[R], func(*args, **kwargs)) + + return wrapped + + +def async_to_raw_response_wrapper(func: Callable[P, Awaitable[R]]) -> Callable[P, Awaitable[AsyncAPIResponse[R]]]: + """Higher order function that takes one of our bound API methods and wraps it + to support returning the raw `APIResponse` object directly. + """ + + @functools.wraps(func) + async def wrapped(*args: P.args, **kwargs: P.kwargs) -> AsyncAPIResponse[R]: + extra_headers: dict[str, str] = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers[RAW_RESPONSE_HEADER] = "raw" + + kwargs["extra_headers"] = extra_headers + + return cast(AsyncAPIResponse[R], await func(*args, **kwargs)) + + return wrapped + + +def to_custom_raw_response_wrapper( + func: Callable[P, object], + response_cls: type[_APIResponseT], +) -> Callable[P, _APIResponseT]: + """Higher order function that takes one of our bound API methods and an `APIResponse` class + and wraps the method to support returning the given response class directly. + + Note: the given `response_cls` *must* be concrete, e.g. `class BinaryAPIResponse(APIResponse[bytes])` + """ + + @functools.wraps(func) + def wrapped(*args: P.args, **kwargs: P.kwargs) -> _APIResponseT: + extra_headers: dict[str, Any] = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers[RAW_RESPONSE_HEADER] = "raw" + extra_headers[OVERRIDE_CAST_TO_HEADER] = response_cls + + kwargs["extra_headers"] = extra_headers + + return cast(_APIResponseT, func(*args, **kwargs)) + + return wrapped + + +def async_to_custom_raw_response_wrapper( + func: Callable[P, Awaitable[object]], + response_cls: type[_AsyncAPIResponseT], +) -> Callable[P, Awaitable[_AsyncAPIResponseT]]: + """Higher order function that takes one of our bound API methods and an `APIResponse` class + and wraps the method to support returning the given response class directly. + + Note: the given `response_cls` *must* be concrete, e.g. `class BinaryAPIResponse(APIResponse[bytes])` + """ + + @functools.wraps(func) + def wrapped(*args: P.args, **kwargs: P.kwargs) -> Awaitable[_AsyncAPIResponseT]: + extra_headers: dict[str, Any] = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers[RAW_RESPONSE_HEADER] = "raw" + extra_headers[OVERRIDE_CAST_TO_HEADER] = response_cls + + kwargs["extra_headers"] = extra_headers + + return cast(Awaitable[_AsyncAPIResponseT], func(*args, **kwargs)) + + return wrapped + + +def extract_response_type(typ: type[BaseAPIResponse[Any]]) -> type: + """Given a type like `APIResponse[T]`, returns the generic type variable `T`. + + This also handles the case where a concrete subclass is given, e.g. + ```py + class MyResponse(APIResponse[bytes]): + ... + + extract_response_type(MyResponse) -> bytes + ``` + """ + return extract_type_var_from_base( + typ, + generic_bases=cast("tuple[type, ...]", (BaseAPIResponse, APIResponse, AsyncAPIResponse)), + index=0, + ) diff --git a/src/postgrid/_streaming.py b/src/postgrid/_streaming.py new file mode 100644 index 0000000..25941e4 --- /dev/null +++ b/src/postgrid/_streaming.py @@ -0,0 +1,333 @@ +# Note: initially copied from https://github.com/florimondmanca/httpx-sse/blob/master/src/httpx_sse/_decoders.py +from __future__ import annotations + +import json +import inspect +from types import TracebackType +from typing import TYPE_CHECKING, Any, Generic, TypeVar, Iterator, AsyncIterator, cast +from typing_extensions import Self, Protocol, TypeGuard, override, get_origin, runtime_checkable + +import httpx + +from ._utils import extract_type_var_from_base + +if TYPE_CHECKING: + from ._client import PostGrid, AsyncPostGrid + + +_T = TypeVar("_T") + + +class Stream(Generic[_T]): + """Provides the core interface to iterate over a synchronous stream response.""" + + response: httpx.Response + + _decoder: SSEBytesDecoder + + def __init__( + self, + *, + cast_to: type[_T], + response: httpx.Response, + client: PostGrid, + ) -> None: + self.response = response + self._cast_to = cast_to + self._client = client + self._decoder = client._make_sse_decoder() + self._iterator = self.__stream__() + + def __next__(self) -> _T: + return self._iterator.__next__() + + def __iter__(self) -> Iterator[_T]: + for item in self._iterator: + yield item + + def _iter_events(self) -> Iterator[ServerSentEvent]: + yield from self._decoder.iter_bytes(self.response.iter_bytes()) + + def __stream__(self) -> Iterator[_T]: + cast_to = cast(Any, self._cast_to) + response = self.response + process_data = self._client._process_response_data + iterator = self._iter_events() + + for sse in iterator: + yield process_data(data=sse.json(), cast_to=cast_to, response=response) + + # Ensure the entire stream is consumed + for _sse in iterator: + ... + + def __enter__(self) -> Self: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + self.close() + + def close(self) -> None: + """ + Close the response and release the connection. + + Automatically called if the response body is read to completion. + """ + self.response.close() + + +class AsyncStream(Generic[_T]): + """Provides the core interface to iterate over an asynchronous stream response.""" + + response: httpx.Response + + _decoder: SSEDecoder | SSEBytesDecoder + + def __init__( + self, + *, + cast_to: type[_T], + response: httpx.Response, + client: AsyncPostGrid, + ) -> None: + self.response = response + self._cast_to = cast_to + self._client = client + self._decoder = client._make_sse_decoder() + self._iterator = self.__stream__() + + async def __anext__(self) -> _T: + return await self._iterator.__anext__() + + async def __aiter__(self) -> AsyncIterator[_T]: + async for item in self._iterator: + yield item + + async def _iter_events(self) -> AsyncIterator[ServerSentEvent]: + async for sse in self._decoder.aiter_bytes(self.response.aiter_bytes()): + yield sse + + async def __stream__(self) -> AsyncIterator[_T]: + cast_to = cast(Any, self._cast_to) + response = self.response + process_data = self._client._process_response_data + iterator = self._iter_events() + + async for sse in iterator: + yield process_data(data=sse.json(), cast_to=cast_to, response=response) + + # Ensure the entire stream is consumed + async for _sse in iterator: + ... + + async def __aenter__(self) -> Self: + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + await self.close() + + async def close(self) -> None: + """ + Close the response and release the connection. + + Automatically called if the response body is read to completion. + """ + await self.response.aclose() + + +class ServerSentEvent: + def __init__( + self, + *, + event: str | None = None, + data: str | None = None, + id: str | None = None, + retry: int | None = None, + ) -> None: + if data is None: + data = "" + + self._id = id + self._data = data + self._event = event or None + self._retry = retry + + @property + def event(self) -> str | None: + return self._event + + @property + def id(self) -> str | None: + return self._id + + @property + def retry(self) -> int | None: + return self._retry + + @property + def data(self) -> str: + return self._data + + def json(self) -> Any: + return json.loads(self.data) + + @override + def __repr__(self) -> str: + return f"ServerSentEvent(event={self.event}, data={self.data}, id={self.id}, retry={self.retry})" + + +class SSEDecoder: + _data: list[str] + _event: str | None + _retry: int | None + _last_event_id: str | None + + def __init__(self) -> None: + self._event = None + self._data = [] + self._last_event_id = None + self._retry = None + + def iter_bytes(self, iterator: Iterator[bytes]) -> Iterator[ServerSentEvent]: + """Given an iterator that yields raw binary data, iterate over it & yield every event encountered""" + for chunk in self._iter_chunks(iterator): + # Split before decoding so splitlines() only uses \r and \n + for raw_line in chunk.splitlines(): + line = raw_line.decode("utf-8") + sse = self.decode(line) + if sse: + yield sse + + def _iter_chunks(self, iterator: Iterator[bytes]) -> Iterator[bytes]: + """Given an iterator that yields raw binary data, iterate over it and yield individual SSE chunks""" + data = b"" + for chunk in iterator: + for line in chunk.splitlines(keepends=True): + data += line + if data.endswith((b"\r\r", b"\n\n", b"\r\n\r\n")): + yield data + data = b"" + if data: + yield data + + async def aiter_bytes(self, iterator: AsyncIterator[bytes]) -> AsyncIterator[ServerSentEvent]: + """Given an iterator that yields raw binary data, iterate over it & yield every event encountered""" + async for chunk in self._aiter_chunks(iterator): + # Split before decoding so splitlines() only uses \r and \n + for raw_line in chunk.splitlines(): + line = raw_line.decode("utf-8") + sse = self.decode(line) + if sse: + yield sse + + async def _aiter_chunks(self, iterator: AsyncIterator[bytes]) -> AsyncIterator[bytes]: + """Given an iterator that yields raw binary data, iterate over it and yield individual SSE chunks""" + data = b"" + async for chunk in iterator: + for line in chunk.splitlines(keepends=True): + data += line + if data.endswith((b"\r\r", b"\n\n", b"\r\n\r\n")): + yield data + data = b"" + if data: + yield data + + def decode(self, line: str) -> ServerSentEvent | None: + # See: https://html.spec.whatwg.org/multipage/server-sent-events.html#event-stream-interpretation # noqa: E501 + + if not line: + if not self._event and not self._data and not self._last_event_id and self._retry is None: + return None + + sse = ServerSentEvent( + event=self._event, + data="\n".join(self._data), + id=self._last_event_id, + retry=self._retry, + ) + + # NOTE: as per the SSE spec, do not reset last_event_id. + self._event = None + self._data = [] + self._retry = None + + return sse + + if line.startswith(":"): + return None + + fieldname, _, value = line.partition(":") + + if value.startswith(" "): + value = value[1:] + + if fieldname == "event": + self._event = value + elif fieldname == "data": + self._data.append(value) + elif fieldname == "id": + if "\0" in value: + pass + else: + self._last_event_id = value + elif fieldname == "retry": + try: + self._retry = int(value) + except (TypeError, ValueError): + pass + else: + pass # Field is ignored. + + return None + + +@runtime_checkable +class SSEBytesDecoder(Protocol): + def iter_bytes(self, iterator: Iterator[bytes]) -> Iterator[ServerSentEvent]: + """Given an iterator that yields raw binary data, iterate over it & yield every event encountered""" + ... + + def aiter_bytes(self, iterator: AsyncIterator[bytes]) -> AsyncIterator[ServerSentEvent]: + """Given an async iterator that yields raw binary data, iterate over it & yield every event encountered""" + ... + + +def is_stream_class_type(typ: type) -> TypeGuard[type[Stream[object]] | type[AsyncStream[object]]]: + """TypeGuard for determining whether or not the given type is a subclass of `Stream` / `AsyncStream`""" + origin = get_origin(typ) or typ + return inspect.isclass(origin) and issubclass(origin, (Stream, AsyncStream)) + + +def extract_stream_chunk_type( + stream_cls: type, + *, + failure_message: str | None = None, +) -> type: + """Given a type like `Stream[T]`, returns the generic type variable `T`. + + This also handles the case where a concrete subclass is given, e.g. + ```py + class MyStream(Stream[bytes]): + ... + + extract_stream_chunk_type(MyStream) -> bytes + ``` + """ + from ._base_client import Stream, AsyncStream + + return extract_type_var_from_base( + stream_cls, + index=0, + generic_bases=cast("tuple[type, ...]", (Stream, AsyncStream)), + failure_message=failure_message, + ) diff --git a/src/postgrid/_types.py b/src/postgrid/_types.py new file mode 100644 index 0000000..02899b6 --- /dev/null +++ b/src/postgrid/_types.py @@ -0,0 +1,217 @@ +from __future__ import annotations + +from os import PathLike +from typing import ( + IO, + TYPE_CHECKING, + Any, + Dict, + List, + Type, + Tuple, + Union, + Mapping, + TypeVar, + Callable, + Optional, + Sequence, +) +from typing_extensions import Set, Literal, Protocol, TypeAlias, TypedDict, override, runtime_checkable + +import httpx +import pydantic +from httpx import URL, Proxy, Timeout, Response, BaseTransport, AsyncBaseTransport + +if TYPE_CHECKING: + from ._models import BaseModel + from ._response import APIResponse, AsyncAPIResponse + +Transport = BaseTransport +AsyncTransport = AsyncBaseTransport +Query = Mapping[str, object] +Body = object +AnyMapping = Mapping[str, object] +ModelT = TypeVar("ModelT", bound=pydantic.BaseModel) +_T = TypeVar("_T") + + +# Approximates httpx internal ProxiesTypes and RequestFiles types +# while adding support for `PathLike` instances +ProxiesDict = Dict["str | URL", Union[None, str, URL, Proxy]] +ProxiesTypes = Union[str, Proxy, ProxiesDict] +if TYPE_CHECKING: + Base64FileInput = Union[IO[bytes], PathLike[str]] + FileContent = Union[IO[bytes], bytes, PathLike[str]] +else: + Base64FileInput = Union[IO[bytes], PathLike] + FileContent = Union[IO[bytes], bytes, PathLike] # PathLike is not subscriptable in Python 3.8. +FileTypes = Union[ + # file (or bytes) + FileContent, + # (filename, file (or bytes)) + Tuple[Optional[str], FileContent], + # (filename, file (or bytes), content_type) + Tuple[Optional[str], FileContent, Optional[str]], + # (filename, file (or bytes), content_type, headers) + Tuple[Optional[str], FileContent, Optional[str], Mapping[str, str]], +] +RequestFiles = Union[Mapping[str, FileTypes], Sequence[Tuple[str, FileTypes]]] + +# duplicate of the above but without our custom file support +HttpxFileContent = Union[IO[bytes], bytes] +HttpxFileTypes = Union[ + # file (or bytes) + HttpxFileContent, + # (filename, file (or bytes)) + Tuple[Optional[str], HttpxFileContent], + # (filename, file (or bytes), content_type) + Tuple[Optional[str], HttpxFileContent, Optional[str]], + # (filename, file (or bytes), content_type, headers) + Tuple[Optional[str], HttpxFileContent, Optional[str], Mapping[str, str]], +] +HttpxRequestFiles = Union[Mapping[str, HttpxFileTypes], Sequence[Tuple[str, HttpxFileTypes]]] + +# Workaround to support (cast_to: Type[ResponseT]) -> ResponseT +# where ResponseT includes `None`. In order to support directly +# passing `None`, overloads would have to be defined for every +# method that uses `ResponseT` which would lead to an unacceptable +# amount of code duplication and make it unreadable. See _base_client.py +# for example usage. +# +# This unfortunately means that you will either have +# to import this type and pass it explicitly: +# +# from postgrid import NoneType +# client.get('/foo', cast_to=NoneType) +# +# or build it yourself: +# +# client.get('/foo', cast_to=type(None)) +if TYPE_CHECKING: + NoneType: Type[None] +else: + NoneType = type(None) + + +class RequestOptions(TypedDict, total=False): + headers: Headers + max_retries: int + timeout: float | Timeout | None + params: Query + extra_json: AnyMapping + idempotency_key: str + + +# Sentinel class used until PEP 0661 is accepted +class NotGiven: + """ + A sentinel singleton class used to distinguish omitted keyword arguments + from those passed in with the value None (which may have different behavior). + + For example: + + ```py + def get(timeout: Union[int, NotGiven, None] = NotGiven()) -> Response: ... + + + get(timeout=1) # 1s timeout + get(timeout=None) # No timeout + get() # Default timeout behavior, which may not be statically known at the method definition. + ``` + """ + + def __bool__(self) -> Literal[False]: + return False + + @override + def __repr__(self) -> str: + return "NOT_GIVEN" + + +NotGivenOr = Union[_T, NotGiven] +NOT_GIVEN = NotGiven() + + +class Omit: + """In certain situations you need to be able to represent a case where a default value has + to be explicitly removed and `None` is not an appropriate substitute, for example: + + ```py + # as the default `Content-Type` header is `application/json` that will be sent + client.post("/upload/files", files={"file": b"my raw file content"}) + + # you can't explicitly override the header as it has to be dynamically generated + # to look something like: 'multipart/form-data; boundary=0d8382fcf5f8c3be01ca2e11002d2983' + client.post(..., headers={"Content-Type": "multipart/form-data"}) + + # instead you can remove the default `application/json` header by passing Omit + client.post(..., headers={"Content-Type": Omit()}) + ``` + """ + + def __bool__(self) -> Literal[False]: + return False + + +@runtime_checkable +class ModelBuilderProtocol(Protocol): + @classmethod + def build( + cls: type[_T], + *, + response: Response, + data: object, + ) -> _T: ... + + +Headers = Mapping[str, Union[str, Omit]] + + +class HeadersLikeProtocol(Protocol): + def get(self, __key: str) -> str | None: ... + + +HeadersLike = Union[Headers, HeadersLikeProtocol] + +ResponseT = TypeVar( + "ResponseT", + bound=Union[ + object, + str, + None, + "BaseModel", + List[Any], + Dict[str, Any], + Response, + ModelBuilderProtocol, + "APIResponse[Any]", + "AsyncAPIResponse[Any]", + ], +) + +StrBytesIntFloat = Union[str, bytes, int, float] + +# Note: copied from Pydantic +# https://github.com/pydantic/pydantic/blob/6f31f8f68ef011f84357330186f603ff295312fd/pydantic/main.py#L79 +IncEx: TypeAlias = Union[Set[int], Set[str], Mapping[int, Union["IncEx", bool]], Mapping[str, Union["IncEx", bool]]] + +PostParser = Callable[[Any], Any] + + +@runtime_checkable +class InheritsGeneric(Protocol): + """Represents a type that has inherited from `Generic` + + The `__orig_bases__` property can be used to determine the resolved + type variable for a given base class. + """ + + __orig_bases__: tuple[_GenericAlias] + + +class _GenericAlias(Protocol): + __origin__: type[object] + + +class HttpxSendArgs(TypedDict, total=False): + auth: httpx.Auth diff --git a/src/postgrid/_utils/__init__.py b/src/postgrid/_utils/__init__.py new file mode 100644 index 0000000..d4fda26 --- /dev/null +++ b/src/postgrid/_utils/__init__.py @@ -0,0 +1,57 @@ +from ._sync import asyncify as asyncify +from ._proxy import LazyProxy as LazyProxy +from ._utils import ( + flatten as flatten, + is_dict as is_dict, + is_list as is_list, + is_given as is_given, + is_tuple as is_tuple, + json_safe as json_safe, + lru_cache as lru_cache, + is_mapping as is_mapping, + is_tuple_t as is_tuple_t, + parse_date as parse_date, + is_iterable as is_iterable, + is_sequence as is_sequence, + coerce_float as coerce_float, + is_mapping_t as is_mapping_t, + removeprefix as removeprefix, + removesuffix as removesuffix, + extract_files as extract_files, + is_sequence_t as is_sequence_t, + required_args as required_args, + coerce_boolean as coerce_boolean, + coerce_integer as coerce_integer, + file_from_path as file_from_path, + parse_datetime as parse_datetime, + strip_not_given as strip_not_given, + deepcopy_minimal as deepcopy_minimal, + get_async_library as get_async_library, + maybe_coerce_float as maybe_coerce_float, + get_required_header as get_required_header, + maybe_coerce_boolean as maybe_coerce_boolean, + maybe_coerce_integer as maybe_coerce_integer, +) +from ._typing import ( + is_list_type as is_list_type, + is_union_type as is_union_type, + extract_type_arg as extract_type_arg, + is_iterable_type as is_iterable_type, + is_required_type as is_required_type, + is_annotated_type as is_annotated_type, + is_type_alias_type as is_type_alias_type, + strip_annotated_type as strip_annotated_type, + extract_type_var_from_base as extract_type_var_from_base, +) +from ._streams import consume_sync_iterator as consume_sync_iterator, consume_async_iterator as consume_async_iterator +from ._transform import ( + PropertyInfo as PropertyInfo, + transform as transform, + async_transform as async_transform, + maybe_transform as maybe_transform, + async_maybe_transform as async_maybe_transform, +) +from ._reflection import ( + function_has_argument as function_has_argument, + assert_signatures_in_sync as assert_signatures_in_sync, +) diff --git a/src/postgrid/_utils/_logs.py b/src/postgrid/_utils/_logs.py new file mode 100644 index 0000000..54feab1 --- /dev/null +++ b/src/postgrid/_utils/_logs.py @@ -0,0 +1,25 @@ +import os +import logging + +logger: logging.Logger = logging.getLogger("postgrid") +httpx_logger: logging.Logger = logging.getLogger("httpx") + + +def _basic_config() -> None: + # e.g. [2023-10-05 14:12:26 - postgrid._base_client:818 - DEBUG] HTTP Request: POST http://127.0.0.1:4010/foo/bar "200 OK" + logging.basicConfig( + format="[%(asctime)s - %(name)s:%(lineno)d - %(levelname)s] %(message)s", + datefmt="%Y-%m-%d %H:%M:%S", + ) + + +def setup_logging() -> None: + env = os.environ.get("POST_GRID_LOG") + if env == "debug": + _basic_config() + logger.setLevel(logging.DEBUG) + httpx_logger.setLevel(logging.DEBUG) + elif env == "info": + _basic_config() + logger.setLevel(logging.INFO) + httpx_logger.setLevel(logging.INFO) diff --git a/src/postgrid/_utils/_proxy.py b/src/postgrid/_utils/_proxy.py new file mode 100644 index 0000000..ffd883e --- /dev/null +++ b/src/postgrid/_utils/_proxy.py @@ -0,0 +1,62 @@ +from __future__ import annotations + +from abc import ABC, abstractmethod +from typing import Generic, TypeVar, Iterable, cast +from typing_extensions import override + +T = TypeVar("T") + + +class LazyProxy(Generic[T], ABC): + """Implements data methods to pretend that an instance is another instance. + + This includes forwarding attribute access and other methods. + """ + + # Note: we have to special case proxies that themselves return proxies + # to support using a proxy as a catch-all for any random access, e.g. `proxy.foo.bar.baz` + + def __getattr__(self, attr: str) -> object: + proxied = self.__get_proxied__() + if isinstance(proxied, LazyProxy): + return proxied # pyright: ignore + return getattr(proxied, attr) + + @override + def __repr__(self) -> str: + proxied = self.__get_proxied__() + if isinstance(proxied, LazyProxy): + return proxied.__class__.__name__ + return repr(self.__get_proxied__()) + + @override + def __str__(self) -> str: + proxied = self.__get_proxied__() + if isinstance(proxied, LazyProxy): + return proxied.__class__.__name__ + return str(proxied) + + @override + def __dir__(self) -> Iterable[str]: + proxied = self.__get_proxied__() + if isinstance(proxied, LazyProxy): + return [] + return proxied.__dir__() + + @property # type: ignore + @override + def __class__(self) -> type: # pyright: ignore + proxied = self.__get_proxied__() + if issubclass(type(proxied), LazyProxy): + return type(proxied) + return proxied.__class__ + + def __get_proxied__(self) -> T: + return self.__load__() + + def __as_proxied__(self) -> T: + """Helper method that returns the current proxy, typed as the loaded object""" + return cast(T, self) + + @abstractmethod + def __load__(self) -> T: ... diff --git a/src/postgrid/_utils/_reflection.py b/src/postgrid/_utils/_reflection.py new file mode 100644 index 0000000..89aa712 --- /dev/null +++ b/src/postgrid/_utils/_reflection.py @@ -0,0 +1,42 @@ +from __future__ import annotations + +import inspect +from typing import Any, Callable + + +def function_has_argument(func: Callable[..., Any], arg_name: str) -> bool: + """Returns whether or not the given function has a specific parameter""" + sig = inspect.signature(func) + return arg_name in sig.parameters + + +def assert_signatures_in_sync( + source_func: Callable[..., Any], + check_func: Callable[..., Any], + *, + exclude_params: set[str] = set(), +) -> None: + """Ensure that the signature of the second function matches the first.""" + + check_sig = inspect.signature(check_func) + source_sig = inspect.signature(source_func) + + errors: list[str] = [] + + for name, source_param in source_sig.parameters.items(): + if name in exclude_params: + continue + + custom_param = check_sig.parameters.get(name) + if not custom_param: + errors.append(f"the `{name}` param is missing") + continue + + if custom_param.annotation != source_param.annotation: + errors.append( + f"types for the `{name}` param are do not match; source={repr(source_param.annotation)} checking={repr(custom_param.annotation)}" + ) + continue + + if errors: + raise AssertionError(f"{len(errors)} errors encountered when comparing signatures:\n\n" + "\n\n".join(errors)) diff --git a/src/postgrid/_utils/_streams.py b/src/postgrid/_utils/_streams.py new file mode 100644 index 0000000..f4a0208 --- /dev/null +++ b/src/postgrid/_utils/_streams.py @@ -0,0 +1,12 @@ +from typing import Any +from typing_extensions import Iterator, AsyncIterator + + +def consume_sync_iterator(iterator: Iterator[Any]) -> None: + for _ in iterator: + ... + + +async def consume_async_iterator(iterator: AsyncIterator[Any]) -> None: + async for _ in iterator: + ... diff --git a/src/postgrid/_utils/_sync.py b/src/postgrid/_utils/_sync.py new file mode 100644 index 0000000..ad7ec71 --- /dev/null +++ b/src/postgrid/_utils/_sync.py @@ -0,0 +1,86 @@ +from __future__ import annotations + +import sys +import asyncio +import functools +import contextvars +from typing import Any, TypeVar, Callable, Awaitable +from typing_extensions import ParamSpec + +import anyio +import sniffio +import anyio.to_thread + +T_Retval = TypeVar("T_Retval") +T_ParamSpec = ParamSpec("T_ParamSpec") + + +if sys.version_info >= (3, 9): + _asyncio_to_thread = asyncio.to_thread +else: + # backport of https://docs.python.org/3/library/asyncio-task.html#asyncio.to_thread + # for Python 3.8 support + async def _asyncio_to_thread( + func: Callable[T_ParamSpec, T_Retval], /, *args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs + ) -> Any: + """Asynchronously run function *func* in a separate thread. + + Any *args and **kwargs supplied for this function are directly passed + to *func*. Also, the current :class:`contextvars.Context` is propagated, + allowing context variables from the main thread to be accessed in the + separate thread. + + Returns a coroutine that can be awaited to get the eventual result of *func*. + """ + loop = asyncio.events.get_running_loop() + ctx = contextvars.copy_context() + func_call = functools.partial(ctx.run, func, *args, **kwargs) + return await loop.run_in_executor(None, func_call) + + +async def to_thread( + func: Callable[T_ParamSpec, T_Retval], /, *args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs +) -> T_Retval: + if sniffio.current_async_library() == "asyncio": + return await _asyncio_to_thread(func, *args, **kwargs) + + return await anyio.to_thread.run_sync( + functools.partial(func, *args, **kwargs), + ) + + +# inspired by `asyncer`, https://github.com/tiangolo/asyncer +def asyncify(function: Callable[T_ParamSpec, T_Retval]) -> Callable[T_ParamSpec, Awaitable[T_Retval]]: + """ + Take a blocking function and create an async one that receives the same + positional and keyword arguments. For python version 3.9 and above, it uses + asyncio.to_thread to run the function in a separate thread. For python version + 3.8, it uses locally defined copy of the asyncio.to_thread function which was + introduced in python 3.9. + + Usage: + + ```python + def blocking_func(arg1, arg2, kwarg1=None): + # blocking code + return result + + + result = asyncify(blocking_function)(arg1, arg2, kwarg1=value1) + ``` + + ## Arguments + + `function`: a blocking regular callable (e.g. a function) + + ## Return + + An async function that takes the same positional and keyword arguments as the + original one, that when called runs the same original function in a thread worker + and returns the result. + """ + + async def wrapper(*args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs) -> T_Retval: + return await to_thread(function, *args, **kwargs) + + return wrapper diff --git a/src/postgrid/_utils/_transform.py b/src/postgrid/_utils/_transform.py new file mode 100644 index 0000000..18afd9d --- /dev/null +++ b/src/postgrid/_utils/_transform.py @@ -0,0 +1,402 @@ +from __future__ import annotations + +import io +import base64 +import pathlib +from typing import Any, Mapping, TypeVar, cast +from datetime import date, datetime +from typing_extensions import Literal, get_args, override, get_type_hints + +import anyio +import pydantic + +from ._utils import ( + is_list, + is_mapping, + is_iterable, +) +from .._files import is_base64_file_input +from ._typing import ( + is_list_type, + is_union_type, + extract_type_arg, + is_iterable_type, + is_required_type, + is_annotated_type, + strip_annotated_type, +) +from .._compat import get_origin, model_dump, is_typeddict + +_T = TypeVar("_T") + + +# TODO: support for drilling globals() and locals() +# TODO: ensure works correctly with forward references in all cases + + +PropertyFormat = Literal["iso8601", "base64", "custom"] + + +class PropertyInfo: + """Metadata class to be used in Annotated types to provide information about a given type. + + For example: + + class MyParams(TypedDict): + account_holder_name: Annotated[str, PropertyInfo(alias='accountHolderName')] + + This means that {'account_holder_name': 'Robert'} will be transformed to {'accountHolderName': 'Robert'} before being sent to the API. + """ + + alias: str | None + format: PropertyFormat | None + format_template: str | None + discriminator: str | None + + def __init__( + self, + *, + alias: str | None = None, + format: PropertyFormat | None = None, + format_template: str | None = None, + discriminator: str | None = None, + ) -> None: + self.alias = alias + self.format = format + self.format_template = format_template + self.discriminator = discriminator + + @override + def __repr__(self) -> str: + return f"{self.__class__.__name__}(alias='{self.alias}', format={self.format}, format_template='{self.format_template}', discriminator='{self.discriminator}')" + + +def maybe_transform( + data: object, + expected_type: object, +) -> Any | None: + """Wrapper over `transform()` that allows `None` to be passed. + + See `transform()` for more details. + """ + if data is None: + return None + return transform(data, expected_type) + + +# Wrapper over _transform_recursive providing fake types +def transform( + data: _T, + expected_type: object, +) -> _T: + """Transform dictionaries based off of type information from the given type, for example: + + ```py + class Params(TypedDict, total=False): + card_id: Required[Annotated[str, PropertyInfo(alias="cardID")]] + + + transformed = transform({"card_id": ""}, Params) + # {'cardID': ''} + ``` + + Any keys / data that does not have type information given will be included as is. + + It should be noted that the transformations that this function does are not represented in the type system. + """ + transformed = _transform_recursive(data, annotation=cast(type, expected_type)) + return cast(_T, transformed) + + +def _get_annotated_type(type_: type) -> type | None: + """If the given type is an `Annotated` type then it is returned, if not `None` is returned. + + This also unwraps the type when applicable, e.g. `Required[Annotated[T, ...]]` + """ + if is_required_type(type_): + # Unwrap `Required[Annotated[T, ...]]` to `Annotated[T, ...]` + type_ = get_args(type_)[0] + + if is_annotated_type(type_): + return type_ + + return None + + +def _maybe_transform_key(key: str, type_: type) -> str: + """Transform the given `data` based on the annotations provided in `type_`. + + Note: this function only looks at `Annotated` types that contain `PropertInfo` metadata. + """ + annotated_type = _get_annotated_type(type_) + if annotated_type is None: + # no `Annotated` definition for this type, no transformation needed + return key + + # ignore the first argument as it is the actual type + annotations = get_args(annotated_type)[1:] + for annotation in annotations: + if isinstance(annotation, PropertyInfo) and annotation.alias is not None: + return annotation.alias + + return key + + +def _transform_recursive( + data: object, + *, + annotation: type, + inner_type: type | None = None, +) -> object: + """Transform the given data against the expected type. + + Args: + annotation: The direct type annotation given to the particular piece of data. + This may or may not be wrapped in metadata types, e.g. `Required[T]`, `Annotated[T, ...]` etc + + inner_type: If applicable, this is the "inside" type. This is useful in certain cases where the outside type + is a container type such as `List[T]`. In that case `inner_type` should be set to `T` so that each entry in + the list can be transformed using the metadata from the container type. + + Defaults to the same value as the `annotation` argument. + """ + if inner_type is None: + inner_type = annotation + + stripped_type = strip_annotated_type(inner_type) + origin = get_origin(stripped_type) or stripped_type + if is_typeddict(stripped_type) and is_mapping(data): + return _transform_typeddict(data, stripped_type) + + if origin == dict and is_mapping(data): + items_type = get_args(stripped_type)[1] + return {key: _transform_recursive(value, annotation=items_type) for key, value in data.items()} + + if ( + # List[T] + (is_list_type(stripped_type) and is_list(data)) + # Iterable[T] + or (is_iterable_type(stripped_type) and is_iterable(data) and not isinstance(data, str)) + ): + # dicts are technically iterable, but it is an iterable on the keys of the dict and is not usually + # intended as an iterable, so we don't transform it. + if isinstance(data, dict): + return cast(object, data) + + inner_type = extract_type_arg(stripped_type, 0) + return [_transform_recursive(d, annotation=annotation, inner_type=inner_type) for d in data] + + if is_union_type(stripped_type): + # For union types we run the transformation against all subtypes to ensure that everything is transformed. + # + # TODO: there may be edge cases where the same normalized field name will transform to two different names + # in different subtypes. + for subtype in get_args(stripped_type): + data = _transform_recursive(data, annotation=annotation, inner_type=subtype) + return data + + if isinstance(data, pydantic.BaseModel): + return model_dump(data, exclude_unset=True, mode="json") + + annotated_type = _get_annotated_type(annotation) + if annotated_type is None: + return data + + # ignore the first argument as it is the actual type + annotations = get_args(annotated_type)[1:] + for annotation in annotations: + if isinstance(annotation, PropertyInfo) and annotation.format is not None: + return _format_data(data, annotation.format, annotation.format_template) + + return data + + +def _format_data(data: object, format_: PropertyFormat, format_template: str | None) -> object: + if isinstance(data, (date, datetime)): + if format_ == "iso8601": + return data.isoformat() + + if format_ == "custom" and format_template is not None: + return data.strftime(format_template) + + if format_ == "base64" and is_base64_file_input(data): + binary: str | bytes | None = None + + if isinstance(data, pathlib.Path): + binary = data.read_bytes() + elif isinstance(data, io.IOBase): + binary = data.read() + + if isinstance(binary, str): # type: ignore[unreachable] + binary = binary.encode() + + if not isinstance(binary, bytes): + raise RuntimeError(f"Could not read bytes from {data}; Received {type(binary)}") + + return base64.b64encode(binary).decode("ascii") + + return data + + +def _transform_typeddict( + data: Mapping[str, object], + expected_type: type, +) -> Mapping[str, object]: + result: dict[str, object] = {} + annotations = get_type_hints(expected_type, include_extras=True) + for key, value in data.items(): + type_ = annotations.get(key) + if type_ is None: + # we do not have a type annotation for this field, leave it as is + result[key] = value + else: + result[_maybe_transform_key(key, type_)] = _transform_recursive(value, annotation=type_) + return result + + +async def async_maybe_transform( + data: object, + expected_type: object, +) -> Any | None: + """Wrapper over `async_transform()` that allows `None` to be passed. + + See `async_transform()` for more details. + """ + if data is None: + return None + return await async_transform(data, expected_type) + + +async def async_transform( + data: _T, + expected_type: object, +) -> _T: + """Transform dictionaries based off of type information from the given type, for example: + + ```py + class Params(TypedDict, total=False): + card_id: Required[Annotated[str, PropertyInfo(alias="cardID")]] + + + transformed = transform({"card_id": ""}, Params) + # {'cardID': ''} + ``` + + Any keys / data that does not have type information given will be included as is. + + It should be noted that the transformations that this function does are not represented in the type system. + """ + transformed = await _async_transform_recursive(data, annotation=cast(type, expected_type)) + return cast(_T, transformed) + + +async def _async_transform_recursive( + data: object, + *, + annotation: type, + inner_type: type | None = None, +) -> object: + """Transform the given data against the expected type. + + Args: + annotation: The direct type annotation given to the particular piece of data. + This may or may not be wrapped in metadata types, e.g. `Required[T]`, `Annotated[T, ...]` etc + + inner_type: If applicable, this is the "inside" type. This is useful in certain cases where the outside type + is a container type such as `List[T]`. In that case `inner_type` should be set to `T` so that each entry in + the list can be transformed using the metadata from the container type. + + Defaults to the same value as the `annotation` argument. + """ + if inner_type is None: + inner_type = annotation + + stripped_type = strip_annotated_type(inner_type) + origin = get_origin(stripped_type) or stripped_type + if is_typeddict(stripped_type) and is_mapping(data): + return await _async_transform_typeddict(data, stripped_type) + + if origin == dict and is_mapping(data): + items_type = get_args(stripped_type)[1] + return {key: _transform_recursive(value, annotation=items_type) for key, value in data.items()} + + if ( + # List[T] + (is_list_type(stripped_type) and is_list(data)) + # Iterable[T] + or (is_iterable_type(stripped_type) and is_iterable(data) and not isinstance(data, str)) + ): + # dicts are technically iterable, but it is an iterable on the keys of the dict and is not usually + # intended as an iterable, so we don't transform it. + if isinstance(data, dict): + return cast(object, data) + + inner_type = extract_type_arg(stripped_type, 0) + return [await _async_transform_recursive(d, annotation=annotation, inner_type=inner_type) for d in data] + + if is_union_type(stripped_type): + # For union types we run the transformation against all subtypes to ensure that everything is transformed. + # + # TODO: there may be edge cases where the same normalized field name will transform to two different names + # in different subtypes. + for subtype in get_args(stripped_type): + data = await _async_transform_recursive(data, annotation=annotation, inner_type=subtype) + return data + + if isinstance(data, pydantic.BaseModel): + return model_dump(data, exclude_unset=True, mode="json") + + annotated_type = _get_annotated_type(annotation) + if annotated_type is None: + return data + + # ignore the first argument as it is the actual type + annotations = get_args(annotated_type)[1:] + for annotation in annotations: + if isinstance(annotation, PropertyInfo) and annotation.format is not None: + return await _async_format_data(data, annotation.format, annotation.format_template) + + return data + + +async def _async_format_data(data: object, format_: PropertyFormat, format_template: str | None) -> object: + if isinstance(data, (date, datetime)): + if format_ == "iso8601": + return data.isoformat() + + if format_ == "custom" and format_template is not None: + return data.strftime(format_template) + + if format_ == "base64" and is_base64_file_input(data): + binary: str | bytes | None = None + + if isinstance(data, pathlib.Path): + binary = await anyio.Path(data).read_bytes() + elif isinstance(data, io.IOBase): + binary = data.read() + + if isinstance(binary, str): # type: ignore[unreachable] + binary = binary.encode() + + if not isinstance(binary, bytes): + raise RuntimeError(f"Could not read bytes from {data}; Received {type(binary)}") + + return base64.b64encode(binary).decode("ascii") + + return data + + +async def _async_transform_typeddict( + data: Mapping[str, object], + expected_type: type, +) -> Mapping[str, object]: + result: dict[str, object] = {} + annotations = get_type_hints(expected_type, include_extras=True) + for key, value in data.items(): + type_ = annotations.get(key) + if type_ is None: + # we do not have a type annotation for this field, leave it as is + result[key] = value + else: + result[_maybe_transform_key(key, type_)] = await _async_transform_recursive(value, annotation=type_) + return result diff --git a/src/postgrid/_utils/_typing.py b/src/postgrid/_utils/_typing.py new file mode 100644 index 0000000..278749b --- /dev/null +++ b/src/postgrid/_utils/_typing.py @@ -0,0 +1,149 @@ +from __future__ import annotations + +import sys +import typing +import typing_extensions +from typing import Any, TypeVar, Iterable, cast +from collections import abc as _c_abc +from typing_extensions import ( + TypeIs, + Required, + Annotated, + get_args, + get_origin, +) + +from .._types import InheritsGeneric +from .._compat import is_union as _is_union + + +def is_annotated_type(typ: type) -> bool: + return get_origin(typ) == Annotated + + +def is_list_type(typ: type) -> bool: + return (get_origin(typ) or typ) == list + + +def is_iterable_type(typ: type) -> bool: + """If the given type is `typing.Iterable[T]`""" + origin = get_origin(typ) or typ + return origin == Iterable or origin == _c_abc.Iterable + + +def is_union_type(typ: type) -> bool: + return _is_union(get_origin(typ)) + + +def is_required_type(typ: type) -> bool: + return get_origin(typ) == Required + + +def is_typevar(typ: type) -> bool: + # type ignore is required because type checkers + # think this expression will always return False + return type(typ) == TypeVar # type: ignore + + +_TYPE_ALIAS_TYPES: tuple[type[typing_extensions.TypeAliasType], ...] = (typing_extensions.TypeAliasType,) +if sys.version_info >= (3, 12): + _TYPE_ALIAS_TYPES = (*_TYPE_ALIAS_TYPES, typing.TypeAliasType) + + +def is_type_alias_type(tp: Any, /) -> TypeIs[typing_extensions.TypeAliasType]: + """Return whether the provided argument is an instance of `TypeAliasType`. + + ```python + type Int = int + is_type_alias_type(Int) + # > True + Str = TypeAliasType("Str", str) + is_type_alias_type(Str) + # > True + ``` + """ + return isinstance(tp, _TYPE_ALIAS_TYPES) + + +# Extracts T from Annotated[T, ...] or from Required[Annotated[T, ...]] +def strip_annotated_type(typ: type) -> type: + if is_required_type(typ) or is_annotated_type(typ): + return strip_annotated_type(cast(type, get_args(typ)[0])) + + return typ + + +def extract_type_arg(typ: type, index: int) -> type: + args = get_args(typ) + try: + return cast(type, args[index]) + except IndexError as err: + raise RuntimeError(f"Expected type {typ} to have a type argument at index {index} but it did not") from err + + +def extract_type_var_from_base( + typ: type, + *, + generic_bases: tuple[type, ...], + index: int, + failure_message: str | None = None, +) -> type: + """Given a type like `Foo[T]`, returns the generic type variable `T`. + + This also handles the case where a concrete subclass is given, e.g. + ```py + class MyResponse(Foo[bytes]): + ... + + extract_type_var(MyResponse, bases=(Foo,), index=0) -> bytes + ``` + + And where a generic subclass is given: + ```py + _T = TypeVar('_T') + class MyResponse(Foo[_T]): + ... + + extract_type_var(MyResponse[bytes], bases=(Foo,), index=0) -> bytes + ``` + """ + cls = cast(object, get_origin(typ) or typ) + if cls in generic_bases: + # we're given the class directly + return extract_type_arg(typ, index) + + # if a subclass is given + # --- + # this is needed as __orig_bases__ is not present in the typeshed stubs + # because it is intended to be for internal use only, however there does + # not seem to be a way to resolve generic TypeVars for inherited subclasses + # without using it. + if isinstance(cls, InheritsGeneric): + target_base_class: Any | None = None + for base in cls.__orig_bases__: + if base.__origin__ in generic_bases: + target_base_class = base + break + + if target_base_class is None: + raise RuntimeError( + "Could not find the generic base class;\n" + "This should never happen;\n" + f"Does {cls} inherit from one of {generic_bases} ?" + ) + + extracted = extract_type_arg(target_base_class, index) + if is_typevar(extracted): + # If the extracted type argument is itself a type variable + # then that means the subclass itself is generic, so we have + # to resolve the type argument from the class itself, not + # the base class. + # + # Note: if there is more than 1 type argument, the subclass could + # change the ordering of the type arguments, this is not currently + # supported. + return extract_type_arg(typ, index) + + return extracted + + raise RuntimeError(failure_message or f"Could not resolve inner type variable at index {index} for {typ}") diff --git a/src/postgrid/_utils/_utils.py b/src/postgrid/_utils/_utils.py new file mode 100644 index 0000000..e5811bb --- /dev/null +++ b/src/postgrid/_utils/_utils.py @@ -0,0 +1,414 @@ +from __future__ import annotations + +import os +import re +import inspect +import functools +from typing import ( + Any, + Tuple, + Mapping, + TypeVar, + Callable, + Iterable, + Sequence, + cast, + overload, +) +from pathlib import Path +from datetime import date, datetime +from typing_extensions import TypeGuard + +import sniffio + +from .._types import NotGiven, FileTypes, NotGivenOr, HeadersLike +from .._compat import parse_date as parse_date, parse_datetime as parse_datetime + +_T = TypeVar("_T") +_TupleT = TypeVar("_TupleT", bound=Tuple[object, ...]) +_MappingT = TypeVar("_MappingT", bound=Mapping[str, object]) +_SequenceT = TypeVar("_SequenceT", bound=Sequence[object]) +CallableT = TypeVar("CallableT", bound=Callable[..., Any]) + + +def flatten(t: Iterable[Iterable[_T]]) -> list[_T]: + return [item for sublist in t for item in sublist] + + +def extract_files( + # TODO: this needs to take Dict but variance issues..... + # create protocol type ? + query: Mapping[str, object], + *, + paths: Sequence[Sequence[str]], +) -> list[tuple[str, FileTypes]]: + """Recursively extract files from the given dictionary based on specified paths. + + A path may look like this ['foo', 'files', '', 'data']. + + Note: this mutates the given dictionary. + """ + files: list[tuple[str, FileTypes]] = [] + for path in paths: + files.extend(_extract_items(query, path, index=0, flattened_key=None)) + return files + + +def _extract_items( + obj: object, + path: Sequence[str], + *, + index: int, + flattened_key: str | None, +) -> list[tuple[str, FileTypes]]: + try: + key = path[index] + except IndexError: + if isinstance(obj, NotGiven): + # no value was provided - we can safely ignore + return [] + + # cyclical import + from .._files import assert_is_file_content + + # We have exhausted the path, return the entry we found. + assert_is_file_content(obj, key=flattened_key) + assert flattened_key is not None + return [(flattened_key, cast(FileTypes, obj))] + + index += 1 + if is_dict(obj): + try: + # We are at the last entry in the path so we must remove the field + if (len(path)) == index: + item = obj.pop(key) + else: + item = obj[key] + except KeyError: + # Key was not present in the dictionary, this is not indicative of an error + # as the given path may not point to a required field. We also do not want + # to enforce required fields as the API may differ from the spec in some cases. + return [] + if flattened_key is None: + flattened_key = key + else: + flattened_key += f"[{key}]" + return _extract_items( + item, + path, + index=index, + flattened_key=flattened_key, + ) + elif is_list(obj): + if key != "": + return [] + + return flatten( + [ + _extract_items( + item, + path, + index=index, + flattened_key=flattened_key + "[]" if flattened_key is not None else "[]", + ) + for item in obj + ] + ) + + # Something unexpected was passed, just ignore it. + return [] + + +def is_given(obj: NotGivenOr[_T]) -> TypeGuard[_T]: + return not isinstance(obj, NotGiven) + + +# Type safe methods for narrowing types with TypeVars. +# The default narrowing for isinstance(obj, dict) is dict[unknown, unknown], +# however this cause Pyright to rightfully report errors. As we know we don't +# care about the contained types we can safely use `object` in it's place. +# +# There are two separate functions defined, `is_*` and `is_*_t` for different use cases. +# `is_*` is for when you're dealing with an unknown input +# `is_*_t` is for when you're narrowing a known union type to a specific subset + + +def is_tuple(obj: object) -> TypeGuard[tuple[object, ...]]: + return isinstance(obj, tuple) + + +def is_tuple_t(obj: _TupleT | object) -> TypeGuard[_TupleT]: + return isinstance(obj, tuple) + + +def is_sequence(obj: object) -> TypeGuard[Sequence[object]]: + return isinstance(obj, Sequence) + + +def is_sequence_t(obj: _SequenceT | object) -> TypeGuard[_SequenceT]: + return isinstance(obj, Sequence) + + +def is_mapping(obj: object) -> TypeGuard[Mapping[str, object]]: + return isinstance(obj, Mapping) + + +def is_mapping_t(obj: _MappingT | object) -> TypeGuard[_MappingT]: + return isinstance(obj, Mapping) + + +def is_dict(obj: object) -> TypeGuard[dict[object, object]]: + return isinstance(obj, dict) + + +def is_list(obj: object) -> TypeGuard[list[object]]: + return isinstance(obj, list) + + +def is_iterable(obj: object) -> TypeGuard[Iterable[object]]: + return isinstance(obj, Iterable) + + +def deepcopy_minimal(item: _T) -> _T: + """Minimal reimplementation of copy.deepcopy() that will only copy certain object types: + + - mappings, e.g. `dict` + - list + + This is done for performance reasons. + """ + if is_mapping(item): + return cast(_T, {k: deepcopy_minimal(v) for k, v in item.items()}) + if is_list(item): + return cast(_T, [deepcopy_minimal(entry) for entry in item]) + return item + + +# copied from https://github.com/Rapptz/RoboDanny +def human_join(seq: Sequence[str], *, delim: str = ", ", final: str = "or") -> str: + size = len(seq) + if size == 0: + return "" + + if size == 1: + return seq[0] + + if size == 2: + return f"{seq[0]} {final} {seq[1]}" + + return delim.join(seq[:-1]) + f" {final} {seq[-1]}" + + +def quote(string: str) -> str: + """Add single quotation marks around the given string. Does *not* do any escaping.""" + return f"'{string}'" + + +def required_args(*variants: Sequence[str]) -> Callable[[CallableT], CallableT]: + """Decorator to enforce a given set of arguments or variants of arguments are passed to the decorated function. + + Useful for enforcing runtime validation of overloaded functions. + + Example usage: + ```py + @overload + def foo(*, a: str) -> str: ... + + + @overload + def foo(*, b: bool) -> str: ... + + + # This enforces the same constraints that a static type checker would + # i.e. that either a or b must be passed to the function + @required_args(["a"], ["b"]) + def foo(*, a: str | None = None, b: bool | None = None) -> str: ... + ``` + """ + + def inner(func: CallableT) -> CallableT: + params = inspect.signature(func).parameters + positional = [ + name + for name, param in params.items() + if param.kind + in { + param.POSITIONAL_ONLY, + param.POSITIONAL_OR_KEYWORD, + } + ] + + @functools.wraps(func) + def wrapper(*args: object, **kwargs: object) -> object: + given_params: set[str] = set() + for i, _ in enumerate(args): + try: + given_params.add(positional[i]) + except IndexError: + raise TypeError( + f"{func.__name__}() takes {len(positional)} argument(s) but {len(args)} were given" + ) from None + + for key in kwargs.keys(): + given_params.add(key) + + for variant in variants: + matches = all((param in given_params for param in variant)) + if matches: + break + else: # no break + if len(variants) > 1: + variations = human_join( + ["(" + human_join([quote(arg) for arg in variant], final="and") + ")" for variant in variants] + ) + msg = f"Missing required arguments; Expected either {variations} arguments to be given" + else: + assert len(variants) > 0 + + # TODO: this error message is not deterministic + missing = list(set(variants[0]) - given_params) + if len(missing) > 1: + msg = f"Missing required arguments: {human_join([quote(arg) for arg in missing])}" + else: + msg = f"Missing required argument: {quote(missing[0])}" + raise TypeError(msg) + return func(*args, **kwargs) + + return wrapper # type: ignore + + return inner + + +_K = TypeVar("_K") +_V = TypeVar("_V") + + +@overload +def strip_not_given(obj: None) -> None: ... + + +@overload +def strip_not_given(obj: Mapping[_K, _V | NotGiven]) -> dict[_K, _V]: ... + + +@overload +def strip_not_given(obj: object) -> object: ... + + +def strip_not_given(obj: object | None) -> object: + """Remove all top-level keys where their values are instances of `NotGiven`""" + if obj is None: + return None + + if not is_mapping(obj): + return obj + + return {key: value for key, value in obj.items() if not isinstance(value, NotGiven)} + + +def coerce_integer(val: str) -> int: + return int(val, base=10) + + +def coerce_float(val: str) -> float: + return float(val) + + +def coerce_boolean(val: str) -> bool: + return val == "true" or val == "1" or val == "on" + + +def maybe_coerce_integer(val: str | None) -> int | None: + if val is None: + return None + return coerce_integer(val) + + +def maybe_coerce_float(val: str | None) -> float | None: + if val is None: + return None + return coerce_float(val) + + +def maybe_coerce_boolean(val: str | None) -> bool | None: + if val is None: + return None + return coerce_boolean(val) + + +def removeprefix(string: str, prefix: str) -> str: + """Remove a prefix from a string. + + Backport of `str.removeprefix` for Python < 3.9 + """ + if string.startswith(prefix): + return string[len(prefix) :] + return string + + +def removesuffix(string: str, suffix: str) -> str: + """Remove a suffix from a string. + + Backport of `str.removesuffix` for Python < 3.9 + """ + if string.endswith(suffix): + return string[: -len(suffix)] + return string + + +def file_from_path(path: str) -> FileTypes: + contents = Path(path).read_bytes() + file_name = os.path.basename(path) + return (file_name, contents) + + +def get_required_header(headers: HeadersLike, header: str) -> str: + lower_header = header.lower() + if is_mapping_t(headers): + # mypy doesn't understand the type narrowing here + for k, v in headers.items(): # type: ignore + if k.lower() == lower_header and isinstance(v, str): + return v + + # to deal with the case where the header looks like Stainless-Event-Id + intercaps_header = re.sub(r"([^\w])(\w)", lambda pat: pat.group(1) + pat.group(2).upper(), header.capitalize()) + + for normalized_header in [header, lower_header, header.upper(), intercaps_header]: + value = headers.get(normalized_header) + if value: + return value + + raise ValueError(f"Could not find {header} header") + + +def get_async_library() -> str: + try: + return sniffio.current_async_library() + except Exception: + return "false" + + +def lru_cache(*, maxsize: int | None = 128) -> Callable[[CallableT], CallableT]: + """A version of functools.lru_cache that retains the type signature + for the wrapped function arguments. + """ + wrapper = functools.lru_cache( # noqa: TID251 + maxsize=maxsize, + ) + return cast(Any, wrapper) # type: ignore[no-any-return] + + +def json_safe(data: object) -> object: + """Translates a mapping / sequence recursively in the same fashion + as `pydantic` v2's `model_dump(mode="json")`. + """ + if is_mapping(data): + return {json_safe(key): json_safe(value) for key, value in data.items()} + + if is_iterable(data) and not isinstance(data, (str, bytes, bytearray)): + return [json_safe(item) for item in data] + + if isinstance(data, (datetime, date)): + return data.isoformat() + + return data diff --git a/src/postgrid/_version.py b/src/postgrid/_version.py new file mode 100644 index 0000000..fc064f0 --- /dev/null +++ b/src/postgrid/_version.py @@ -0,0 +1,4 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +__title__ = "postgrid" +__version__ = "0.0.1-alpha.0" diff --git a/src/postgrid/lib/.keep b/src/postgrid/lib/.keep new file mode 100644 index 0000000..5e2c99f --- /dev/null +++ b/src/postgrid/lib/.keep @@ -0,0 +1,4 @@ +File generated from our OpenAPI spec by Stainless. + +This directory can be used to store custom files to expand the SDK. +It is ignored by Stainless code generation and its content (other than this keep file) won't be touched. \ No newline at end of file diff --git a/src/postgrid/pagination.py b/src/postgrid/pagination.py new file mode 100644 index 0000000..dc6ac70 --- /dev/null +++ b/src/postgrid/pagination.py @@ -0,0 +1,72 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Generic, TypeVar, Optional +from typing_extensions import override + +from pydantic import Field as FieldInfo + +from ._base_client import BasePage, PageInfo, BaseSyncPage, BaseAsyncPage + +__all__ = ["SyncSkipLimit", "AsyncSkipLimit"] + +_T = TypeVar("_T") + + +class SyncSkipLimit(BaseSyncPage[_T], BasePage[_T], Generic[_T]): + data: List[_T] + total_count: Optional[int] = FieldInfo(alias="totalCount", default=None) + + @override + def _get_page_items(self) -> List[_T]: + data = self.data + if not data: + return [] + return data + + @override + def next_page_info(self) -> Optional[PageInfo]: + offset = self._options.params.get("skip") or 0 + if not isinstance(offset, int): + raise ValueError(f'Expected "skip" param to be an integer but got {offset}') + + length = len(self._get_page_items()) + current_count = offset + length + + total_count = self.total_count + if total_count is None: + return None + + if current_count < total_count: + return PageInfo(params={"skip": current_count}) + + return None + + +class AsyncSkipLimit(BaseAsyncPage[_T], BasePage[_T], Generic[_T]): + data: List[_T] + total_count: Optional[int] = FieldInfo(alias="totalCount", default=None) + + @override + def _get_page_items(self) -> List[_T]: + data = self.data + if not data: + return [] + return data + + @override + def next_page_info(self) -> Optional[PageInfo]: + offset = self._options.params.get("skip") or 0 + if not isinstance(offset, int): + raise ValueError(f'Expected "skip" param to be an integer but got {offset}') + + length = len(self._get_page_items()) + current_count = offset + length + + total_count = self.total_count + if total_count is None: + return None + + if current_count < total_count: + return PageInfo(params={"skip": current_count}) + + return None diff --git a/src/postgrid/py.typed b/src/postgrid/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/src/postgrid/resources/__init__.py b/src/postgrid/resources/__init__.py new file mode 100644 index 0000000..2375536 --- /dev/null +++ b/src/postgrid/resources/__init__.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .print_mail import ( + PrintMailResource, + AsyncPrintMailResource, + PrintMailResourceWithRawResponse, + AsyncPrintMailResourceWithRawResponse, + PrintMailResourceWithStreamingResponse, + AsyncPrintMailResourceWithStreamingResponse, +) +from .address_verification import ( + AddressVerificationResource, + AsyncAddressVerificationResource, + AddressVerificationResourceWithRawResponse, + AsyncAddressVerificationResourceWithRawResponse, + AddressVerificationResourceWithStreamingResponse, + AsyncAddressVerificationResourceWithStreamingResponse, +) +from .intl_address_verification import ( + IntlAddressVerificationResource, + AsyncIntlAddressVerificationResource, + IntlAddressVerificationResourceWithRawResponse, + AsyncIntlAddressVerificationResourceWithRawResponse, + IntlAddressVerificationResourceWithStreamingResponse, + AsyncIntlAddressVerificationResourceWithStreamingResponse, +) + +__all__ = [ + "AddressVerificationResource", + "AsyncAddressVerificationResource", + "AddressVerificationResourceWithRawResponse", + "AsyncAddressVerificationResourceWithRawResponse", + "AddressVerificationResourceWithStreamingResponse", + "AsyncAddressVerificationResourceWithStreamingResponse", + "IntlAddressVerificationResource", + "AsyncIntlAddressVerificationResource", + "IntlAddressVerificationResourceWithRawResponse", + "AsyncIntlAddressVerificationResourceWithRawResponse", + "IntlAddressVerificationResourceWithStreamingResponse", + "AsyncIntlAddressVerificationResourceWithStreamingResponse", + "PrintMailResource", + "AsyncPrintMailResource", + "PrintMailResourceWithRawResponse", + "AsyncPrintMailResourceWithRawResponse", + "PrintMailResourceWithStreamingResponse", + "AsyncPrintMailResourceWithStreamingResponse", +] diff --git a/src/postgrid/resources/address_verification.py b/src/postgrid/resources/address_verification.py new file mode 100644 index 0000000..a7892b6 --- /dev/null +++ b/src/postgrid/resources/address_verification.py @@ -0,0 +1,339 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import overload + +import httpx + +from ..types import address_verification_verify_params +from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from .._utils import ( + required_args, + maybe_transform, + async_maybe_transform, +) +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from .._base_client import make_request_options +from ..types.address_verification_verify_response import AddressVerificationVerifyResponse + +__all__ = ["AddressVerificationResource", "AsyncAddressVerificationResource"] + + +class AddressVerificationResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> AddressVerificationResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + """ + return AddressVerificationResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AddressVerificationResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + """ + return AddressVerificationResourceWithStreamingResponse(self) + + @overload + def verify( + self, + *, + address: str, + geocode: bool | NotGiven = NOT_GIVEN, + include_details: bool | NotGiven = NOT_GIVEN, + proper_case: bool | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AddressVerificationVerifyResponse: + """1. + + **Structured Address** — Verify and standardize a structured address (e.g., + with `line1`, `city`, etc.). + 2. **Freeform Address** — Verify and standardize a freeform address written on + one line. For best results, append the ISO 2-letter country code (e.g., `US`, + `CA`) to the end of the line. + + - Specifying `includeDetails=true` will provide additional output as documented + in the `Details` schema. + - Uses 1 lookup for verification, and 1 more if geocoding (unless your contract + says otherwise). + + Args: + address: The address you want to verify, written on a single line. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def verify( + self, + *, + address: address_verification_verify_params.StandardStructuredAddressInputAddress, + geocode: bool | NotGiven = NOT_GIVEN, + include_details: bool | NotGiven = NOT_GIVEN, + proper_case: bool | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AddressVerificationVerifyResponse: + """1. + + **Structured Address** — Verify and standardize a structured address (e.g., + with `line1`, `city`, etc.). + 2. **Freeform Address** — Verify and standardize a freeform address written on + one line. For best results, append the ISO 2-letter country code (e.g., `US`, + `CA`) to the end of the line. + + - Specifying `includeDetails=true` will provide additional output as documented + in the `Details` schema. + - Uses 1 lookup for verification, and 1 more if geocoding (unless your contract + says otherwise). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["address"]) + def verify( + self, + *, + address: str | address_verification_verify_params.StandardStructuredAddressInputAddress, + geocode: bool | NotGiven = NOT_GIVEN, + include_details: bool | NotGiven = NOT_GIVEN, + proper_case: bool | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AddressVerificationVerifyResponse: + return self._post( + "/v1/addver/verifications", + body=maybe_transform( + {"address": address}, address_verification_verify_params.AddressVerificationVerifyParams + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "geocode": geocode, + "include_details": include_details, + "proper_case": proper_case, + }, + address_verification_verify_params.AddressVerificationVerifyParams, + ), + ), + cast_to=AddressVerificationVerifyResponse, + ) + + +class AsyncAddressVerificationResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncAddressVerificationResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + """ + return AsyncAddressVerificationResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncAddressVerificationResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + """ + return AsyncAddressVerificationResourceWithStreamingResponse(self) + + @overload + async def verify( + self, + *, + address: str, + geocode: bool | NotGiven = NOT_GIVEN, + include_details: bool | NotGiven = NOT_GIVEN, + proper_case: bool | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AddressVerificationVerifyResponse: + """1. + + **Structured Address** — Verify and standardize a structured address (e.g., + with `line1`, `city`, etc.). + 2. **Freeform Address** — Verify and standardize a freeform address written on + one line. For best results, append the ISO 2-letter country code (e.g., `US`, + `CA`) to the end of the line. + + - Specifying `includeDetails=true` will provide additional output as documented + in the `Details` schema. + - Uses 1 lookup for verification, and 1 more if geocoding (unless your contract + says otherwise). + + Args: + address: The address you want to verify, written on a single line. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def verify( + self, + *, + address: address_verification_verify_params.StandardStructuredAddressInputAddress, + geocode: bool | NotGiven = NOT_GIVEN, + include_details: bool | NotGiven = NOT_GIVEN, + proper_case: bool | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AddressVerificationVerifyResponse: + """1. + + **Structured Address** — Verify and standardize a structured address (e.g., + with `line1`, `city`, etc.). + 2. **Freeform Address** — Verify and standardize a freeform address written on + one line. For best results, append the ISO 2-letter country code (e.g., `US`, + `CA`) to the end of the line. + + - Specifying `includeDetails=true` will provide additional output as documented + in the `Details` schema. + - Uses 1 lookup for verification, and 1 more if geocoding (unless your contract + says otherwise). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["address"]) + async def verify( + self, + *, + address: str | address_verification_verify_params.StandardStructuredAddressInputAddress, + geocode: bool | NotGiven = NOT_GIVEN, + include_details: bool | NotGiven = NOT_GIVEN, + proper_case: bool | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AddressVerificationVerifyResponse: + return await self._post( + "/v1/addver/verifications", + body=await async_maybe_transform( + {"address": address}, address_verification_verify_params.AddressVerificationVerifyParams + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "geocode": geocode, + "include_details": include_details, + "proper_case": proper_case, + }, + address_verification_verify_params.AddressVerificationVerifyParams, + ), + ), + cast_to=AddressVerificationVerifyResponse, + ) + + +class AddressVerificationResourceWithRawResponse: + def __init__(self, address_verification: AddressVerificationResource) -> None: + self._address_verification = address_verification + + self.verify = to_raw_response_wrapper( + address_verification.verify, + ) + + +class AsyncAddressVerificationResourceWithRawResponse: + def __init__(self, address_verification: AsyncAddressVerificationResource) -> None: + self._address_verification = address_verification + + self.verify = async_to_raw_response_wrapper( + address_verification.verify, + ) + + +class AddressVerificationResourceWithStreamingResponse: + def __init__(self, address_verification: AddressVerificationResource) -> None: + self._address_verification = address_verification + + self.verify = to_streamed_response_wrapper( + address_verification.verify, + ) + + +class AsyncAddressVerificationResourceWithStreamingResponse: + def __init__(self, address_verification: AsyncAddressVerificationResource) -> None: + self._address_verification = address_verification + + self.verify = async_to_streamed_response_wrapper( + address_verification.verify, + ) diff --git a/src/postgrid/resources/intl_address_verification.py b/src/postgrid/resources/intl_address_verification.py new file mode 100644 index 0000000..b281dff --- /dev/null +++ b/src/postgrid/resources/intl_address_verification.py @@ -0,0 +1,319 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import overload + +import httpx + +from ..types import intl_address_verification_verify_params +from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from .._utils import ( + required_args, + maybe_transform, + async_maybe_transform, +) +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from .._base_client import make_request_options +from ..types.intl_address_verification_verify_response import IntlAddressVerificationVerifyResponse + +__all__ = ["IntlAddressVerificationResource", "AsyncIntlAddressVerificationResource"] + + +class IntlAddressVerificationResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> IntlAddressVerificationResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + """ + return IntlAddressVerificationResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> IntlAddressVerificationResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + """ + return IntlAddressVerificationResourceWithStreamingResponse(self) + + @overload + def verify( + self, + *, + address: intl_address_verification_verify_params.StructuredAddressInputAddress, + geo_data: bool | NotGiven = NOT_GIVEN, + include_details: bool | NotGiven = NOT_GIVEN, + proper_case: bool | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> IntlAddressVerificationVerifyResponse: + """ + Verify and standardize an international address. + + - Supports both structured and freeform address inputs. + - Specify `includeDetails=true` to get additional details as per the + `IntlDetails` schema. + - Uses 1 lookup. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def verify( + self, + *, + address: str, + geo_data: bool | NotGiven = NOT_GIVEN, + include_details: bool | NotGiven = NOT_GIVEN, + proper_case: bool | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> IntlAddressVerificationVerifyResponse: + """ + Verify and standardize an international address. + + - Supports both structured and freeform address inputs. + - Specify `includeDetails=true` to get additional details as per the + `IntlDetails` schema. + - Uses 1 lookup. + + Args: + address: The full address as a single string. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["address"]) + def verify( + self, + *, + address: intl_address_verification_verify_params.StructuredAddressInputAddress | str, + geo_data: bool | NotGiven = NOT_GIVEN, + include_details: bool | NotGiven = NOT_GIVEN, + proper_case: bool | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> IntlAddressVerificationVerifyResponse: + return self._post( + "/v1/intl_addver/verifications", + body=maybe_transform( + {"address": address}, intl_address_verification_verify_params.IntlAddressVerificationVerifyParams + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "geo_data": geo_data, + "include_details": include_details, + "proper_case": proper_case, + }, + intl_address_verification_verify_params.IntlAddressVerificationVerifyParams, + ), + ), + cast_to=IntlAddressVerificationVerifyResponse, + ) + + +class AsyncIntlAddressVerificationResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncIntlAddressVerificationResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + """ + return AsyncIntlAddressVerificationResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncIntlAddressVerificationResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + """ + return AsyncIntlAddressVerificationResourceWithStreamingResponse(self) + + @overload + async def verify( + self, + *, + address: intl_address_verification_verify_params.StructuredAddressInputAddress, + geo_data: bool | NotGiven = NOT_GIVEN, + include_details: bool | NotGiven = NOT_GIVEN, + proper_case: bool | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> IntlAddressVerificationVerifyResponse: + """ + Verify and standardize an international address. + + - Supports both structured and freeform address inputs. + - Specify `includeDetails=true` to get additional details as per the + `IntlDetails` schema. + - Uses 1 lookup. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def verify( + self, + *, + address: str, + geo_data: bool | NotGiven = NOT_GIVEN, + include_details: bool | NotGiven = NOT_GIVEN, + proper_case: bool | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> IntlAddressVerificationVerifyResponse: + """ + Verify and standardize an international address. + + - Supports both structured and freeform address inputs. + - Specify `includeDetails=true` to get additional details as per the + `IntlDetails` schema. + - Uses 1 lookup. + + Args: + address: The full address as a single string. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["address"]) + async def verify( + self, + *, + address: intl_address_verification_verify_params.StructuredAddressInputAddress | str, + geo_data: bool | NotGiven = NOT_GIVEN, + include_details: bool | NotGiven = NOT_GIVEN, + proper_case: bool | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> IntlAddressVerificationVerifyResponse: + return await self._post( + "/v1/intl_addver/verifications", + body=await async_maybe_transform( + {"address": address}, intl_address_verification_verify_params.IntlAddressVerificationVerifyParams + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "geo_data": geo_data, + "include_details": include_details, + "proper_case": proper_case, + }, + intl_address_verification_verify_params.IntlAddressVerificationVerifyParams, + ), + ), + cast_to=IntlAddressVerificationVerifyResponse, + ) + + +class IntlAddressVerificationResourceWithRawResponse: + def __init__(self, intl_address_verification: IntlAddressVerificationResource) -> None: + self._intl_address_verification = intl_address_verification + + self.verify = to_raw_response_wrapper( + intl_address_verification.verify, + ) + + +class AsyncIntlAddressVerificationResourceWithRawResponse: + def __init__(self, intl_address_verification: AsyncIntlAddressVerificationResource) -> None: + self._intl_address_verification = intl_address_verification + + self.verify = async_to_raw_response_wrapper( + intl_address_verification.verify, + ) + + +class IntlAddressVerificationResourceWithStreamingResponse: + def __init__(self, intl_address_verification: IntlAddressVerificationResource) -> None: + self._intl_address_verification = intl_address_verification + + self.verify = to_streamed_response_wrapper( + intl_address_verification.verify, + ) + + +class AsyncIntlAddressVerificationResourceWithStreamingResponse: + def __init__(self, intl_address_verification: AsyncIntlAddressVerificationResource) -> None: + self._intl_address_verification = intl_address_verification + + self.verify = async_to_streamed_response_wrapper( + intl_address_verification.verify, + ) diff --git a/src/postgrid/resources/print_mail/__init__.py b/src/postgrid/resources/print_mail/__init__.py new file mode 100644 index 0000000..c3138c6 --- /dev/null +++ b/src/postgrid/resources/print_mail/__init__.py @@ -0,0 +1,215 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .boxes import ( + BoxesResource, + AsyncBoxesResource, + BoxesResourceWithRawResponse, + AsyncBoxesResourceWithRawResponse, + BoxesResourceWithStreamingResponse, + AsyncBoxesResourceWithStreamingResponse, +) +from .cheques import ( + ChequesResource, + AsyncChequesResource, + ChequesResourceWithRawResponse, + AsyncChequesResourceWithRawResponse, + ChequesResourceWithStreamingResponse, + AsyncChequesResourceWithStreamingResponse, +) +from .letters import ( + LettersResource, + AsyncLettersResource, + LettersResourceWithRawResponse, + AsyncLettersResourceWithRawResponse, + LettersResourceWithStreamingResponse, + AsyncLettersResourceWithStreamingResponse, +) +from .reports import ( + ReportsResource, + AsyncReportsResource, + ReportsResourceWithRawResponse, + AsyncReportsResourceWithRawResponse, + ReportsResourceWithStreamingResponse, + AsyncReportsResourceWithStreamingResponse, +) +from .contacts import ( + ContactsResource, + AsyncContactsResource, + ContactsResourceWithRawResponse, + AsyncContactsResourceWithRawResponse, + ContactsResourceWithStreamingResponse, + AsyncContactsResourceWithStreamingResponse, +) +from .campaigns import ( + CampaignsResource, + AsyncCampaignsResource, + CampaignsResourceWithRawResponse, + AsyncCampaignsResourceWithRawResponse, + CampaignsResourceWithStreamingResponse, + AsyncCampaignsResourceWithStreamingResponse, +) +from .postcards import ( + PostcardsResource, + AsyncPostcardsResource, + PostcardsResourceWithRawResponse, + AsyncPostcardsResourceWithRawResponse, + PostcardsResourceWithStreamingResponse, + AsyncPostcardsResourceWithStreamingResponse, +) +from .templates import ( + TemplatesResource, + AsyncTemplatesResource, + TemplatesResourceWithRawResponse, + AsyncTemplatesResourceWithRawResponse, + TemplatesResourceWithStreamingResponse, + AsyncTemplatesResourceWithStreamingResponse, +) +from .print_mail import ( + PrintMailResource, + AsyncPrintMailResource, + PrintMailResourceWithRawResponse, + AsyncPrintMailResourceWithRawResponse, + PrintMailResourceWithStreamingResponse, + AsyncPrintMailResourceWithStreamingResponse, +) +from .self_mailers import ( + SelfMailersResource, + AsyncSelfMailersResource, + SelfMailersResourceWithRawResponse, + AsyncSelfMailersResourceWithRawResponse, + SelfMailersResourceWithStreamingResponse, + AsyncSelfMailersResourceWithStreamingResponse, +) +from .bank_accounts import ( + BankAccountsResource, + AsyncBankAccountsResource, + BankAccountsResourceWithRawResponse, + AsyncBankAccountsResourceWithRawResponse, + BankAccountsResourceWithStreamingResponse, + AsyncBankAccountsResourceWithStreamingResponse, +) +from .mailing_lists import ( + MailingListsResource, + AsyncMailingListsResource, + MailingListsResourceWithRawResponse, + AsyncMailingListsResourceWithRawResponse, + MailingListsResourceWithStreamingResponse, + AsyncMailingListsResourceWithStreamingResponse, +) +from .order_profiles import ( + OrderProfilesResource, + AsyncOrderProfilesResource, + OrderProfilesResourceWithRawResponse, + AsyncOrderProfilesResourceWithRawResponse, + OrderProfilesResourceWithStreamingResponse, + AsyncOrderProfilesResourceWithStreamingResponse, +) +from .sub_organizations import ( + SubOrganizationsResource, + AsyncSubOrganizationsResource, + SubOrganizationsResourceWithRawResponse, + AsyncSubOrganizationsResourceWithRawResponse, + SubOrganizationsResourceWithStreamingResponse, + AsyncSubOrganizationsResourceWithStreamingResponse, +) +from .mailing_list_imports import ( + MailingListImportsResource, + AsyncMailingListImportsResource, + MailingListImportsResourceWithRawResponse, + AsyncMailingListImportsResourceWithRawResponse, + MailingListImportsResourceWithStreamingResponse, + AsyncMailingListImportsResourceWithStreamingResponse, +) + +__all__ = [ + "BankAccountsResource", + "AsyncBankAccountsResource", + "BankAccountsResourceWithRawResponse", + "AsyncBankAccountsResourceWithRawResponse", + "BankAccountsResourceWithStreamingResponse", + "AsyncBankAccountsResourceWithStreamingResponse", + "BoxesResource", + "AsyncBoxesResource", + "BoxesResourceWithRawResponse", + "AsyncBoxesResourceWithRawResponse", + "BoxesResourceWithStreamingResponse", + "AsyncBoxesResourceWithStreamingResponse", + "CampaignsResource", + "AsyncCampaignsResource", + "CampaignsResourceWithRawResponse", + "AsyncCampaignsResourceWithRawResponse", + "CampaignsResourceWithStreamingResponse", + "AsyncCampaignsResourceWithStreamingResponse", + "ChequesResource", + "AsyncChequesResource", + "ChequesResourceWithRawResponse", + "AsyncChequesResourceWithRawResponse", + "ChequesResourceWithStreamingResponse", + "AsyncChequesResourceWithStreamingResponse", + "ContactsResource", + "AsyncContactsResource", + "ContactsResourceWithRawResponse", + "AsyncContactsResourceWithRawResponse", + "ContactsResourceWithStreamingResponse", + "AsyncContactsResourceWithStreamingResponse", + "LettersResource", + "AsyncLettersResource", + "LettersResourceWithRawResponse", + "AsyncLettersResourceWithRawResponse", + "LettersResourceWithStreamingResponse", + "AsyncLettersResourceWithStreamingResponse", + "MailingListImportsResource", + "AsyncMailingListImportsResource", + "MailingListImportsResourceWithRawResponse", + "AsyncMailingListImportsResourceWithRawResponse", + "MailingListImportsResourceWithStreamingResponse", + "AsyncMailingListImportsResourceWithStreamingResponse", + "MailingListsResource", + "AsyncMailingListsResource", + "MailingListsResourceWithRawResponse", + "AsyncMailingListsResourceWithRawResponse", + "MailingListsResourceWithStreamingResponse", + "AsyncMailingListsResourceWithStreamingResponse", + "OrderProfilesResource", + "AsyncOrderProfilesResource", + "OrderProfilesResourceWithRawResponse", + "AsyncOrderProfilesResourceWithRawResponse", + "OrderProfilesResourceWithStreamingResponse", + "AsyncOrderProfilesResourceWithStreamingResponse", + "PostcardsResource", + "AsyncPostcardsResource", + "PostcardsResourceWithRawResponse", + "AsyncPostcardsResourceWithRawResponse", + "PostcardsResourceWithStreamingResponse", + "AsyncPostcardsResourceWithStreamingResponse", + "ReportsResource", + "AsyncReportsResource", + "ReportsResourceWithRawResponse", + "AsyncReportsResourceWithRawResponse", + "ReportsResourceWithStreamingResponse", + "AsyncReportsResourceWithStreamingResponse", + "SelfMailersResource", + "AsyncSelfMailersResource", + "SelfMailersResourceWithRawResponse", + "AsyncSelfMailersResourceWithRawResponse", + "SelfMailersResourceWithStreamingResponse", + "AsyncSelfMailersResourceWithStreamingResponse", + "SubOrganizationsResource", + "AsyncSubOrganizationsResource", + "SubOrganizationsResourceWithRawResponse", + "AsyncSubOrganizationsResourceWithRawResponse", + "SubOrganizationsResourceWithStreamingResponse", + "AsyncSubOrganizationsResourceWithStreamingResponse", + "TemplatesResource", + "AsyncTemplatesResource", + "TemplatesResourceWithRawResponse", + "AsyncTemplatesResourceWithRawResponse", + "TemplatesResourceWithStreamingResponse", + "AsyncTemplatesResourceWithStreamingResponse", + "PrintMailResource", + "AsyncPrintMailResource", + "PrintMailResourceWithRawResponse", + "AsyncPrintMailResourceWithRawResponse", + "PrintMailResourceWithStreamingResponse", + "AsyncPrintMailResourceWithStreamingResponse", +] diff --git a/src/postgrid/resources/print_mail/bank_accounts.py b/src/postgrid/resources/print_mail/bank_accounts.py new file mode 100644 index 0000000..981c838 --- /dev/null +++ b/src/postgrid/resources/print_mail/bank_accounts.py @@ -0,0 +1,918 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union +from typing_extensions import overload + +import httpx + +from ..._types import ( + NOT_GIVEN, + Body, + Query, + Headers, + NotGiven, + Base64FileInput, +) +from ..._utils import ( + required_args, + maybe_transform, + async_maybe_transform, +) +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...pagination import SyncSkipLimit, AsyncSkipLimit +from ..._base_client import AsyncPaginator, make_request_options +from ...types.print_mail import BankAccountCountryCode, bank_account_list_params, bank_account_create_params +from ...types.print_mail.bank_account import BankAccount +from ...types.print_mail.bank_account_country_code import BankAccountCountryCode +from ...types.print_mail.bank_account_delete_response import BankAccountDeleteResponse + +__all__ = ["BankAccountsResource", "AsyncBankAccountsResource"] + + +class BankAccountsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> BankAccountsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + """ + return BankAccountsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> BankAccountsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + """ + return BankAccountsResourceWithStreamingResponse(self) + + @overload + def create( + self, + *, + account_number: str, + bank_country_code: BankAccountCountryCode, + bank_name: str, + signature_text: str, + bank_primary_line: str | NotGiven = NOT_GIVEN, + bank_secondary_line: str | NotGiven = NOT_GIVEN, + ca_designation_number: str | NotGiven = NOT_GIVEN, + description: str | NotGiven = NOT_GIVEN, + metadata: Dict[str, object] | NotGiven = NOT_GIVEN, + route_number: str | NotGiven = NOT_GIVEN, + routing_number: str | NotGiven = NOT_GIVEN, + transit_number: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> BankAccount: + """Create a bank account. + + A US bank account is created by setting `bankCountryCode` + to `US` and providing `accountNumber` and `routingNumber`. A canadian bank + account can be created by specifying `bankCountryCode` as `CA` and setting + `accountNumber`, `routeNumber`, and `transitNumber` accordingly. + + You must specify _either_ `signatureImage` or `signatureText`. The image can be + supplied as either a URL or a multipart file upload. + + Args: + account_number: The account number of the bank account. + + bank_country_code: Countries typically have different bank account formats and standards. These are + the countries which PostGrid's bank accounts API supports. + + bank_name: The name of the bank. + + signature_text: The signature text of the bank account. + + bank_primary_line: The primary address line of the bank. + + bank_secondary_line: The secondary address line of the bank. + + ca_designation_number: The designation number of the bank account (for CA). + + description: An optional string describing this resource. Will be visible in the API and the + dashboard. + + metadata: See the section on Metadata. + + route_number: The route number of the bank account (for CA). + + routing_number: The routing number of the bank account (for US). + + transit_number: The transit number of the bank account (for CA). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def create( + self, + *, + account_number: str, + bank_country_code: BankAccountCountryCode, + bank_name: str, + signature_image: str, + bank_primary_line: str | NotGiven = NOT_GIVEN, + bank_secondary_line: str | NotGiven = NOT_GIVEN, + ca_designation_number: str | NotGiven = NOT_GIVEN, + description: str | NotGiven = NOT_GIVEN, + metadata: Dict[str, object] | NotGiven = NOT_GIVEN, + route_number: str | NotGiven = NOT_GIVEN, + routing_number: str | NotGiven = NOT_GIVEN, + transit_number: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> BankAccount: + """Create a bank account. + + A US bank account is created by setting `bankCountryCode` + to `US` and providing `accountNumber` and `routingNumber`. A canadian bank + account can be created by specifying `bankCountryCode` as `CA` and setting + `accountNumber`, `routeNumber`, and `transitNumber` accordingly. + + You must specify _either_ `signatureImage` or `signatureText`. The image can be + supplied as either a URL or a multipart file upload. + + Args: + account_number: The account number of the bank account. + + bank_country_code: Countries typically have different bank account formats and standards. These are + the countries which PostGrid's bank accounts API supports. + + bank_name: The name of the bank. + + signature_image: Link to signature image which PostGrid will download and apply to cheques + created with this bank account. + + bank_primary_line: The primary address line of the bank. + + bank_secondary_line: The secondary address line of the bank. + + ca_designation_number: The designation number of the bank account (for CA). + + description: An optional string describing this resource. Will be visible in the API and the + dashboard. + + metadata: See the section on Metadata. + + route_number: The route number of the bank account (for CA). + + routing_number: The routing number of the bank account (for US). + + transit_number: The transit number of the bank account (for CA). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def create( + self, + *, + account_number: str, + bank_country_code: BankAccountCountryCode, + bank_name: str, + signature_image: Union[str, Base64FileInput], + bank_primary_line: str | NotGiven = NOT_GIVEN, + bank_secondary_line: str | NotGiven = NOT_GIVEN, + ca_designation_number: str | NotGiven = NOT_GIVEN, + description: str | NotGiven = NOT_GIVEN, + metadata: Dict[str, object] | NotGiven = NOT_GIVEN, + route_number: str | NotGiven = NOT_GIVEN, + routing_number: str | NotGiven = NOT_GIVEN, + transit_number: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> BankAccount: + """Create a bank account. + + A US bank account is created by setting `bankCountryCode` + to `US` and providing `accountNumber` and `routingNumber`. A canadian bank + account can be created by specifying `bankCountryCode` as `CA` and setting + `accountNumber`, `routeNumber`, and `transitNumber` accordingly. + + You must specify _either_ `signatureImage` or `signatureText`. The image can be + supplied as either a URL or a multipart file upload. + + Args: + account_number: The account number of the bank account. + + bank_country_code: Countries typically have different bank account formats and standards. These are + the countries which PostGrid's bank accounts API supports. + + bank_name: The name of the bank. + + signature_image: A PNG or JPEG file which PostGrid will apply to checks created with this bank + account. + + bank_primary_line: The primary address line of the bank. + + bank_secondary_line: The secondary address line of the bank. + + ca_designation_number: The designation number of the bank account (for CA). + + description: An optional string describing this resource. Will be visible in the API and the + dashboard. + + metadata: See the section on Metadata. + + route_number: The route number of the bank account (for CA). + + routing_number: The routing number of the bank account (for US). + + transit_number: The transit number of the bank account (for CA). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args( + ["account_number", "bank_country_code", "bank_name", "signature_text"], + ["account_number", "bank_country_code", "bank_name", "signature_image"], + ) + def create( + self, + *, + account_number: str, + bank_country_code: BankAccountCountryCode, + bank_name: str, + signature_text: str | NotGiven = NOT_GIVEN, + bank_primary_line: str | NotGiven = NOT_GIVEN, + bank_secondary_line: str | NotGiven = NOT_GIVEN, + ca_designation_number: str | NotGiven = NOT_GIVEN, + description: str | NotGiven = NOT_GIVEN, + metadata: Dict[str, object] | NotGiven = NOT_GIVEN, + route_number: str | NotGiven = NOT_GIVEN, + routing_number: str | NotGiven = NOT_GIVEN, + transit_number: str | NotGiven = NOT_GIVEN, + signature_image: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> BankAccount: + return self._post( + "/print-mail/v1/bank_accounts", + body=maybe_transform( + { + "account_number": account_number, + "bank_country_code": bank_country_code, + "bank_name": bank_name, + "signature_text": signature_text, + "bank_primary_line": bank_primary_line, + "bank_secondary_line": bank_secondary_line, + "ca_designation_number": ca_designation_number, + "description": description, + "metadata": metadata, + "route_number": route_number, + "routing_number": routing_number, + "transit_number": transit_number, + "signature_image": signature_image, + }, + bank_account_create_params.BankAccountCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=BankAccount, + ) + + def retrieve( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> BankAccount: + """ + Retrieve a bank account by ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._get( + f"/print-mail/v1/bank_accounts/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=BankAccount, + ) + + def list( + self, + *, + limit: int | NotGiven = NOT_GIVEN, + search: str | NotGiven = NOT_GIVEN, + skip: int | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SyncSkipLimit[BankAccount]: + """ + Get a list of bank accounts. + + Args: + search: You can supply any string to help narrow down the list of resources. For + example, if you pass `"New York"` (quoted), it will return resources that have + that string present somewhere in their response. Alternatively, you can supply a + structured search query. See the documentation on `StructuredSearchQuery` for + more details. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/print-mail/v1/bank_accounts", + page=SyncSkipLimit[BankAccount], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "limit": limit, + "search": search, + "skip": skip, + }, + bank_account_list_params.BankAccountListParams, + ), + ), + model=BankAccount, + ) + + def delete( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> BankAccountDeleteResponse: + """Delete a bank account by ID. + + Note that this operation cannot be undone. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._delete( + f"/print-mail/v1/bank_accounts/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=BankAccountDeleteResponse, + ) + + +class AsyncBankAccountsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncBankAccountsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + """ + return AsyncBankAccountsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncBankAccountsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + """ + return AsyncBankAccountsResourceWithStreamingResponse(self) + + @overload + async def create( + self, + *, + account_number: str, + bank_country_code: BankAccountCountryCode, + bank_name: str, + signature_text: str, + bank_primary_line: str | NotGiven = NOT_GIVEN, + bank_secondary_line: str | NotGiven = NOT_GIVEN, + ca_designation_number: str | NotGiven = NOT_GIVEN, + description: str | NotGiven = NOT_GIVEN, + metadata: Dict[str, object] | NotGiven = NOT_GIVEN, + route_number: str | NotGiven = NOT_GIVEN, + routing_number: str | NotGiven = NOT_GIVEN, + transit_number: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> BankAccount: + """Create a bank account. + + A US bank account is created by setting `bankCountryCode` + to `US` and providing `accountNumber` and `routingNumber`. A canadian bank + account can be created by specifying `bankCountryCode` as `CA` and setting + `accountNumber`, `routeNumber`, and `transitNumber` accordingly. + + You must specify _either_ `signatureImage` or `signatureText`. The image can be + supplied as either a URL or a multipart file upload. + + Args: + account_number: The account number of the bank account. + + bank_country_code: Countries typically have different bank account formats and standards. These are + the countries which PostGrid's bank accounts API supports. + + bank_name: The name of the bank. + + signature_text: The signature text of the bank account. + + bank_primary_line: The primary address line of the bank. + + bank_secondary_line: The secondary address line of the bank. + + ca_designation_number: The designation number of the bank account (for CA). + + description: An optional string describing this resource. Will be visible in the API and the + dashboard. + + metadata: See the section on Metadata. + + route_number: The route number of the bank account (for CA). + + routing_number: The routing number of the bank account (for US). + + transit_number: The transit number of the bank account (for CA). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def create( + self, + *, + account_number: str, + bank_country_code: BankAccountCountryCode, + bank_name: str, + signature_image: str, + bank_primary_line: str | NotGiven = NOT_GIVEN, + bank_secondary_line: str | NotGiven = NOT_GIVEN, + ca_designation_number: str | NotGiven = NOT_GIVEN, + description: str | NotGiven = NOT_GIVEN, + metadata: Dict[str, object] | NotGiven = NOT_GIVEN, + route_number: str | NotGiven = NOT_GIVEN, + routing_number: str | NotGiven = NOT_GIVEN, + transit_number: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> BankAccount: + """Create a bank account. + + A US bank account is created by setting `bankCountryCode` + to `US` and providing `accountNumber` and `routingNumber`. A canadian bank + account can be created by specifying `bankCountryCode` as `CA` and setting + `accountNumber`, `routeNumber`, and `transitNumber` accordingly. + + You must specify _either_ `signatureImage` or `signatureText`. The image can be + supplied as either a URL or a multipart file upload. + + Args: + account_number: The account number of the bank account. + + bank_country_code: Countries typically have different bank account formats and standards. These are + the countries which PostGrid's bank accounts API supports. + + bank_name: The name of the bank. + + signature_image: Link to signature image which PostGrid will download and apply to cheques + created with this bank account. + + bank_primary_line: The primary address line of the bank. + + bank_secondary_line: The secondary address line of the bank. + + ca_designation_number: The designation number of the bank account (for CA). + + description: An optional string describing this resource. Will be visible in the API and the + dashboard. + + metadata: See the section on Metadata. + + route_number: The route number of the bank account (for CA). + + routing_number: The routing number of the bank account (for US). + + transit_number: The transit number of the bank account (for CA). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def create( + self, + *, + account_number: str, + bank_country_code: BankAccountCountryCode, + bank_name: str, + signature_image: Union[str, Base64FileInput], + bank_primary_line: str | NotGiven = NOT_GIVEN, + bank_secondary_line: str | NotGiven = NOT_GIVEN, + ca_designation_number: str | NotGiven = NOT_GIVEN, + description: str | NotGiven = NOT_GIVEN, + metadata: Dict[str, object] | NotGiven = NOT_GIVEN, + route_number: str | NotGiven = NOT_GIVEN, + routing_number: str | NotGiven = NOT_GIVEN, + transit_number: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> BankAccount: + """Create a bank account. + + A US bank account is created by setting `bankCountryCode` + to `US` and providing `accountNumber` and `routingNumber`. A canadian bank + account can be created by specifying `bankCountryCode` as `CA` and setting + `accountNumber`, `routeNumber`, and `transitNumber` accordingly. + + You must specify _either_ `signatureImage` or `signatureText`. The image can be + supplied as either a URL or a multipart file upload. + + Args: + account_number: The account number of the bank account. + + bank_country_code: Countries typically have different bank account formats and standards. These are + the countries which PostGrid's bank accounts API supports. + + bank_name: The name of the bank. + + signature_image: A PNG or JPEG file which PostGrid will apply to checks created with this bank + account. + + bank_primary_line: The primary address line of the bank. + + bank_secondary_line: The secondary address line of the bank. + + ca_designation_number: The designation number of the bank account (for CA). + + description: An optional string describing this resource. Will be visible in the API and the + dashboard. + + metadata: See the section on Metadata. + + route_number: The route number of the bank account (for CA). + + routing_number: The routing number of the bank account (for US). + + transit_number: The transit number of the bank account (for CA). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args( + ["account_number", "bank_country_code", "bank_name", "signature_text"], + ["account_number", "bank_country_code", "bank_name", "signature_image"], + ) + async def create( + self, + *, + account_number: str, + bank_country_code: BankAccountCountryCode, + bank_name: str, + signature_text: str | NotGiven = NOT_GIVEN, + bank_primary_line: str | NotGiven = NOT_GIVEN, + bank_secondary_line: str | NotGiven = NOT_GIVEN, + ca_designation_number: str | NotGiven = NOT_GIVEN, + description: str | NotGiven = NOT_GIVEN, + metadata: Dict[str, object] | NotGiven = NOT_GIVEN, + route_number: str | NotGiven = NOT_GIVEN, + routing_number: str | NotGiven = NOT_GIVEN, + transit_number: str | NotGiven = NOT_GIVEN, + signature_image: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> BankAccount: + return await self._post( + "/print-mail/v1/bank_accounts", + body=await async_maybe_transform( + { + "account_number": account_number, + "bank_country_code": bank_country_code, + "bank_name": bank_name, + "signature_text": signature_text, + "bank_primary_line": bank_primary_line, + "bank_secondary_line": bank_secondary_line, + "ca_designation_number": ca_designation_number, + "description": description, + "metadata": metadata, + "route_number": route_number, + "routing_number": routing_number, + "transit_number": transit_number, + "signature_image": signature_image, + }, + bank_account_create_params.BankAccountCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=BankAccount, + ) + + async def retrieve( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> BankAccount: + """ + Retrieve a bank account by ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._get( + f"/print-mail/v1/bank_accounts/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=BankAccount, + ) + + def list( + self, + *, + limit: int | NotGiven = NOT_GIVEN, + search: str | NotGiven = NOT_GIVEN, + skip: int | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncPaginator[BankAccount, AsyncSkipLimit[BankAccount]]: + """ + Get a list of bank accounts. + + Args: + search: You can supply any string to help narrow down the list of resources. For + example, if you pass `"New York"` (quoted), it will return resources that have + that string present somewhere in their response. Alternatively, you can supply a + structured search query. See the documentation on `StructuredSearchQuery` for + more details. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/print-mail/v1/bank_accounts", + page=AsyncSkipLimit[BankAccount], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "limit": limit, + "search": search, + "skip": skip, + }, + bank_account_list_params.BankAccountListParams, + ), + ), + model=BankAccount, + ) + + async def delete( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> BankAccountDeleteResponse: + """Delete a bank account by ID. + + Note that this operation cannot be undone. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._delete( + f"/print-mail/v1/bank_accounts/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=BankAccountDeleteResponse, + ) + + +class BankAccountsResourceWithRawResponse: + def __init__(self, bank_accounts: BankAccountsResource) -> None: + self._bank_accounts = bank_accounts + + self.create = to_raw_response_wrapper( + bank_accounts.create, + ) + self.retrieve = to_raw_response_wrapper( + bank_accounts.retrieve, + ) + self.list = to_raw_response_wrapper( + bank_accounts.list, + ) + self.delete = to_raw_response_wrapper( + bank_accounts.delete, + ) + + +class AsyncBankAccountsResourceWithRawResponse: + def __init__(self, bank_accounts: AsyncBankAccountsResource) -> None: + self._bank_accounts = bank_accounts + + self.create = async_to_raw_response_wrapper( + bank_accounts.create, + ) + self.retrieve = async_to_raw_response_wrapper( + bank_accounts.retrieve, + ) + self.list = async_to_raw_response_wrapper( + bank_accounts.list, + ) + self.delete = async_to_raw_response_wrapper( + bank_accounts.delete, + ) + + +class BankAccountsResourceWithStreamingResponse: + def __init__(self, bank_accounts: BankAccountsResource) -> None: + self._bank_accounts = bank_accounts + + self.create = to_streamed_response_wrapper( + bank_accounts.create, + ) + self.retrieve = to_streamed_response_wrapper( + bank_accounts.retrieve, + ) + self.list = to_streamed_response_wrapper( + bank_accounts.list, + ) + self.delete = to_streamed_response_wrapper( + bank_accounts.delete, + ) + + +class AsyncBankAccountsResourceWithStreamingResponse: + def __init__(self, bank_accounts: AsyncBankAccountsResource) -> None: + self._bank_accounts = bank_accounts + + self.create = async_to_streamed_response_wrapper( + bank_accounts.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + bank_accounts.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + bank_accounts.list, + ) + self.delete = async_to_streamed_response_wrapper( + bank_accounts.delete, + ) diff --git a/src/postgrid/resources/print_mail/boxes.py b/src/postgrid/resources/print_mail/boxes.py new file mode 100644 index 0000000..dfbcadb --- /dev/null +++ b/src/postgrid/resources/print_mail/boxes.py @@ -0,0 +1,565 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union, Iterable +from datetime import datetime + +import httpx + +from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._utils import ( + maybe_transform, + async_maybe_transform, +) +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...pagination import SyncSkipLimit, AsyncSkipLimit +from ..._base_client import AsyncPaginator, make_request_options +from ...types.print_mail import OrderMailingClass, box_list_params, box_create_params +from ...types.print_mail.box import Box +from ...types.print_mail.order_mailing_class import OrderMailingClass + +__all__ = ["BoxesResource", "AsyncBoxesResource"] + + +class BoxesResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> BoxesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + """ + return BoxesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> BoxesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + """ + return BoxesResourceWithStreamingResponse(self) + + def create( + self, + *, + cheques: Iterable[box_create_params.Cheque], + from_: box_create_params.From, + to: box_create_params.To, + description: str | NotGiven = NOT_GIVEN, + mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, + merge_variables: Dict[str, object] | NotGiven = NOT_GIVEN, + metadata: Dict[str, object] | NotGiven = NOT_GIVEN, + send_date: Union[str, datetime] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Box: + """This endpoint allows you to create a box containing up to 100 cheques. + + A Box is + mailed to a single destination. + + To create a box. You must specify: + + - `to`: The recipient (contact or contact ID) + - `from`: The sender (contact or contact ID) + - `cheques`: An array of cheques to go in the box + + For each cheque You must specify: + + - `to`: The recipient (contact or contact ID) + - `from`: The sender (contact or contact ID) + - `bankAccount`: The bank account ID + - `amount`: The amount to be sent + - `number`: The cheque number + + Args: + cheques: The cheques to be mailed in the box. Only 100 cheques can be included in a box + at a time. + + from_: The 'from' (sender) of the entire box. Accepts inline ContactCreate or a + contactID. + + to: The recipient of this order. You can either supply the contact information + inline here or provide a contact ID. PostGrid will automatically deduplicate + contacts regardless of whether you provide the information inline here or call + the contact creation endpoint. + + description: An optional string describing this resource. Will be visible in the API and the + dashboard. + + mailing_class: The mailing class of this order. If not provided, automatically set to + `first_class`. + + merge_variables: These will be merged with the variables in the template or HTML you create this + order with. The keys in this object should match the variable names in the + template _exactly_ as they are case-sensitive. Note that these _do not_ apply to + PDFs uploaded with the order. + + metadata: See the section on Metadata. + + send_date: This order will transition from `ready` to `printing` on the day after this + date. You can use this parameter to schedule orders for a future date. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/print-mail/v1/boxes", + body=maybe_transform( + { + "cheques": cheques, + "from_": from_, + "to": to, + "description": description, + "mailing_class": mailing_class, + "merge_variables": merge_variables, + "metadata": metadata, + "send_date": send_date, + }, + box_create_params.BoxCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Box, + ) + + def retrieve( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Box: + """ + Retrieve a box by ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._get( + f"/print-mail/v1/boxes/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Box, + ) + + def list( + self, + *, + limit: int | NotGiven = NOT_GIVEN, + search: str | NotGiven = NOT_GIVEN, + skip: int | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SyncSkipLimit[Box]: + """ + List all boxes. + + Args: + search: You can supply any string to help narrow down the list of resources. For + example, if you pass `"New York"` (quoted), it will return resources that have + that string present somewhere in their response. Alternatively, you can supply a + structured search query. See the documentation on `StructuredSearchQuery` for + more details. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/print-mail/v1/boxes", + page=SyncSkipLimit[Box], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "limit": limit, + "search": search, + "skip": skip, + }, + box_list_params.BoxListParams, + ), + ), + model=Box, + ) + + def delete( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Box: + """ + Cancel a box by ID (cannot be undone). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._delete( + f"/print-mail/v1/boxes/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Box, + ) + + +class AsyncBoxesResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncBoxesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + """ + return AsyncBoxesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncBoxesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + """ + return AsyncBoxesResourceWithStreamingResponse(self) + + async def create( + self, + *, + cheques: Iterable[box_create_params.Cheque], + from_: box_create_params.From, + to: box_create_params.To, + description: str | NotGiven = NOT_GIVEN, + mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, + merge_variables: Dict[str, object] | NotGiven = NOT_GIVEN, + metadata: Dict[str, object] | NotGiven = NOT_GIVEN, + send_date: Union[str, datetime] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Box: + """This endpoint allows you to create a box containing up to 100 cheques. + + A Box is + mailed to a single destination. + + To create a box. You must specify: + + - `to`: The recipient (contact or contact ID) + - `from`: The sender (contact or contact ID) + - `cheques`: An array of cheques to go in the box + + For each cheque You must specify: + + - `to`: The recipient (contact or contact ID) + - `from`: The sender (contact or contact ID) + - `bankAccount`: The bank account ID + - `amount`: The amount to be sent + - `number`: The cheque number + + Args: + cheques: The cheques to be mailed in the box. Only 100 cheques can be included in a box + at a time. + + from_: The 'from' (sender) of the entire box. Accepts inline ContactCreate or a + contactID. + + to: The recipient of this order. You can either supply the contact information + inline here or provide a contact ID. PostGrid will automatically deduplicate + contacts regardless of whether you provide the information inline here or call + the contact creation endpoint. + + description: An optional string describing this resource. Will be visible in the API and the + dashboard. + + mailing_class: The mailing class of this order. If not provided, automatically set to + `first_class`. + + merge_variables: These will be merged with the variables in the template or HTML you create this + order with. The keys in this object should match the variable names in the + template _exactly_ as they are case-sensitive. Note that these _do not_ apply to + PDFs uploaded with the order. + + metadata: See the section on Metadata. + + send_date: This order will transition from `ready` to `printing` on the day after this + date. You can use this parameter to schedule orders for a future date. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/print-mail/v1/boxes", + body=await async_maybe_transform( + { + "cheques": cheques, + "from_": from_, + "to": to, + "description": description, + "mailing_class": mailing_class, + "merge_variables": merge_variables, + "metadata": metadata, + "send_date": send_date, + }, + box_create_params.BoxCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Box, + ) + + async def retrieve( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Box: + """ + Retrieve a box by ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._get( + f"/print-mail/v1/boxes/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Box, + ) + + def list( + self, + *, + limit: int | NotGiven = NOT_GIVEN, + search: str | NotGiven = NOT_GIVEN, + skip: int | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncPaginator[Box, AsyncSkipLimit[Box]]: + """ + List all boxes. + + Args: + search: You can supply any string to help narrow down the list of resources. For + example, if you pass `"New York"` (quoted), it will return resources that have + that string present somewhere in their response. Alternatively, you can supply a + structured search query. See the documentation on `StructuredSearchQuery` for + more details. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/print-mail/v1/boxes", + page=AsyncSkipLimit[Box], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "limit": limit, + "search": search, + "skip": skip, + }, + box_list_params.BoxListParams, + ), + ), + model=Box, + ) + + async def delete( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Box: + """ + Cancel a box by ID (cannot be undone). + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._delete( + f"/print-mail/v1/boxes/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Box, + ) + + +class BoxesResourceWithRawResponse: + def __init__(self, boxes: BoxesResource) -> None: + self._boxes = boxes + + self.create = to_raw_response_wrapper( + boxes.create, + ) + self.retrieve = to_raw_response_wrapper( + boxes.retrieve, + ) + self.list = to_raw_response_wrapper( + boxes.list, + ) + self.delete = to_raw_response_wrapper( + boxes.delete, + ) + + +class AsyncBoxesResourceWithRawResponse: + def __init__(self, boxes: AsyncBoxesResource) -> None: + self._boxes = boxes + + self.create = async_to_raw_response_wrapper( + boxes.create, + ) + self.retrieve = async_to_raw_response_wrapper( + boxes.retrieve, + ) + self.list = async_to_raw_response_wrapper( + boxes.list, + ) + self.delete = async_to_raw_response_wrapper( + boxes.delete, + ) + + +class BoxesResourceWithStreamingResponse: + def __init__(self, boxes: BoxesResource) -> None: + self._boxes = boxes + + self.create = to_streamed_response_wrapper( + boxes.create, + ) + self.retrieve = to_streamed_response_wrapper( + boxes.retrieve, + ) + self.list = to_streamed_response_wrapper( + boxes.list, + ) + self.delete = to_streamed_response_wrapper( + boxes.delete, + ) + + +class AsyncBoxesResourceWithStreamingResponse: + def __init__(self, boxes: AsyncBoxesResource) -> None: + self._boxes = boxes + + self.create = async_to_streamed_response_wrapper( + boxes.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + boxes.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + boxes.list, + ) + self.delete = async_to_streamed_response_wrapper( + boxes.delete, + ) diff --git a/src/postgrid/resources/print_mail/campaigns.py b/src/postgrid/resources/print_mail/campaigns.py new file mode 100644 index 0000000..ca89d8c --- /dev/null +++ b/src/postgrid/resources/print_mail/campaigns.py @@ -0,0 +1,823 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union, Optional +from datetime import datetime + +import httpx + +from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._utils import ( + maybe_transform, + strip_not_given, + async_maybe_transform, +) +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...pagination import SyncSkipLimit, AsyncSkipLimit +from ..._base_client import AsyncPaginator, make_request_options +from ...types.print_mail import ( + campaign_list_params, + campaign_send_params, + campaign_create_params, + campaign_update_params, +) +from ...types.print_mail.campaign import Campaign +from ...types.print_mail.campaign_delete_response import CampaignDeleteResponse + +__all__ = ["CampaignsResource", "AsyncCampaignsResource"] + + +class CampaignsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> CampaignsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + """ + return CampaignsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> CampaignsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + """ + return CampaignsResourceWithStreamingResponse(self) + + def create( + self, + *, + mailing_list: str, + cheque_profile: str | NotGiven = NOT_GIVEN, + default_sender_contact: str | NotGiven = NOT_GIVEN, + description: str | NotGiven = NOT_GIVEN, + letter_profile: str | NotGiven = NOT_GIVEN, + metadata: Dict[str, object] | NotGiven = NOT_GIVEN, + postcard_profile: str | NotGiven = NOT_GIVEN, + self_mailer_profile: str | NotGiven = NOT_GIVEN, + send_date: Union[str, datetime] | NotGiven = NOT_GIVEN, + idempotency_key: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Campaign: + """ + Create a new campaign. + + A campaign links a mailing list with a specific mail piece profile (letter, + postcard, cheque, or self-mailer) to send bulk mail. Upon creation, the campaign + enters the `drafting` status while assets are validated. + + Args: + mailing_list: The ID of the mailing list associated with this campaign. + + cheque_profile: The ID of the cheque profile used for this campaign, if applicable. + + default_sender_contact: The ID of the default sender contact to use for orders if not specified per + recipient. + + description: An optional string describing this resource. Will be visible in the API and the + dashboard. + + letter_profile: The ID of the letter profile used for this campaign, if applicable. + + metadata: See the section on Metadata. + + postcard_profile: The ID of the postcard profile used for this campaign, if applicable. + + self_mailer_profile: The ID of the self-mailer profile used for this campaign, if applicable. + + send_date: The scheduled date and time for the campaign to be sent. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {**strip_not_given({"idempotency-key": idempotency_key}), **(extra_headers or {})} + return self._post( + "/print-mail/v1/campaigns", + body=maybe_transform( + { + "mailing_list": mailing_list, + "cheque_profile": cheque_profile, + "default_sender_contact": default_sender_contact, + "description": description, + "letter_profile": letter_profile, + "metadata": metadata, + "postcard_profile": postcard_profile, + "self_mailer_profile": self_mailer_profile, + "send_date": send_date, + }, + campaign_create_params.CampaignCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Campaign, + ) + + def retrieve( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Campaign: + """ + Retrieve a specific campaign by its ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._get( + f"/print-mail/v1/campaigns/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Campaign, + ) + + def update( + self, + id: str, + *, + cheque_profile: Optional[str] | NotGiven = NOT_GIVEN, + default_sender_contact: Optional[str] | NotGiven = NOT_GIVEN, + description: Optional[str] | NotGiven = NOT_GIVEN, + letter_profile: Optional[str] | NotGiven = NOT_GIVEN, + mailing_list: str | NotGiven = NOT_GIVEN, + metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, + postcard_profile: Optional[str] | NotGiven = NOT_GIVEN, + self_mailer_profile: Optional[str] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Campaign: + """ + Update an existing campaign. + + Campaigns can only be updated if they are in the `draft` or `changes_required` + status. Updating a campaign will trigger reprocessing and set its status back to + `drafting`. + + Args: + cheque_profile: The ID of the cheque profile to use. Setting this will remove other profile + types. Set to `null` to remove. + + default_sender_contact: The ID of the default sender contact. Set to `null` to remove. + + description: An optional description for the campaign. Set to `null` to remove the existing + description. + + letter_profile: The ID of the letter profile to use. Setting this will remove other profile + types. Set to `null` to remove. + + mailing_list: The ID of the mailing list to associate with this campaign. + + metadata: Optional key-value data associated with the campaign. Set to `null` to remove + existing metadata. + + postcard_profile: The ID of the postcard profile to use. Setting this will remove other profile + types. Set to `null` to remove. + + self_mailer_profile: The ID of the self-mailer profile to use. Setting this will remove other profile + types. Set to `null` to remove. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._post( + f"/print-mail/v1/campaigns/{id}", + body=maybe_transform( + { + "cheque_profile": cheque_profile, + "default_sender_contact": default_sender_contact, + "description": description, + "letter_profile": letter_profile, + "mailing_list": mailing_list, + "metadata": metadata, + "postcard_profile": postcard_profile, + "self_mailer_profile": self_mailer_profile, + }, + campaign_update_params.CampaignUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Campaign, + ) + + def list( + self, + *, + limit: int | NotGiven = NOT_GIVEN, + search: str | NotGiven = NOT_GIVEN, + skip: int | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SyncSkipLimit[Campaign]: + """ + Retrieve a list of campaigns. + + Returns a paginated list of campaigns associated with the authenticated + organization, filterable by various parameters. + + Args: + search: You can supply any string to help narrow down the list of resources. For + example, if you pass `"New York"` (quoted), it will return resources that have + that string present somewhere in their response. Alternatively, you can supply a + structured search query. See the documentation on `StructuredSearchQuery` for + more details. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/print-mail/v1/campaigns", + page=SyncSkipLimit[Campaign], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "limit": limit, + "search": search, + "skip": skip, + }, + campaign_list_params.CampaignListParams, + ), + ), + model=Campaign, + ) + + def delete( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> CampaignDeleteResponse: + """ + Delete a campaign. + + Campaigns can only be deleted if they are in `draft`, `changes_required`, or + `ready` status. This also permanently deletes associated resources. This + operation cannot be undone. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._delete( + f"/print-mail/v1/campaigns/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=CampaignDeleteResponse, + ) + + def send( + self, + id: str, + *, + send_date: Union[Union[str, datetime], str] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Campaign: + """ + Send a campaign for processing. + + This action transitions a campaign from the `draft` status to `creating_orders`. + You can optionally specify a future `sendDate`. Once sent, the campaign cannot + be updated. + + Args: + send_date: The date and time the campaign should be sent. Must be in the future. If + omitted, defaults to the earliest possible processing date. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._post( + f"/print-mail/v1/campaigns/{id}/send", + body=maybe_transform({"send_date": send_date}, campaign_send_params.CampaignSendParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Campaign, + ) + + +class AsyncCampaignsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncCampaignsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + """ + return AsyncCampaignsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncCampaignsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + """ + return AsyncCampaignsResourceWithStreamingResponse(self) + + async def create( + self, + *, + mailing_list: str, + cheque_profile: str | NotGiven = NOT_GIVEN, + default_sender_contact: str | NotGiven = NOT_GIVEN, + description: str | NotGiven = NOT_GIVEN, + letter_profile: str | NotGiven = NOT_GIVEN, + metadata: Dict[str, object] | NotGiven = NOT_GIVEN, + postcard_profile: str | NotGiven = NOT_GIVEN, + self_mailer_profile: str | NotGiven = NOT_GIVEN, + send_date: Union[str, datetime] | NotGiven = NOT_GIVEN, + idempotency_key: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Campaign: + """ + Create a new campaign. + + A campaign links a mailing list with a specific mail piece profile (letter, + postcard, cheque, or self-mailer) to send bulk mail. Upon creation, the campaign + enters the `drafting` status while assets are validated. + + Args: + mailing_list: The ID of the mailing list associated with this campaign. + + cheque_profile: The ID of the cheque profile used for this campaign, if applicable. + + default_sender_contact: The ID of the default sender contact to use for orders if not specified per + recipient. + + description: An optional string describing this resource. Will be visible in the API and the + dashboard. + + letter_profile: The ID of the letter profile used for this campaign, if applicable. + + metadata: See the section on Metadata. + + postcard_profile: The ID of the postcard profile used for this campaign, if applicable. + + self_mailer_profile: The ID of the self-mailer profile used for this campaign, if applicable. + + send_date: The scheduled date and time for the campaign to be sent. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {**strip_not_given({"idempotency-key": idempotency_key}), **(extra_headers or {})} + return await self._post( + "/print-mail/v1/campaigns", + body=await async_maybe_transform( + { + "mailing_list": mailing_list, + "cheque_profile": cheque_profile, + "default_sender_contact": default_sender_contact, + "description": description, + "letter_profile": letter_profile, + "metadata": metadata, + "postcard_profile": postcard_profile, + "self_mailer_profile": self_mailer_profile, + "send_date": send_date, + }, + campaign_create_params.CampaignCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Campaign, + ) + + async def retrieve( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Campaign: + """ + Retrieve a specific campaign by its ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._get( + f"/print-mail/v1/campaigns/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Campaign, + ) + + async def update( + self, + id: str, + *, + cheque_profile: Optional[str] | NotGiven = NOT_GIVEN, + default_sender_contact: Optional[str] | NotGiven = NOT_GIVEN, + description: Optional[str] | NotGiven = NOT_GIVEN, + letter_profile: Optional[str] | NotGiven = NOT_GIVEN, + mailing_list: str | NotGiven = NOT_GIVEN, + metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, + postcard_profile: Optional[str] | NotGiven = NOT_GIVEN, + self_mailer_profile: Optional[str] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Campaign: + """ + Update an existing campaign. + + Campaigns can only be updated if they are in the `draft` or `changes_required` + status. Updating a campaign will trigger reprocessing and set its status back to + `drafting`. + + Args: + cheque_profile: The ID of the cheque profile to use. Setting this will remove other profile + types. Set to `null` to remove. + + default_sender_contact: The ID of the default sender contact. Set to `null` to remove. + + description: An optional description for the campaign. Set to `null` to remove the existing + description. + + letter_profile: The ID of the letter profile to use. Setting this will remove other profile + types. Set to `null` to remove. + + mailing_list: The ID of the mailing list to associate with this campaign. + + metadata: Optional key-value data associated with the campaign. Set to `null` to remove + existing metadata. + + postcard_profile: The ID of the postcard profile to use. Setting this will remove other profile + types. Set to `null` to remove. + + self_mailer_profile: The ID of the self-mailer profile to use. Setting this will remove other profile + types. Set to `null` to remove. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._post( + f"/print-mail/v1/campaigns/{id}", + body=await async_maybe_transform( + { + "cheque_profile": cheque_profile, + "default_sender_contact": default_sender_contact, + "description": description, + "letter_profile": letter_profile, + "mailing_list": mailing_list, + "metadata": metadata, + "postcard_profile": postcard_profile, + "self_mailer_profile": self_mailer_profile, + }, + campaign_update_params.CampaignUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Campaign, + ) + + def list( + self, + *, + limit: int | NotGiven = NOT_GIVEN, + search: str | NotGiven = NOT_GIVEN, + skip: int | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncPaginator[Campaign, AsyncSkipLimit[Campaign]]: + """ + Retrieve a list of campaigns. + + Returns a paginated list of campaigns associated with the authenticated + organization, filterable by various parameters. + + Args: + search: You can supply any string to help narrow down the list of resources. For + example, if you pass `"New York"` (quoted), it will return resources that have + that string present somewhere in their response. Alternatively, you can supply a + structured search query. See the documentation on `StructuredSearchQuery` for + more details. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/print-mail/v1/campaigns", + page=AsyncSkipLimit[Campaign], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "limit": limit, + "search": search, + "skip": skip, + }, + campaign_list_params.CampaignListParams, + ), + ), + model=Campaign, + ) + + async def delete( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> CampaignDeleteResponse: + """ + Delete a campaign. + + Campaigns can only be deleted if they are in `draft`, `changes_required`, or + `ready` status. This also permanently deletes associated resources. This + operation cannot be undone. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._delete( + f"/print-mail/v1/campaigns/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=CampaignDeleteResponse, + ) + + async def send( + self, + id: str, + *, + send_date: Union[Union[str, datetime], str] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Campaign: + """ + Send a campaign for processing. + + This action transitions a campaign from the `draft` status to `creating_orders`. + You can optionally specify a future `sendDate`. Once sent, the campaign cannot + be updated. + + Args: + send_date: The date and time the campaign should be sent. Must be in the future. If + omitted, defaults to the earliest possible processing date. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._post( + f"/print-mail/v1/campaigns/{id}/send", + body=await async_maybe_transform({"send_date": send_date}, campaign_send_params.CampaignSendParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Campaign, + ) + + +class CampaignsResourceWithRawResponse: + def __init__(self, campaigns: CampaignsResource) -> None: + self._campaigns = campaigns + + self.create = to_raw_response_wrapper( + campaigns.create, + ) + self.retrieve = to_raw_response_wrapper( + campaigns.retrieve, + ) + self.update = to_raw_response_wrapper( + campaigns.update, + ) + self.list = to_raw_response_wrapper( + campaigns.list, + ) + self.delete = to_raw_response_wrapper( + campaigns.delete, + ) + self.send = to_raw_response_wrapper( + campaigns.send, + ) + + +class AsyncCampaignsResourceWithRawResponse: + def __init__(self, campaigns: AsyncCampaignsResource) -> None: + self._campaigns = campaigns + + self.create = async_to_raw_response_wrapper( + campaigns.create, + ) + self.retrieve = async_to_raw_response_wrapper( + campaigns.retrieve, + ) + self.update = async_to_raw_response_wrapper( + campaigns.update, + ) + self.list = async_to_raw_response_wrapper( + campaigns.list, + ) + self.delete = async_to_raw_response_wrapper( + campaigns.delete, + ) + self.send = async_to_raw_response_wrapper( + campaigns.send, + ) + + +class CampaignsResourceWithStreamingResponse: + def __init__(self, campaigns: CampaignsResource) -> None: + self._campaigns = campaigns + + self.create = to_streamed_response_wrapper( + campaigns.create, + ) + self.retrieve = to_streamed_response_wrapper( + campaigns.retrieve, + ) + self.update = to_streamed_response_wrapper( + campaigns.update, + ) + self.list = to_streamed_response_wrapper( + campaigns.list, + ) + self.delete = to_streamed_response_wrapper( + campaigns.delete, + ) + self.send = to_streamed_response_wrapper( + campaigns.send, + ) + + +class AsyncCampaignsResourceWithStreamingResponse: + def __init__(self, campaigns: AsyncCampaignsResource) -> None: + self._campaigns = campaigns + + self.create = async_to_streamed_response_wrapper( + campaigns.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + campaigns.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + campaigns.update, + ) + self.list = async_to_streamed_response_wrapper( + campaigns.list, + ) + self.delete = async_to_streamed_response_wrapper( + campaigns.delete, + ) + self.send = async_to_streamed_response_wrapper( + campaigns.send, + ) diff --git a/src/postgrid/resources/print_mail/cheques.py b/src/postgrid/resources/print_mail/cheques.py new file mode 100644 index 0000000..6dd5f30 --- /dev/null +++ b/src/postgrid/resources/print_mail/cheques.py @@ -0,0 +1,861 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union +from datetime import datetime +from typing_extensions import Literal + +import httpx + +from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._utils import ( + maybe_transform, + async_maybe_transform, +) +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...pagination import SyncSkipLimit, AsyncSkipLimit +from ..._base_client import AsyncPaginator, make_request_options +from ...types.print_mail import ChequeSize, OrderMailingClass, cheque_list_params, cheque_create_params +from ...types.print_mail.cheque import Cheque +from ...types.print_mail.cheque_size import ChequeSize +from ...types.print_mail.digital_only_param import DigitalOnlyParam +from ...types.print_mail.order_mailing_class import OrderMailingClass +from ...types.print_mail.cheque_retrieve_url_response import ChequeRetrieveURLResponse + +__all__ = ["ChequesResource", "AsyncChequesResource"] + + +class ChequesResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ChequesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + """ + return ChequesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ChequesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + """ + return ChequesResourceWithStreamingResponse(self) + + def create( + self, + *, + amount: int, + bank_account: str, + from_: cheque_create_params.From, + to: cheque_create_params.To, + currency_code: Literal["USD", "CAD"] | NotGiven = NOT_GIVEN, + description: str | NotGiven = NOT_GIVEN, + digital_only: DigitalOnlyParam | NotGiven = NOT_GIVEN, + envelope: Union[Literal["standard"], str] | NotGiven = NOT_GIVEN, + logo_url: str | NotGiven = NOT_GIVEN, + mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, + memo: str | NotGiven = NOT_GIVEN, + merge_variables: Dict[str, object] | NotGiven = NOT_GIVEN, + message: str | NotGiven = NOT_GIVEN, + metadata: Dict[str, object] | NotGiven = NOT_GIVEN, + number: int | NotGiven = NOT_GIVEN, + redirect_to: cheque_create_params.RedirectTo | NotGiven = NOT_GIVEN, + send_date: Union[str, datetime] | NotGiven = NOT_GIVEN, + size: ChequeSize | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Cheque: + """ + Create a cheque. + + This endpoint allows you to create a new cheque with the specified details. + + If you would like to create a digitalOnly cheque, the digitalOnly object with + the watermark will need to be passed in. Feature is available on request, e-mail + support@postgrid.com for access. + + Example request body: + + ```json + { + "from": "contact_123", + "bankAccount": "bank_123", + "amount": 1000, + "currencyCode": "USD", + "number": 123456, + "size": "us_letter", + "digitalOnly": { + "watermark": "VOID" + } + } + ``` + + Args: + amount: The amount of the cheque in cents. + + bank_account: The bank account (ID) associated with the cheque. + + from_: The contact information of the sender. You can pass contact information inline + here just like you can for the `to`. + + to: The recipient of this order. You can either supply the contact information + inline here or provide a contact ID. PostGrid will automatically deduplicate + contacts regardless of whether you provide the information inline here or call + the contact creation endpoint. + + currency_code: The currency code of the cheque. This will be set to the default currency of the + bank account (`USD` for US bank accounts and `CAD` for Canadian bank accounts) + if not provided. You can set this value to `USD` if you want to draw USD from a + Canadian bank account or vice versa. + + description: An optional string describing this resource. Will be visible in the API and the + dashboard. + + digital_only: The digitalOnly object contains data for digital-only cheques. A watermark must + be provided. + + envelope: The envelope of the cheque. If a custom envelope ID is not specified, defaults + to `standard`. + + logo_url: An optional logo URL for the cheque. This will be placed next to the recipient + address at the top left corner of the cheque. This needs to be a public link to + an image file (e.g. a PNG or JPEG file). + + mailing_class: The mailing class of this order. If not provided, automatically set to + `first_class`. + + memo: The memo of the cheque. + + merge_variables: These will be merged with the variables in the template or HTML you create this + order with. The keys in this object should match the variable names in the + template _exactly_ as they are case-sensitive. Note that these _do not_ apply to + PDFs uploaded with the order. + + message: The message of the cheque. + + metadata: See the section on Metadata. + + number: The number of the cheque. If you don't provide this, it will automatically be + set to an incrementing number starting from 1 across your entire account, + ensuring that every cheque has a unique number. + + redirect_to: Providing this inserts a blank page at the start of the cheque with the + recipient you provide here. This leaves the cheque that follows intact, which + means you can use this to intercept at cheque at the redirected address and then + mail it forward to the final recipient yourself. One use case for this is + signing cheques at your office before mailing them out yourself. + + send_date: This order will transition from `ready` to `printing` on the day after this + date. You can use this parameter to schedule orders for a future date. + + size: Enum representing the supported cheque sizes. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/print-mail/v1/cheques", + body=maybe_transform( + { + "amount": amount, + "bank_account": bank_account, + "from_": from_, + "to": to, + "currency_code": currency_code, + "description": description, + "digital_only": digital_only, + "envelope": envelope, + "logo_url": logo_url, + "mailing_class": mailing_class, + "memo": memo, + "merge_variables": merge_variables, + "message": message, + "metadata": metadata, + "number": number, + "redirect_to": redirect_to, + "send_date": send_date, + "size": size, + }, + cheque_create_params.ChequeCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Cheque, + ) + + def retrieve( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Cheque: + """ + Retrieve a cheque by ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._get( + f"/print-mail/v1/cheques/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Cheque, + ) + + def list( + self, + *, + limit: int | NotGiven = NOT_GIVEN, + search: str | NotGiven = NOT_GIVEN, + skip: int | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SyncSkipLimit[Cheque]: + """ + Get a list of cheques. + + Args: + search: You can supply any string to help narrow down the list of resources. For + example, if you pass `"New York"` (quoted), it will return resources that have + that string present somewhere in their response. Alternatively, you can supply a + structured search query. See the documentation on `StructuredSearchQuery` for + more details. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/print-mail/v1/cheques", + page=SyncSkipLimit[Cheque], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "limit": limit, + "search": search, + "skip": skip, + }, + cheque_list_params.ChequeListParams, + ), + ), + model=Cheque, + ) + + def delete( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Cheque: + """Cancel a cheque by ID. + + Note that this operation cannot be undone. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._delete( + f"/print-mail/v1/cheques/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Cheque, + ) + + def retrieve_url( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ChequeRetrieveURLResponse: + """ + Retrieve a cheque preview URL. + + This is only available for customers with our document management addon, which + offers document generation and hosting capabilities. This endpoint has a much + higher rate limit than the regular order retrieval endpoint, so it is suitable + for customer-facing use-cases. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._get( + f"/print-mail/v1/cheques/{id}/url", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ChequeRetrieveURLResponse, + ) + + def retrieve_with_deposit_ready_pdf( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Cheque: + """Retrieve the deposit-ready PDF for a digital-only cheque. + + The endpoint can only + be called by users with 'Admin' role. In test mode, the preview PDF of the + digitalOnly cheque and the deposit-ready PDF are the same. In live mode, the + deposit-ready will have the full account number. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._get( + f"/print-mail/v1/cheques/{id}/with_deposit_ready_pdf", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Cheque, + ) + + +class AsyncChequesResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncChequesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + """ + return AsyncChequesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncChequesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + """ + return AsyncChequesResourceWithStreamingResponse(self) + + async def create( + self, + *, + amount: int, + bank_account: str, + from_: cheque_create_params.From, + to: cheque_create_params.To, + currency_code: Literal["USD", "CAD"] | NotGiven = NOT_GIVEN, + description: str | NotGiven = NOT_GIVEN, + digital_only: DigitalOnlyParam | NotGiven = NOT_GIVEN, + envelope: Union[Literal["standard"], str] | NotGiven = NOT_GIVEN, + logo_url: str | NotGiven = NOT_GIVEN, + mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, + memo: str | NotGiven = NOT_GIVEN, + merge_variables: Dict[str, object] | NotGiven = NOT_GIVEN, + message: str | NotGiven = NOT_GIVEN, + metadata: Dict[str, object] | NotGiven = NOT_GIVEN, + number: int | NotGiven = NOT_GIVEN, + redirect_to: cheque_create_params.RedirectTo | NotGiven = NOT_GIVEN, + send_date: Union[str, datetime] | NotGiven = NOT_GIVEN, + size: ChequeSize | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Cheque: + """ + Create a cheque. + + This endpoint allows you to create a new cheque with the specified details. + + If you would like to create a digitalOnly cheque, the digitalOnly object with + the watermark will need to be passed in. Feature is available on request, e-mail + support@postgrid.com for access. + + Example request body: + + ```json + { + "from": "contact_123", + "bankAccount": "bank_123", + "amount": 1000, + "currencyCode": "USD", + "number": 123456, + "size": "us_letter", + "digitalOnly": { + "watermark": "VOID" + } + } + ``` + + Args: + amount: The amount of the cheque in cents. + + bank_account: The bank account (ID) associated with the cheque. + + from_: The contact information of the sender. You can pass contact information inline + here just like you can for the `to`. + + to: The recipient of this order. You can either supply the contact information + inline here or provide a contact ID. PostGrid will automatically deduplicate + contacts regardless of whether you provide the information inline here or call + the contact creation endpoint. + + currency_code: The currency code of the cheque. This will be set to the default currency of the + bank account (`USD` for US bank accounts and `CAD` for Canadian bank accounts) + if not provided. You can set this value to `USD` if you want to draw USD from a + Canadian bank account or vice versa. + + description: An optional string describing this resource. Will be visible in the API and the + dashboard. + + digital_only: The digitalOnly object contains data for digital-only cheques. A watermark must + be provided. + + envelope: The envelope of the cheque. If a custom envelope ID is not specified, defaults + to `standard`. + + logo_url: An optional logo URL for the cheque. This will be placed next to the recipient + address at the top left corner of the cheque. This needs to be a public link to + an image file (e.g. a PNG or JPEG file). + + mailing_class: The mailing class of this order. If not provided, automatically set to + `first_class`. + + memo: The memo of the cheque. + + merge_variables: These will be merged with the variables in the template or HTML you create this + order with. The keys in this object should match the variable names in the + template _exactly_ as they are case-sensitive. Note that these _do not_ apply to + PDFs uploaded with the order. + + message: The message of the cheque. + + metadata: See the section on Metadata. + + number: The number of the cheque. If you don't provide this, it will automatically be + set to an incrementing number starting from 1 across your entire account, + ensuring that every cheque has a unique number. + + redirect_to: Providing this inserts a blank page at the start of the cheque with the + recipient you provide here. This leaves the cheque that follows intact, which + means you can use this to intercept at cheque at the redirected address and then + mail it forward to the final recipient yourself. One use case for this is + signing cheques at your office before mailing them out yourself. + + send_date: This order will transition from `ready` to `printing` on the day after this + date. You can use this parameter to schedule orders for a future date. + + size: Enum representing the supported cheque sizes. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/print-mail/v1/cheques", + body=await async_maybe_transform( + { + "amount": amount, + "bank_account": bank_account, + "from_": from_, + "to": to, + "currency_code": currency_code, + "description": description, + "digital_only": digital_only, + "envelope": envelope, + "logo_url": logo_url, + "mailing_class": mailing_class, + "memo": memo, + "merge_variables": merge_variables, + "message": message, + "metadata": metadata, + "number": number, + "redirect_to": redirect_to, + "send_date": send_date, + "size": size, + }, + cheque_create_params.ChequeCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Cheque, + ) + + async def retrieve( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Cheque: + """ + Retrieve a cheque by ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._get( + f"/print-mail/v1/cheques/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Cheque, + ) + + def list( + self, + *, + limit: int | NotGiven = NOT_GIVEN, + search: str | NotGiven = NOT_GIVEN, + skip: int | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncPaginator[Cheque, AsyncSkipLimit[Cheque]]: + """ + Get a list of cheques. + + Args: + search: You can supply any string to help narrow down the list of resources. For + example, if you pass `"New York"` (quoted), it will return resources that have + that string present somewhere in their response. Alternatively, you can supply a + structured search query. See the documentation on `StructuredSearchQuery` for + more details. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/print-mail/v1/cheques", + page=AsyncSkipLimit[Cheque], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "limit": limit, + "search": search, + "skip": skip, + }, + cheque_list_params.ChequeListParams, + ), + ), + model=Cheque, + ) + + async def delete( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Cheque: + """Cancel a cheque by ID. + + Note that this operation cannot be undone. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._delete( + f"/print-mail/v1/cheques/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Cheque, + ) + + async def retrieve_url( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ChequeRetrieveURLResponse: + """ + Retrieve a cheque preview URL. + + This is only available for customers with our document management addon, which + offers document generation and hosting capabilities. This endpoint has a much + higher rate limit than the regular order retrieval endpoint, so it is suitable + for customer-facing use-cases. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._get( + f"/print-mail/v1/cheques/{id}/url", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ChequeRetrieveURLResponse, + ) + + async def retrieve_with_deposit_ready_pdf( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Cheque: + """Retrieve the deposit-ready PDF for a digital-only cheque. + + The endpoint can only + be called by users with 'Admin' role. In test mode, the preview PDF of the + digitalOnly cheque and the deposit-ready PDF are the same. In live mode, the + deposit-ready will have the full account number. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._get( + f"/print-mail/v1/cheques/{id}/with_deposit_ready_pdf", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Cheque, + ) + + +class ChequesResourceWithRawResponse: + def __init__(self, cheques: ChequesResource) -> None: + self._cheques = cheques + + self.create = to_raw_response_wrapper( + cheques.create, + ) + self.retrieve = to_raw_response_wrapper( + cheques.retrieve, + ) + self.list = to_raw_response_wrapper( + cheques.list, + ) + self.delete = to_raw_response_wrapper( + cheques.delete, + ) + self.retrieve_url = to_raw_response_wrapper( + cheques.retrieve_url, + ) + self.retrieve_with_deposit_ready_pdf = to_raw_response_wrapper( + cheques.retrieve_with_deposit_ready_pdf, + ) + + +class AsyncChequesResourceWithRawResponse: + def __init__(self, cheques: AsyncChequesResource) -> None: + self._cheques = cheques + + self.create = async_to_raw_response_wrapper( + cheques.create, + ) + self.retrieve = async_to_raw_response_wrapper( + cheques.retrieve, + ) + self.list = async_to_raw_response_wrapper( + cheques.list, + ) + self.delete = async_to_raw_response_wrapper( + cheques.delete, + ) + self.retrieve_url = async_to_raw_response_wrapper( + cheques.retrieve_url, + ) + self.retrieve_with_deposit_ready_pdf = async_to_raw_response_wrapper( + cheques.retrieve_with_deposit_ready_pdf, + ) + + +class ChequesResourceWithStreamingResponse: + def __init__(self, cheques: ChequesResource) -> None: + self._cheques = cheques + + self.create = to_streamed_response_wrapper( + cheques.create, + ) + self.retrieve = to_streamed_response_wrapper( + cheques.retrieve, + ) + self.list = to_streamed_response_wrapper( + cheques.list, + ) + self.delete = to_streamed_response_wrapper( + cheques.delete, + ) + self.retrieve_url = to_streamed_response_wrapper( + cheques.retrieve_url, + ) + self.retrieve_with_deposit_ready_pdf = to_streamed_response_wrapper( + cheques.retrieve_with_deposit_ready_pdf, + ) + + +class AsyncChequesResourceWithStreamingResponse: + def __init__(self, cheques: AsyncChequesResource) -> None: + self._cheques = cheques + + self.create = async_to_streamed_response_wrapper( + cheques.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + cheques.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + cheques.list, + ) + self.delete = async_to_streamed_response_wrapper( + cheques.delete, + ) + self.retrieve_url = async_to_streamed_response_wrapper( + cheques.retrieve_url, + ) + self.retrieve_with_deposit_ready_pdf = async_to_streamed_response_wrapper( + cheques.retrieve_with_deposit_ready_pdf, + ) diff --git a/src/postgrid/resources/print_mail/contacts.py b/src/postgrid/resources/print_mail/contacts.py new file mode 100644 index 0000000..8559434 --- /dev/null +++ b/src/postgrid/resources/print_mail/contacts.py @@ -0,0 +1,842 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict +from typing_extensions import overload + +import httpx + +from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._utils import ( + required_args, + maybe_transform, + async_maybe_transform, +) +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...pagination import SyncSkipLimit, AsyncSkipLimit +from ..._base_client import AsyncPaginator, make_request_options +from ...types.print_mail import contact_list_params, contact_create_params +from ...types.print_mail.contact import Contact +from ...types.print_mail.contact_delete_response import ContactDeleteResponse + +__all__ = ["ContactsResource", "AsyncContactsResource"] + + +class ContactsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ContactsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + """ + return ContactsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ContactsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + """ + return ContactsResourceWithStreamingResponse(self) + + @overload + def create( + self, + *, + address_line1: str, + country_code: str, + first_name: str, + address_line2: str | NotGiven = NOT_GIVEN, + city: str | NotGiven = NOT_GIVEN, + company_name: str | NotGiven = NOT_GIVEN, + description: str | NotGiven = NOT_GIVEN, + email: str | NotGiven = NOT_GIVEN, + force_verified_status: bool | NotGiven = NOT_GIVEN, + job_title: str | NotGiven = NOT_GIVEN, + last_name: str | NotGiven = NOT_GIVEN, + metadata: Dict[str, object] | NotGiven = NOT_GIVEN, + phone_number: str | NotGiven = NOT_GIVEN, + postal_or_zip: str | NotGiven = NOT_GIVEN, + province_or_state: str | NotGiven = NOT_GIVEN, + skip_verification: bool | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Contact: + """Creates a contact. + + This will also verify the contact's address **if you create + it using a live API key**. To sucessfully create a contact, either a + `firstName`, a `companyName`, or both are required. You can supply both, but you + **cannot** supply neither. + + You have the option to supply the entire address (except for `countryCode`) via + `addressLine1`, in which case PostGrid will parse it automatically. However, + this is **not guaranteed to be correct**, so we recommend passing along the + structured address fields (`city`, `provinceOrState`, etc) if you have them. + + _Note that if you create a contact that has identical information to another + contact, this will simply update the description of the existing contact and + return it. This avoids creating duplicate contacts._ + + Args: + address_line1: The first line of the contact's address. + + country_code: The ISO 3611-1 country code of the contact's address. + + address_line2: Second line of the contact's address, if applicable. + + city: The city of the contact's address. + + company_name: Company name of the contact. + + description: An optional string describing this resource. Will be visible in the API and the + dashboard. + + email: Email of the contact. + + force_verified_status: If `true`, PostGrid will force this contact to have an `addressStatus` of + `verified` even if our address verification system says otherwise. + + job_title: Job title of the contact. + + last_name: Last name of the contact. + + metadata: See the section on Metadata. + + phone_number: Phone number of the contact. + + postal_or_zip: The postal or ZIP code of the contact's address. + + province_or_state: Province or state of the contact's address. + + skip_verification: If `true`, PostGrid will skip running this contact's address through our address + verification system. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def create( + self, + *, + address_line1: str, + company_name: str, + country_code: str, + address_line2: str | NotGiven = NOT_GIVEN, + city: str | NotGiven = NOT_GIVEN, + description: str | NotGiven = NOT_GIVEN, + email: str | NotGiven = NOT_GIVEN, + first_name: str | NotGiven = NOT_GIVEN, + force_verified_status: bool | NotGiven = NOT_GIVEN, + job_title: str | NotGiven = NOT_GIVEN, + last_name: str | NotGiven = NOT_GIVEN, + metadata: Dict[str, object] | NotGiven = NOT_GIVEN, + phone_number: str | NotGiven = NOT_GIVEN, + postal_or_zip: str | NotGiven = NOT_GIVEN, + province_or_state: str | NotGiven = NOT_GIVEN, + skip_verification: bool | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Contact: + """Creates a contact. + + This will also verify the contact's address **if you create + it using a live API key**. To sucessfully create a contact, either a + `firstName`, a `companyName`, or both are required. You can supply both, but you + **cannot** supply neither. + + You have the option to supply the entire address (except for `countryCode`) via + `addressLine1`, in which case PostGrid will parse it automatically. However, + this is **not guaranteed to be correct**, so we recommend passing along the + structured address fields (`city`, `provinceOrState`, etc) if you have them. + + _Note that if you create a contact that has identical information to another + contact, this will simply update the description of the existing contact and + return it. This avoids creating duplicate contacts._ + + Args: + address_line1: The first line of the contact's address. + + country_code: The ISO 3611-1 country code of the contact's address. + + address_line2: Second line of the contact's address, if applicable. + + city: The city of the contact's address. + + description: An optional string describing this resource. Will be visible in the API and the + dashboard. + + email: Email of the contact. + + first_name: First name of the contact. + + force_verified_status: If `true`, PostGrid will force this contact to have an `addressStatus` of + `verified` even if our address verification system says otherwise. + + job_title: Job title of the contact. + + last_name: Last name of the contact. + + metadata: See the section on Metadata. + + phone_number: Phone number of the contact. + + postal_or_zip: The postal or ZIP code of the contact's address. + + province_or_state: Province or state of the contact's address. + + skip_verification: If `true`, PostGrid will skip running this contact's address through our address + verification system. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["address_line1", "country_code", "first_name"], ["address_line1", "company_name", "country_code"]) + def create( + self, + *, + address_line1: str, + country_code: str, + first_name: str | NotGiven = NOT_GIVEN, + address_line2: str | NotGiven = NOT_GIVEN, + city: str | NotGiven = NOT_GIVEN, + company_name: str | NotGiven = NOT_GIVEN, + description: str | NotGiven = NOT_GIVEN, + email: str | NotGiven = NOT_GIVEN, + force_verified_status: bool | NotGiven = NOT_GIVEN, + job_title: str | NotGiven = NOT_GIVEN, + last_name: str | NotGiven = NOT_GIVEN, + metadata: Dict[str, object] | NotGiven = NOT_GIVEN, + phone_number: str | NotGiven = NOT_GIVEN, + postal_or_zip: str | NotGiven = NOT_GIVEN, + province_or_state: str | NotGiven = NOT_GIVEN, + skip_verification: bool | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Contact: + return self._post( + "/print-mail/v1/contacts", + body=maybe_transform( + { + "address_line1": address_line1, + "country_code": country_code, + "first_name": first_name, + "address_line2": address_line2, + "city": city, + "company_name": company_name, + "description": description, + "email": email, + "force_verified_status": force_verified_status, + "job_title": job_title, + "last_name": last_name, + "metadata": metadata, + "phone_number": phone_number, + "postal_or_zip": postal_or_zip, + "province_or_state": province_or_state, + "skip_verification": skip_verification, + }, + contact_create_params.ContactCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Contact, + ) + + def retrieve( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Contact: + """ + Retrieve a contact. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._get( + f"/print-mail/v1/contacts/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Contact, + ) + + def list( + self, + *, + limit: int | NotGiven = NOT_GIVEN, + search: str | NotGiven = NOT_GIVEN, + skip: int | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SyncSkipLimit[Contact]: + """ + Get a list of contacts. + + Args: + search: You can supply any string to help narrow down the list of resources. For + example, if you pass `"New York"` (quoted), it will return resources that have + that string present somewhere in their response. Alternatively, you can supply a + structured search query. See the documentation on `StructuredSearchQuery` for + more details. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/print-mail/v1/contacts", + page=SyncSkipLimit[Contact], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "limit": limit, + "search": search, + "skip": skip, + }, + contact_list_params.ContactListParams, + ), + ), + model=Contact, + ) + + def delete( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ContactDeleteResponse: + """Delete a contact. + + Note that this will not affect orders that were sent to this + contact. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._delete( + f"/print-mail/v1/contacts/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ContactDeleteResponse, + ) + + +class AsyncContactsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncContactsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + """ + return AsyncContactsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncContactsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + """ + return AsyncContactsResourceWithStreamingResponse(self) + + @overload + async def create( + self, + *, + address_line1: str, + country_code: str, + first_name: str, + address_line2: str | NotGiven = NOT_GIVEN, + city: str | NotGiven = NOT_GIVEN, + company_name: str | NotGiven = NOT_GIVEN, + description: str | NotGiven = NOT_GIVEN, + email: str | NotGiven = NOT_GIVEN, + force_verified_status: bool | NotGiven = NOT_GIVEN, + job_title: str | NotGiven = NOT_GIVEN, + last_name: str | NotGiven = NOT_GIVEN, + metadata: Dict[str, object] | NotGiven = NOT_GIVEN, + phone_number: str | NotGiven = NOT_GIVEN, + postal_or_zip: str | NotGiven = NOT_GIVEN, + province_or_state: str | NotGiven = NOT_GIVEN, + skip_verification: bool | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Contact: + """Creates a contact. + + This will also verify the contact's address **if you create + it using a live API key**. To sucessfully create a contact, either a + `firstName`, a `companyName`, or both are required. You can supply both, but you + **cannot** supply neither. + + You have the option to supply the entire address (except for `countryCode`) via + `addressLine1`, in which case PostGrid will parse it automatically. However, + this is **not guaranteed to be correct**, so we recommend passing along the + structured address fields (`city`, `provinceOrState`, etc) if you have them. + + _Note that if you create a contact that has identical information to another + contact, this will simply update the description of the existing contact and + return it. This avoids creating duplicate contacts._ + + Args: + address_line1: The first line of the contact's address. + + country_code: The ISO 3611-1 country code of the contact's address. + + address_line2: Second line of the contact's address, if applicable. + + city: The city of the contact's address. + + company_name: Company name of the contact. + + description: An optional string describing this resource. Will be visible in the API and the + dashboard. + + email: Email of the contact. + + force_verified_status: If `true`, PostGrid will force this contact to have an `addressStatus` of + `verified` even if our address verification system says otherwise. + + job_title: Job title of the contact. + + last_name: Last name of the contact. + + metadata: See the section on Metadata. + + phone_number: Phone number of the contact. + + postal_or_zip: The postal or ZIP code of the contact's address. + + province_or_state: Province or state of the contact's address. + + skip_verification: If `true`, PostGrid will skip running this contact's address through our address + verification system. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def create( + self, + *, + address_line1: str, + company_name: str, + country_code: str, + address_line2: str | NotGiven = NOT_GIVEN, + city: str | NotGiven = NOT_GIVEN, + description: str | NotGiven = NOT_GIVEN, + email: str | NotGiven = NOT_GIVEN, + first_name: str | NotGiven = NOT_GIVEN, + force_verified_status: bool | NotGiven = NOT_GIVEN, + job_title: str | NotGiven = NOT_GIVEN, + last_name: str | NotGiven = NOT_GIVEN, + metadata: Dict[str, object] | NotGiven = NOT_GIVEN, + phone_number: str | NotGiven = NOT_GIVEN, + postal_or_zip: str | NotGiven = NOT_GIVEN, + province_or_state: str | NotGiven = NOT_GIVEN, + skip_verification: bool | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Contact: + """Creates a contact. + + This will also verify the contact's address **if you create + it using a live API key**. To sucessfully create a contact, either a + `firstName`, a `companyName`, or both are required. You can supply both, but you + **cannot** supply neither. + + You have the option to supply the entire address (except for `countryCode`) via + `addressLine1`, in which case PostGrid will parse it automatically. However, + this is **not guaranteed to be correct**, so we recommend passing along the + structured address fields (`city`, `provinceOrState`, etc) if you have them. + + _Note that if you create a contact that has identical information to another + contact, this will simply update the description of the existing contact and + return it. This avoids creating duplicate contacts._ + + Args: + address_line1: The first line of the contact's address. + + country_code: The ISO 3611-1 country code of the contact's address. + + address_line2: Second line of the contact's address, if applicable. + + city: The city of the contact's address. + + description: An optional string describing this resource. Will be visible in the API and the + dashboard. + + email: Email of the contact. + + first_name: First name of the contact. + + force_verified_status: If `true`, PostGrid will force this contact to have an `addressStatus` of + `verified` even if our address verification system says otherwise. + + job_title: Job title of the contact. + + last_name: Last name of the contact. + + metadata: See the section on Metadata. + + phone_number: Phone number of the contact. + + postal_or_zip: The postal or ZIP code of the contact's address. + + province_or_state: Province or state of the contact's address. + + skip_verification: If `true`, PostGrid will skip running this contact's address through our address + verification system. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["address_line1", "country_code", "first_name"], ["address_line1", "company_name", "country_code"]) + async def create( + self, + *, + address_line1: str, + country_code: str, + first_name: str | NotGiven = NOT_GIVEN, + address_line2: str | NotGiven = NOT_GIVEN, + city: str | NotGiven = NOT_GIVEN, + company_name: str | NotGiven = NOT_GIVEN, + description: str | NotGiven = NOT_GIVEN, + email: str | NotGiven = NOT_GIVEN, + force_verified_status: bool | NotGiven = NOT_GIVEN, + job_title: str | NotGiven = NOT_GIVEN, + last_name: str | NotGiven = NOT_GIVEN, + metadata: Dict[str, object] | NotGiven = NOT_GIVEN, + phone_number: str | NotGiven = NOT_GIVEN, + postal_or_zip: str | NotGiven = NOT_GIVEN, + province_or_state: str | NotGiven = NOT_GIVEN, + skip_verification: bool | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Contact: + return await self._post( + "/print-mail/v1/contacts", + body=await async_maybe_transform( + { + "address_line1": address_line1, + "country_code": country_code, + "first_name": first_name, + "address_line2": address_line2, + "city": city, + "company_name": company_name, + "description": description, + "email": email, + "force_verified_status": force_verified_status, + "job_title": job_title, + "last_name": last_name, + "metadata": metadata, + "phone_number": phone_number, + "postal_or_zip": postal_or_zip, + "province_or_state": province_or_state, + "skip_verification": skip_verification, + }, + contact_create_params.ContactCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Contact, + ) + + async def retrieve( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Contact: + """ + Retrieve a contact. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._get( + f"/print-mail/v1/contacts/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Contact, + ) + + def list( + self, + *, + limit: int | NotGiven = NOT_GIVEN, + search: str | NotGiven = NOT_GIVEN, + skip: int | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncPaginator[Contact, AsyncSkipLimit[Contact]]: + """ + Get a list of contacts. + + Args: + search: You can supply any string to help narrow down the list of resources. For + example, if you pass `"New York"` (quoted), it will return resources that have + that string present somewhere in their response. Alternatively, you can supply a + structured search query. See the documentation on `StructuredSearchQuery` for + more details. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/print-mail/v1/contacts", + page=AsyncSkipLimit[Contact], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "limit": limit, + "search": search, + "skip": skip, + }, + contact_list_params.ContactListParams, + ), + ), + model=Contact, + ) + + async def delete( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ContactDeleteResponse: + """Delete a contact. + + Note that this will not affect orders that were sent to this + contact. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._delete( + f"/print-mail/v1/contacts/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ContactDeleteResponse, + ) + + +class ContactsResourceWithRawResponse: + def __init__(self, contacts: ContactsResource) -> None: + self._contacts = contacts + + self.create = to_raw_response_wrapper( + contacts.create, + ) + self.retrieve = to_raw_response_wrapper( + contacts.retrieve, + ) + self.list = to_raw_response_wrapper( + contacts.list, + ) + self.delete = to_raw_response_wrapper( + contacts.delete, + ) + + +class AsyncContactsResourceWithRawResponse: + def __init__(self, contacts: AsyncContactsResource) -> None: + self._contacts = contacts + + self.create = async_to_raw_response_wrapper( + contacts.create, + ) + self.retrieve = async_to_raw_response_wrapper( + contacts.retrieve, + ) + self.list = async_to_raw_response_wrapper( + contacts.list, + ) + self.delete = async_to_raw_response_wrapper( + contacts.delete, + ) + + +class ContactsResourceWithStreamingResponse: + def __init__(self, contacts: ContactsResource) -> None: + self._contacts = contacts + + self.create = to_streamed_response_wrapper( + contacts.create, + ) + self.retrieve = to_streamed_response_wrapper( + contacts.retrieve, + ) + self.list = to_streamed_response_wrapper( + contacts.list, + ) + self.delete = to_streamed_response_wrapper( + contacts.delete, + ) + + +class AsyncContactsResourceWithStreamingResponse: + def __init__(self, contacts: AsyncContactsResource) -> None: + self._contacts = contacts + + self.create = async_to_streamed_response_wrapper( + contacts.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + contacts.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + contacts.list, + ) + self.delete = async_to_streamed_response_wrapper( + contacts.delete, + ) diff --git a/src/postgrid/resources/print_mail/letters.py b/src/postgrid/resources/print_mail/letters.py new file mode 100644 index 0000000..853c817 --- /dev/null +++ b/src/postgrid/resources/print_mail/letters.py @@ -0,0 +1,1052 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union +from datetime import datetime +from typing_extensions import Literal, overload + +import httpx + +from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._utils import ( + required_args, + maybe_transform, + async_maybe_transform, +) +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...pagination import SyncSkipLimit, AsyncSkipLimit +from ..._base_client import AsyncPaginator, make_request_options +from ...types.print_mail import ( + LetterSize, + AddressPlacement, + OrderMailingClass, + letter_list_params, + letter_create_params, +) +from ...types.print_mail.letter import Letter +from ...types.print_mail.letter_size import LetterSize +from ...types.print_mail.address_placement import AddressPlacement +from ...types.print_mail.attached_pdf_param import AttachedPdfParam +from ...types.print_mail.plastic_card_param import PlasticCardParam +from ...types.print_mail.order_mailing_class import OrderMailingClass +from ...types.print_mail.letter_retrieve_url_response import LetterRetrieveURLResponse + +__all__ = ["LettersResource", "AsyncLettersResource"] + + +class LettersResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> LettersResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + """ + return LettersResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> LettersResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + """ + return LettersResourceWithStreamingResponse(self) + + @overload + def create( + self, + *, + from_: letter_create_params.LetterCreateWithHTMLFrom, + html: str, + to: letter_create_params.LetterCreateWithHTMLTo, + address_placement: AddressPlacement | NotGiven = NOT_GIVEN, + attached_pdf: AttachedPdfParam | NotGiven = NOT_GIVEN, + color: bool | NotGiven = NOT_GIVEN, + description: str | NotGiven = NOT_GIVEN, + double_sided: bool | NotGiven = NOT_GIVEN, + envelope: str | NotGiven = NOT_GIVEN, + mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, + merge_variables: Dict[str, object] | NotGiven = NOT_GIVEN, + metadata: Dict[str, object] | NotGiven = NOT_GIVEN, + perforated_page: Literal[1] | NotGiven = NOT_GIVEN, + plastic_card: PlasticCardParam | NotGiven = NOT_GIVEN, + return_envelope: str | NotGiven = NOT_GIVEN, + send_date: Union[str, datetime] | NotGiven = NOT_GIVEN, + size: LetterSize | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Letter: + """Create a letter. + + Note that you can supply one of the following: + + - HTML content for the letter + - A template ID for the letter + - A URL or file for a PDF for the letter + - Upload the aforementioned PDF file via a multipart form upload request + + Args: + from_: The contact information of the sender. You can pass contact information inline + here just like you can for the `to`. + + html: The HTML content for the letter. You can supply _either_ this or `template` but + not both. + + to: The recipient of this order. You can either supply the contact information + inline here or provide a contact ID. PostGrid will automatically deduplicate + contacts regardless of whether you provide the information inline here or call + the contact creation endpoint. + + address_placement: Enum representing the placement of the address on the letter. + + attached_pdf: Model representing an attached PDF. + + color: Indicates if the letter is in color. + + description: An optional string describing this resource. Will be visible in the API and the + dashboard. + + double_sided: Indicates if the letter is double-sided. + + envelope: The envelope (ID) for the letter. You can either specify a custom envelope ID or + use the default `standard` envelope. + + mailing_class: The mailing class of this order. If not provided, automatically set to + `first_class`. + + merge_variables: These will be merged with the variables in the template or HTML you create this + order with. The keys in this object should match the variable names in the + template _exactly_ as they are case-sensitive. Note that these _do not_ apply to + PDFs uploaded with the order. + + metadata: See the section on Metadata. + + perforated_page: If specified, indicates which letter page is perforated. Currently, only the + first page can be perforated. + + plastic_card: Model representing a plastic card. + + return_envelope: The return envelope (ID) sent out with the letter, if any. + + send_date: This order will transition from `ready` to `printing` on the day after this + date. You can use this parameter to schedule orders for a future date. + + size: Enum representing the supported letter sizes. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def create( + self, + *, + template: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Letter: + """Create a letter. + + Note that you can supply one of the following: + + - HTML content for the letter + - A template ID for the letter + - A URL or file for a PDF for the letter + - Upload the aforementioned PDF file via a multipart form upload request + + Args: + template: The template ID for the letter. You can supply _either_ this or `html` but not + both. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def create( + self, + *, + from_: letter_create_params.LetterCreateWithPdfFrom, + pdf: str, + to: letter_create_params.LetterCreateWithPdfTo, + address_placement: AddressPlacement | NotGiven = NOT_GIVEN, + attached_pdf: AttachedPdfParam | NotGiven = NOT_GIVEN, + color: bool | NotGiven = NOT_GIVEN, + description: str | NotGiven = NOT_GIVEN, + double_sided: bool | NotGiven = NOT_GIVEN, + envelope: str | NotGiven = NOT_GIVEN, + mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, + merge_variables: Dict[str, object] | NotGiven = NOT_GIVEN, + metadata: Dict[str, object] | NotGiven = NOT_GIVEN, + perforated_page: Literal[1] | NotGiven = NOT_GIVEN, + plastic_card: PlasticCardParam | NotGiven = NOT_GIVEN, + return_envelope: str | NotGiven = NOT_GIVEN, + send_date: Union[str, datetime] | NotGiven = NOT_GIVEN, + size: LetterSize | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Letter: + """Create a letter. + + Note that you can supply one of the following: + + - HTML content for the letter + - A template ID for the letter + - A URL or file for a PDF for the letter + - Upload the aforementioned PDF file via a multipart form upload request + + Args: + from_: The contact information of the sender. You can pass contact information inline + here just like you can for the `to`. + + pdf: A URL pointing to a PDF file for the letter or the PDF file itself. + + to: The recipient of this order. You can either supply the contact information + inline here or provide a contact ID. PostGrid will automatically deduplicate + contacts regardless of whether you provide the information inline here or call + the contact creation endpoint. + + address_placement: Enum representing the placement of the address on the letter. + + attached_pdf: Model representing an attached PDF. + + color: Indicates if the letter is in color. + + description: An optional string describing this resource. Will be visible in the API and the + dashboard. + + double_sided: Indicates if the letter is double-sided. + + envelope: The envelope (ID) for the letter. You can either specify a custom envelope ID or + use the default `standard` envelope. + + mailing_class: The mailing class of this order. If not provided, automatically set to + `first_class`. + + merge_variables: These will be merged with the variables in the template or HTML you create this + order with. The keys in this object should match the variable names in the + template _exactly_ as they are case-sensitive. Note that these _do not_ apply to + PDFs uploaded with the order. + + metadata: See the section on Metadata. + + perforated_page: If specified, indicates which letter page is perforated. Currently, only the + first page can be perforated. + + plastic_card: Model representing a plastic card. + + return_envelope: The return envelope (ID) sent out with the letter, if any. + + send_date: This order will transition from `ready` to `printing` on the day after this + date. You can use this parameter to schedule orders for a future date. + + size: Enum representing the supported letter sizes. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["from_", "html", "to"], ["template"], ["from_", "pdf", "to"]) + def create( + self, + *, + from_: letter_create_params.LetterCreateWithHTMLFrom | NotGiven = NOT_GIVEN, + html: str | NotGiven = NOT_GIVEN, + to: letter_create_params.LetterCreateWithHTMLTo | NotGiven = NOT_GIVEN, + address_placement: AddressPlacement | NotGiven = NOT_GIVEN, + attached_pdf: AttachedPdfParam | NotGiven = NOT_GIVEN, + color: bool | NotGiven = NOT_GIVEN, + description: str | NotGiven = NOT_GIVEN, + double_sided: bool | NotGiven = NOT_GIVEN, + envelope: str | NotGiven = NOT_GIVEN, + mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, + merge_variables: Dict[str, object] | NotGiven = NOT_GIVEN, + metadata: Dict[str, object] | NotGiven = NOT_GIVEN, + perforated_page: Literal[1] | NotGiven = NOT_GIVEN, + plastic_card: PlasticCardParam | NotGiven = NOT_GIVEN, + return_envelope: str | NotGiven = NOT_GIVEN, + send_date: Union[str, datetime] | NotGiven = NOT_GIVEN, + size: LetterSize | NotGiven = NOT_GIVEN, + template: str | NotGiven = NOT_GIVEN, + pdf: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Letter: + return self._post( + "/print-mail/v1/letters", + body=maybe_transform( + { + "from_": from_, + "html": html, + "to": to, + "address_placement": address_placement, + "attached_pdf": attached_pdf, + "color": color, + "description": description, + "double_sided": double_sided, + "envelope": envelope, + "mailing_class": mailing_class, + "merge_variables": merge_variables, + "metadata": metadata, + "perforated_page": perforated_page, + "plastic_card": plastic_card, + "return_envelope": return_envelope, + "send_date": send_date, + "size": size, + "template": template, + "pdf": pdf, + }, + letter_create_params.LetterCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Letter, + ) + + def retrieve( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Letter: + """ + Retrieve a letter by ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._get( + f"/print-mail/v1/letters/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Letter, + ) + + def list( + self, + *, + limit: int | NotGiven = NOT_GIVEN, + search: str | NotGiven = NOT_GIVEN, + skip: int | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SyncSkipLimit[Letter]: + """ + Get a list of letters. + + Args: + search: You can supply any string to help narrow down the list of resources. For + example, if you pass `"New York"` (quoted), it will return resources that have + that string present somewhere in their response. Alternatively, you can supply a + structured search query. See the documentation on `StructuredSearchQuery` for + more details. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/print-mail/v1/letters", + page=SyncSkipLimit[Letter], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "limit": limit, + "search": search, + "skip": skip, + }, + letter_list_params.LetterListParams, + ), + ), + model=Letter, + ) + + def delete( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Letter: + """Cancel a letter by ID. + + Note that this operation cannot be undone. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._delete( + f"/print-mail/v1/letters/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Letter, + ) + + def retrieve_url( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> LetterRetrieveURLResponse: + """ + Retrieve a letter preview URL. + + This is only available for customers with our document management addon, which + offers document generation and hosting capabilities. This endpoint has a much + higher rate limit than the regular order retrieval endpoint, so it is suitable + for customer-facing use-cases. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._get( + f"/print-mail/v1/letters/{id}/url", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=LetterRetrieveURLResponse, + ) + + +class AsyncLettersResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncLettersResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + """ + return AsyncLettersResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncLettersResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + """ + return AsyncLettersResourceWithStreamingResponse(self) + + @overload + async def create( + self, + *, + from_: letter_create_params.LetterCreateWithHTMLFrom, + html: str, + to: letter_create_params.LetterCreateWithHTMLTo, + address_placement: AddressPlacement | NotGiven = NOT_GIVEN, + attached_pdf: AttachedPdfParam | NotGiven = NOT_GIVEN, + color: bool | NotGiven = NOT_GIVEN, + description: str | NotGiven = NOT_GIVEN, + double_sided: bool | NotGiven = NOT_GIVEN, + envelope: str | NotGiven = NOT_GIVEN, + mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, + merge_variables: Dict[str, object] | NotGiven = NOT_GIVEN, + metadata: Dict[str, object] | NotGiven = NOT_GIVEN, + perforated_page: Literal[1] | NotGiven = NOT_GIVEN, + plastic_card: PlasticCardParam | NotGiven = NOT_GIVEN, + return_envelope: str | NotGiven = NOT_GIVEN, + send_date: Union[str, datetime] | NotGiven = NOT_GIVEN, + size: LetterSize | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Letter: + """Create a letter. + + Note that you can supply one of the following: + + - HTML content for the letter + - A template ID for the letter + - A URL or file for a PDF for the letter + - Upload the aforementioned PDF file via a multipart form upload request + + Args: + from_: The contact information of the sender. You can pass contact information inline + here just like you can for the `to`. + + html: The HTML content for the letter. You can supply _either_ this or `template` but + not both. + + to: The recipient of this order. You can either supply the contact information + inline here or provide a contact ID. PostGrid will automatically deduplicate + contacts regardless of whether you provide the information inline here or call + the contact creation endpoint. + + address_placement: Enum representing the placement of the address on the letter. + + attached_pdf: Model representing an attached PDF. + + color: Indicates if the letter is in color. + + description: An optional string describing this resource. Will be visible in the API and the + dashboard. + + double_sided: Indicates if the letter is double-sided. + + envelope: The envelope (ID) for the letter. You can either specify a custom envelope ID or + use the default `standard` envelope. + + mailing_class: The mailing class of this order. If not provided, automatically set to + `first_class`. + + merge_variables: These will be merged with the variables in the template or HTML you create this + order with. The keys in this object should match the variable names in the + template _exactly_ as they are case-sensitive. Note that these _do not_ apply to + PDFs uploaded with the order. + + metadata: See the section on Metadata. + + perforated_page: If specified, indicates which letter page is perforated. Currently, only the + first page can be perforated. + + plastic_card: Model representing a plastic card. + + return_envelope: The return envelope (ID) sent out with the letter, if any. + + send_date: This order will transition from `ready` to `printing` on the day after this + date. You can use this parameter to schedule orders for a future date. + + size: Enum representing the supported letter sizes. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def create( + self, + *, + template: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Letter: + """Create a letter. + + Note that you can supply one of the following: + + - HTML content for the letter + - A template ID for the letter + - A URL or file for a PDF for the letter + - Upload the aforementioned PDF file via a multipart form upload request + + Args: + template: The template ID for the letter. You can supply _either_ this or `html` but not + both. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def create( + self, + *, + from_: letter_create_params.LetterCreateWithPdfFrom, + pdf: str, + to: letter_create_params.LetterCreateWithPdfTo, + address_placement: AddressPlacement | NotGiven = NOT_GIVEN, + attached_pdf: AttachedPdfParam | NotGiven = NOT_GIVEN, + color: bool | NotGiven = NOT_GIVEN, + description: str | NotGiven = NOT_GIVEN, + double_sided: bool | NotGiven = NOT_GIVEN, + envelope: str | NotGiven = NOT_GIVEN, + mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, + merge_variables: Dict[str, object] | NotGiven = NOT_GIVEN, + metadata: Dict[str, object] | NotGiven = NOT_GIVEN, + perforated_page: Literal[1] | NotGiven = NOT_GIVEN, + plastic_card: PlasticCardParam | NotGiven = NOT_GIVEN, + return_envelope: str | NotGiven = NOT_GIVEN, + send_date: Union[str, datetime] | NotGiven = NOT_GIVEN, + size: LetterSize | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Letter: + """Create a letter. + + Note that you can supply one of the following: + + - HTML content for the letter + - A template ID for the letter + - A URL or file for a PDF for the letter + - Upload the aforementioned PDF file via a multipart form upload request + + Args: + from_: The contact information of the sender. You can pass contact information inline + here just like you can for the `to`. + + pdf: A URL pointing to a PDF file for the letter or the PDF file itself. + + to: The recipient of this order. You can either supply the contact information + inline here or provide a contact ID. PostGrid will automatically deduplicate + contacts regardless of whether you provide the information inline here or call + the contact creation endpoint. + + address_placement: Enum representing the placement of the address on the letter. + + attached_pdf: Model representing an attached PDF. + + color: Indicates if the letter is in color. + + description: An optional string describing this resource. Will be visible in the API and the + dashboard. + + double_sided: Indicates if the letter is double-sided. + + envelope: The envelope (ID) for the letter. You can either specify a custom envelope ID or + use the default `standard` envelope. + + mailing_class: The mailing class of this order. If not provided, automatically set to + `first_class`. + + merge_variables: These will be merged with the variables in the template or HTML you create this + order with. The keys in this object should match the variable names in the + template _exactly_ as they are case-sensitive. Note that these _do not_ apply to + PDFs uploaded with the order. + + metadata: See the section on Metadata. + + perforated_page: If specified, indicates which letter page is perforated. Currently, only the + first page can be perforated. + + plastic_card: Model representing a plastic card. + + return_envelope: The return envelope (ID) sent out with the letter, if any. + + send_date: This order will transition from `ready` to `printing` on the day after this + date. You can use this parameter to schedule orders for a future date. + + size: Enum representing the supported letter sizes. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args(["from_", "html", "to"], ["template"], ["from_", "pdf", "to"]) + async def create( + self, + *, + from_: letter_create_params.LetterCreateWithHTMLFrom | NotGiven = NOT_GIVEN, + html: str | NotGiven = NOT_GIVEN, + to: letter_create_params.LetterCreateWithHTMLTo | NotGiven = NOT_GIVEN, + address_placement: AddressPlacement | NotGiven = NOT_GIVEN, + attached_pdf: AttachedPdfParam | NotGiven = NOT_GIVEN, + color: bool | NotGiven = NOT_GIVEN, + description: str | NotGiven = NOT_GIVEN, + double_sided: bool | NotGiven = NOT_GIVEN, + envelope: str | NotGiven = NOT_GIVEN, + mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, + merge_variables: Dict[str, object] | NotGiven = NOT_GIVEN, + metadata: Dict[str, object] | NotGiven = NOT_GIVEN, + perforated_page: Literal[1] | NotGiven = NOT_GIVEN, + plastic_card: PlasticCardParam | NotGiven = NOT_GIVEN, + return_envelope: str | NotGiven = NOT_GIVEN, + send_date: Union[str, datetime] | NotGiven = NOT_GIVEN, + size: LetterSize | NotGiven = NOT_GIVEN, + template: str | NotGiven = NOT_GIVEN, + pdf: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Letter: + return await self._post( + "/print-mail/v1/letters", + body=await async_maybe_transform( + { + "from_": from_, + "html": html, + "to": to, + "address_placement": address_placement, + "attached_pdf": attached_pdf, + "color": color, + "description": description, + "double_sided": double_sided, + "envelope": envelope, + "mailing_class": mailing_class, + "merge_variables": merge_variables, + "metadata": metadata, + "perforated_page": perforated_page, + "plastic_card": plastic_card, + "return_envelope": return_envelope, + "send_date": send_date, + "size": size, + "template": template, + "pdf": pdf, + }, + letter_create_params.LetterCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Letter, + ) + + async def retrieve( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Letter: + """ + Retrieve a letter by ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._get( + f"/print-mail/v1/letters/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Letter, + ) + + def list( + self, + *, + limit: int | NotGiven = NOT_GIVEN, + search: str | NotGiven = NOT_GIVEN, + skip: int | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncPaginator[Letter, AsyncSkipLimit[Letter]]: + """ + Get a list of letters. + + Args: + search: You can supply any string to help narrow down the list of resources. For + example, if you pass `"New York"` (quoted), it will return resources that have + that string present somewhere in their response. Alternatively, you can supply a + structured search query. See the documentation on `StructuredSearchQuery` for + more details. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/print-mail/v1/letters", + page=AsyncSkipLimit[Letter], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "limit": limit, + "search": search, + "skip": skip, + }, + letter_list_params.LetterListParams, + ), + ), + model=Letter, + ) + + async def delete( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Letter: + """Cancel a letter by ID. + + Note that this operation cannot be undone. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._delete( + f"/print-mail/v1/letters/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Letter, + ) + + async def retrieve_url( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> LetterRetrieveURLResponse: + """ + Retrieve a letter preview URL. + + This is only available for customers with our document management addon, which + offers document generation and hosting capabilities. This endpoint has a much + higher rate limit than the regular order retrieval endpoint, so it is suitable + for customer-facing use-cases. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._get( + f"/print-mail/v1/letters/{id}/url", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=LetterRetrieveURLResponse, + ) + + +class LettersResourceWithRawResponse: + def __init__(self, letters: LettersResource) -> None: + self._letters = letters + + self.create = to_raw_response_wrapper( + letters.create, + ) + self.retrieve = to_raw_response_wrapper( + letters.retrieve, + ) + self.list = to_raw_response_wrapper( + letters.list, + ) + self.delete = to_raw_response_wrapper( + letters.delete, + ) + self.retrieve_url = to_raw_response_wrapper( + letters.retrieve_url, + ) + + +class AsyncLettersResourceWithRawResponse: + def __init__(self, letters: AsyncLettersResource) -> None: + self._letters = letters + + self.create = async_to_raw_response_wrapper( + letters.create, + ) + self.retrieve = async_to_raw_response_wrapper( + letters.retrieve, + ) + self.list = async_to_raw_response_wrapper( + letters.list, + ) + self.delete = async_to_raw_response_wrapper( + letters.delete, + ) + self.retrieve_url = async_to_raw_response_wrapper( + letters.retrieve_url, + ) + + +class LettersResourceWithStreamingResponse: + def __init__(self, letters: LettersResource) -> None: + self._letters = letters + + self.create = to_streamed_response_wrapper( + letters.create, + ) + self.retrieve = to_streamed_response_wrapper( + letters.retrieve, + ) + self.list = to_streamed_response_wrapper( + letters.list, + ) + self.delete = to_streamed_response_wrapper( + letters.delete, + ) + self.retrieve_url = to_streamed_response_wrapper( + letters.retrieve_url, + ) + + +class AsyncLettersResourceWithStreamingResponse: + def __init__(self, letters: AsyncLettersResource) -> None: + self._letters = letters + + self.create = async_to_streamed_response_wrapper( + letters.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + letters.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + letters.list, + ) + self.delete = async_to_streamed_response_wrapper( + letters.delete, + ) + self.retrieve_url = async_to_streamed_response_wrapper( + letters.retrieve_url, + ) diff --git a/src/postgrid/resources/print_mail/mailing_list_imports.py b/src/postgrid/resources/print_mail/mailing_list_imports.py new file mode 100644 index 0000000..da8c926 --- /dev/null +++ b/src/postgrid/resources/print_mail/mailing_list_imports.py @@ -0,0 +1,651 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Optional + +import httpx + +from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._utils import ( + maybe_transform, + strip_not_given, + async_maybe_transform, +) +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...pagination import SyncSkipLimit, AsyncSkipLimit +from ..._base_client import AsyncPaginator, make_request_options +from ...types.print_mail import ( + FileType, + mailing_list_import_list_params, + mailing_list_import_create_params, + mailing_list_import_update_params, +) +from ...types.print_mail.file_type import FileType +from ...types.print_mail.mailing_list_import_response import MailingListImportResponse +from ...types.print_mail.mailing_list_import_delete_response import MailingListImportDeleteResponse + +__all__ = ["MailingListImportsResource", "AsyncMailingListImportsResource"] + + +class MailingListImportsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> MailingListImportsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + """ + return MailingListImportsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> MailingListImportsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + """ + return MailingListImportsResourceWithStreamingResponse(self) + + def create( + self, + *, + file: str, + file_type: FileType, + receiver_address_mapping: Dict[str, str], + description: str | NotGiven = NOT_GIVEN, + metadata: Dict[str, object] | NotGiven = NOT_GIVEN, + receiver_merge_variable_mapping: Dict[str, str] | NotGiven = NOT_GIVEN, + sender_address_mapping: Dict[str, str] | NotGiven = NOT_GIVEN, + sender_merge_variable_mapping: Dict[str, str] | NotGiven = NOT_GIVEN, + idempotency_key: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> MailingListImportResponse: + """ + Create a new mailing list import. + + Initiates the import process for a contact list file. The import enters the + `validating` status while contacts are processed and verified. + + Args: + file: The CSV file for this import. + + file_type: Type of file supported for mailing list imports. + + receiver_address_mapping: Mapping of columns for receiver addresses. + + description: An optional string describing this resource. Will be visible in the API and the + dashboard. + + metadata: See the section on Metadata. + + receiver_merge_variable_mapping: Optional mapping of columns for receiver merge variables. + + sender_address_mapping: Optional mapping of columns for sender addresses. If this is present, then all + receivers should have a corresponding sender. + + sender_merge_variable_mapping: Optional mapping of columns for sender merge variables. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {**strip_not_given({"idempotency-key": idempotency_key}), **(extra_headers or {})} + return self._post( + "/print-mail/v1/mailing_list_imports", + body=maybe_transform( + { + "file": file, + "file_type": file_type, + "receiver_address_mapping": receiver_address_mapping, + "description": description, + "metadata": metadata, + "receiver_merge_variable_mapping": receiver_merge_variable_mapping, + "sender_address_mapping": sender_address_mapping, + "sender_merge_variable_mapping": sender_merge_variable_mapping, + }, + mailing_list_import_create_params.MailingListImportCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=MailingListImportResponse, + ) + + def retrieve( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> MailingListImportResponse: + """ + Retrieve a specific mailing list import by its ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._get( + f"/print-mail/v1/mailing_list_imports/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=MailingListImportResponse, + ) + + def update( + self, + id: str, + *, + description: Optional[str] | NotGiven = NOT_GIVEN, + metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> MailingListImportResponse: + """ + Update an existing mailing list import. + + Args: + description: An optional description for the import. Set to `null` to remove the existing + description. + + metadata: Optional key-value data associated with the import. Set to `null` to remove + existing metadata. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._post( + f"/print-mail/v1/mailing_list_imports/{id}", + body=maybe_transform( + { + "description": description, + "metadata": metadata, + }, + mailing_list_import_update_params.MailingListImportUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=MailingListImportResponse, + ) + + def list( + self, + *, + limit: int | NotGiven = NOT_GIVEN, + search: str | NotGiven = NOT_GIVEN, + skip: int | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SyncSkipLimit[MailingListImportResponse]: + """ + Retrieve a list of mailing list imports. + + Returns a paginated list of imports associated with the authenticated + organization, filterable by various parameters. + + Args: + search: You can supply any string to help narrow down the list of resources. For + example, if you pass `"New York"` (quoted), it will return resources that have + that string present somewhere in their response. Alternatively, you can supply a + structured search query. See the documentation on `StructuredSearchQuery` for + more details. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/print-mail/v1/mailing_list_imports", + page=SyncSkipLimit[MailingListImportResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "limit": limit, + "search": search, + "skip": skip, + }, + mailing_list_import_list_params.MailingListImportListParams, + ), + ), + model=MailingListImportResponse, + ) + + def delete( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> MailingListImportDeleteResponse: + """ + Delete a mailing list import. + + This permanently deletes the import and its associated resources. This operation + cannot be undone. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._delete( + f"/print-mail/v1/mailing_list_imports/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=MailingListImportDeleteResponse, + ) + + +class AsyncMailingListImportsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncMailingListImportsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + """ + return AsyncMailingListImportsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncMailingListImportsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + """ + return AsyncMailingListImportsResourceWithStreamingResponse(self) + + async def create( + self, + *, + file: str, + file_type: FileType, + receiver_address_mapping: Dict[str, str], + description: str | NotGiven = NOT_GIVEN, + metadata: Dict[str, object] | NotGiven = NOT_GIVEN, + receiver_merge_variable_mapping: Dict[str, str] | NotGiven = NOT_GIVEN, + sender_address_mapping: Dict[str, str] | NotGiven = NOT_GIVEN, + sender_merge_variable_mapping: Dict[str, str] | NotGiven = NOT_GIVEN, + idempotency_key: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> MailingListImportResponse: + """ + Create a new mailing list import. + + Initiates the import process for a contact list file. The import enters the + `validating` status while contacts are processed and verified. + + Args: + file: The CSV file for this import. + + file_type: Type of file supported for mailing list imports. + + receiver_address_mapping: Mapping of columns for receiver addresses. + + description: An optional string describing this resource. Will be visible in the API and the + dashboard. + + metadata: See the section on Metadata. + + receiver_merge_variable_mapping: Optional mapping of columns for receiver merge variables. + + sender_address_mapping: Optional mapping of columns for sender addresses. If this is present, then all + receivers should have a corresponding sender. + + sender_merge_variable_mapping: Optional mapping of columns for sender merge variables. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {**strip_not_given({"idempotency-key": idempotency_key}), **(extra_headers or {})} + return await self._post( + "/print-mail/v1/mailing_list_imports", + body=await async_maybe_transform( + { + "file": file, + "file_type": file_type, + "receiver_address_mapping": receiver_address_mapping, + "description": description, + "metadata": metadata, + "receiver_merge_variable_mapping": receiver_merge_variable_mapping, + "sender_address_mapping": sender_address_mapping, + "sender_merge_variable_mapping": sender_merge_variable_mapping, + }, + mailing_list_import_create_params.MailingListImportCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=MailingListImportResponse, + ) + + async def retrieve( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> MailingListImportResponse: + """ + Retrieve a specific mailing list import by its ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._get( + f"/print-mail/v1/mailing_list_imports/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=MailingListImportResponse, + ) + + async def update( + self, + id: str, + *, + description: Optional[str] | NotGiven = NOT_GIVEN, + metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> MailingListImportResponse: + """ + Update an existing mailing list import. + + Args: + description: An optional description for the import. Set to `null` to remove the existing + description. + + metadata: Optional key-value data associated with the import. Set to `null` to remove + existing metadata. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._post( + f"/print-mail/v1/mailing_list_imports/{id}", + body=await async_maybe_transform( + { + "description": description, + "metadata": metadata, + }, + mailing_list_import_update_params.MailingListImportUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=MailingListImportResponse, + ) + + def list( + self, + *, + limit: int | NotGiven = NOT_GIVEN, + search: str | NotGiven = NOT_GIVEN, + skip: int | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncPaginator[MailingListImportResponse, AsyncSkipLimit[MailingListImportResponse]]: + """ + Retrieve a list of mailing list imports. + + Returns a paginated list of imports associated with the authenticated + organization, filterable by various parameters. + + Args: + search: You can supply any string to help narrow down the list of resources. For + example, if you pass `"New York"` (quoted), it will return resources that have + that string present somewhere in their response. Alternatively, you can supply a + structured search query. See the documentation on `StructuredSearchQuery` for + more details. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/print-mail/v1/mailing_list_imports", + page=AsyncSkipLimit[MailingListImportResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "limit": limit, + "search": search, + "skip": skip, + }, + mailing_list_import_list_params.MailingListImportListParams, + ), + ), + model=MailingListImportResponse, + ) + + async def delete( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> MailingListImportDeleteResponse: + """ + Delete a mailing list import. + + This permanently deletes the import and its associated resources. This operation + cannot be undone. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._delete( + f"/print-mail/v1/mailing_list_imports/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=MailingListImportDeleteResponse, + ) + + +class MailingListImportsResourceWithRawResponse: + def __init__(self, mailing_list_imports: MailingListImportsResource) -> None: + self._mailing_list_imports = mailing_list_imports + + self.create = to_raw_response_wrapper( + mailing_list_imports.create, + ) + self.retrieve = to_raw_response_wrapper( + mailing_list_imports.retrieve, + ) + self.update = to_raw_response_wrapper( + mailing_list_imports.update, + ) + self.list = to_raw_response_wrapper( + mailing_list_imports.list, + ) + self.delete = to_raw_response_wrapper( + mailing_list_imports.delete, + ) + + +class AsyncMailingListImportsResourceWithRawResponse: + def __init__(self, mailing_list_imports: AsyncMailingListImportsResource) -> None: + self._mailing_list_imports = mailing_list_imports + + self.create = async_to_raw_response_wrapper( + mailing_list_imports.create, + ) + self.retrieve = async_to_raw_response_wrapper( + mailing_list_imports.retrieve, + ) + self.update = async_to_raw_response_wrapper( + mailing_list_imports.update, + ) + self.list = async_to_raw_response_wrapper( + mailing_list_imports.list, + ) + self.delete = async_to_raw_response_wrapper( + mailing_list_imports.delete, + ) + + +class MailingListImportsResourceWithStreamingResponse: + def __init__(self, mailing_list_imports: MailingListImportsResource) -> None: + self._mailing_list_imports = mailing_list_imports + + self.create = to_streamed_response_wrapper( + mailing_list_imports.create, + ) + self.retrieve = to_streamed_response_wrapper( + mailing_list_imports.retrieve, + ) + self.update = to_streamed_response_wrapper( + mailing_list_imports.update, + ) + self.list = to_streamed_response_wrapper( + mailing_list_imports.list, + ) + self.delete = to_streamed_response_wrapper( + mailing_list_imports.delete, + ) + + +class AsyncMailingListImportsResourceWithStreamingResponse: + def __init__(self, mailing_list_imports: AsyncMailingListImportsResource) -> None: + self._mailing_list_imports = mailing_list_imports + + self.create = async_to_streamed_response_wrapper( + mailing_list_imports.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + mailing_list_imports.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + mailing_list_imports.update, + ) + self.list = async_to_streamed_response_wrapper( + mailing_list_imports.list, + ) + self.delete = async_to_streamed_response_wrapper( + mailing_list_imports.delete, + ) diff --git a/src/postgrid/resources/print_mail/mailing_lists.py b/src/postgrid/resources/print_mail/mailing_lists.py new file mode 100644 index 0000000..ded09ca --- /dev/null +++ b/src/postgrid/resources/print_mail/mailing_lists.py @@ -0,0 +1,743 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, List + +import httpx + +from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._utils import ( + maybe_transform, + strip_not_given, + async_maybe_transform, +) +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...pagination import SyncSkipLimit, AsyncSkipLimit +from ..._base_client import AsyncPaginator, make_request_options +from ...types.print_mail import ( + mailing_list_jobs_params, + mailing_list_list_params, + mailing_list_create_params, + mailing_list_update_params, +) +from ...types.print_mail.mailing_list import MailingList +from ...types.print_mail.mailing_list_update import MailingListUpdate +from ...types.print_mail.mailing_list_delete_response import MailingListDeleteResponse + +__all__ = ["MailingListsResource", "AsyncMailingListsResource"] + + +class MailingListsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> MailingListsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + """ + return MailingListsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> MailingListsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + """ + return MailingListsResourceWithStreamingResponse(self) + + def create( + self, + *, + description: str | NotGiven = NOT_GIVEN, + metadata: Dict[str, object] | NotGiven = NOT_GIVEN, + idempotency_key: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> MailingList: + """ + Create a new mailing list. + + Args: + description: An optional string describing this resource. Will be visible in the API and the + dashboard. + + metadata: See the section on Metadata. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {**strip_not_given({"idempotency-key": idempotency_key}), **(extra_headers or {})} + return self._post( + "/print-mail/v1/mailing_lists", + body=maybe_transform( + { + "description": description, + "metadata": metadata, + }, + mailing_list_create_params.MailingListCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=MailingList, + ) + + def retrieve( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> MailingList: + """ + Retrieve a specific mailing list by its ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._get( + f"/print-mail/v1/mailing_lists/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=MailingList, + ) + + def update( + self, + id: str, + *, + description: str | NotGiven = NOT_GIVEN, + metadata: Dict[str, object] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> MailingListUpdate: + """ + Update an existing mailing list. + + Args: + description: An optional string describing this resource. Will be visible in the API and the + dashboard. + + metadata: See the section on Metadata. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._post( + f"/print-mail/v1/mailing_lists/{id}", + body=maybe_transform( + { + "description": description, + "metadata": metadata, + }, + mailing_list_update_params.MailingListUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=MailingListUpdate, + ) + + def list( + self, + *, + limit: int | NotGiven = NOT_GIVEN, + search: str | NotGiven = NOT_GIVEN, + skip: int | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SyncSkipLimit[MailingList]: + """ + Retrieve a list of mailing lists. + + Returns a paginated list of mailing lists associated with the authenticated + organization, filterable by various parameters. + + Args: + search: You can supply any string to help narrow down the list of resources. For + example, if you pass `"New York"` (quoted), it will return resources that have + that string present somewhere in their response. Alternatively, you can supply a + structured search query. See the documentation on `StructuredSearchQuery` for + more details. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/print-mail/v1/mailing_lists", + page=SyncSkipLimit[MailingList], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "limit": limit, + "search": search, + "skip": skip, + }, + mailing_list_list_params.MailingListListParams, + ), + ), + model=MailingList, + ) + + def delete( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> MailingListDeleteResponse: + """ + Delete a mailing list. + + This permanently deletes the mailing list and its associations. This operation + cannot be undone. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._delete( + f"/print-mail/v1/mailing_lists/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=MailingListDeleteResponse, + ) + + def jobs( + self, + id: str, + *, + add_contacts: List[str] | NotGiven = NOT_GIVEN, + add_mailing_list_imports: List[str] | NotGiven = NOT_GIVEN, + remove_contacts: List[str] | NotGiven = NOT_GIVEN, + remove_mailing_list_imports: List[str] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> MailingList: + """Runs a mailing list job. + + Mailing list jobs allow you to add or remove contacts + to your mailing list from mailing list imports or directly with contact IDs. + Only one job can be ran at a time and jobs are only able to be ran while the + mailing list has a `status` of "completed". + + Once a job as successfully been kicked off, the mailing list will have a + `status` of either `creating_contacts` or `removing_contacts` depending on which + job was ran. After the job has finished, the mailing list will go back into the + `completed` state where more jobs can be ran. If there are any errors while + running a job, the `errors` field on the mailing list will contain a list of + error objects which describe the errors. + + Args: + add_contacts: List of contact IDs to add to the mailing list. Cannot be used with other + operations. + + add_mailing_list_imports: List of mailing list import IDs to add to the mailing list. Cannot be used with + other operations. + + remove_contacts: List of contact IDs to remove from the mailing list. Cannot be used with other + operations. + + remove_mailing_list_imports: List of mailing list import IDs to remove from the mailing list. Cannot be used + with other operations. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._post( + f"/print-mail/v1/mailing_lists/{id}/jobs", + body=maybe_transform( + { + "add_contacts": add_contacts, + "add_mailing_list_imports": add_mailing_list_imports, + "remove_contacts": remove_contacts, + "remove_mailing_list_imports": remove_mailing_list_imports, + }, + mailing_list_jobs_params.MailingListJobsParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=MailingList, + ) + + +class AsyncMailingListsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncMailingListsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + """ + return AsyncMailingListsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncMailingListsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + """ + return AsyncMailingListsResourceWithStreamingResponse(self) + + async def create( + self, + *, + description: str | NotGiven = NOT_GIVEN, + metadata: Dict[str, object] | NotGiven = NOT_GIVEN, + idempotency_key: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> MailingList: + """ + Create a new mailing list. + + Args: + description: An optional string describing this resource. Will be visible in the API and the + dashboard. + + metadata: See the section on Metadata. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + extra_headers = {**strip_not_given({"idempotency-key": idempotency_key}), **(extra_headers or {})} + return await self._post( + "/print-mail/v1/mailing_lists", + body=await async_maybe_transform( + { + "description": description, + "metadata": metadata, + }, + mailing_list_create_params.MailingListCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=MailingList, + ) + + async def retrieve( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> MailingList: + """ + Retrieve a specific mailing list by its ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._get( + f"/print-mail/v1/mailing_lists/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=MailingList, + ) + + async def update( + self, + id: str, + *, + description: str | NotGiven = NOT_GIVEN, + metadata: Dict[str, object] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> MailingListUpdate: + """ + Update an existing mailing list. + + Args: + description: An optional string describing this resource. Will be visible in the API and the + dashboard. + + metadata: See the section on Metadata. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._post( + f"/print-mail/v1/mailing_lists/{id}", + body=await async_maybe_transform( + { + "description": description, + "metadata": metadata, + }, + mailing_list_update_params.MailingListUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=MailingListUpdate, + ) + + def list( + self, + *, + limit: int | NotGiven = NOT_GIVEN, + search: str | NotGiven = NOT_GIVEN, + skip: int | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncPaginator[MailingList, AsyncSkipLimit[MailingList]]: + """ + Retrieve a list of mailing lists. + + Returns a paginated list of mailing lists associated with the authenticated + organization, filterable by various parameters. + + Args: + search: You can supply any string to help narrow down the list of resources. For + example, if you pass `"New York"` (quoted), it will return resources that have + that string present somewhere in their response. Alternatively, you can supply a + structured search query. See the documentation on `StructuredSearchQuery` for + more details. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/print-mail/v1/mailing_lists", + page=AsyncSkipLimit[MailingList], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "limit": limit, + "search": search, + "skip": skip, + }, + mailing_list_list_params.MailingListListParams, + ), + ), + model=MailingList, + ) + + async def delete( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> MailingListDeleteResponse: + """ + Delete a mailing list. + + This permanently deletes the mailing list and its associations. This operation + cannot be undone. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._delete( + f"/print-mail/v1/mailing_lists/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=MailingListDeleteResponse, + ) + + async def jobs( + self, + id: str, + *, + add_contacts: List[str] | NotGiven = NOT_GIVEN, + add_mailing_list_imports: List[str] | NotGiven = NOT_GIVEN, + remove_contacts: List[str] | NotGiven = NOT_GIVEN, + remove_mailing_list_imports: List[str] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> MailingList: + """Runs a mailing list job. + + Mailing list jobs allow you to add or remove contacts + to your mailing list from mailing list imports or directly with contact IDs. + Only one job can be ran at a time and jobs are only able to be ran while the + mailing list has a `status` of "completed". + + Once a job as successfully been kicked off, the mailing list will have a + `status` of either `creating_contacts` or `removing_contacts` depending on which + job was ran. After the job has finished, the mailing list will go back into the + `completed` state where more jobs can be ran. If there are any errors while + running a job, the `errors` field on the mailing list will contain a list of + error objects which describe the errors. + + Args: + add_contacts: List of contact IDs to add to the mailing list. Cannot be used with other + operations. + + add_mailing_list_imports: List of mailing list import IDs to add to the mailing list. Cannot be used with + other operations. + + remove_contacts: List of contact IDs to remove from the mailing list. Cannot be used with other + operations. + + remove_mailing_list_imports: List of mailing list import IDs to remove from the mailing list. Cannot be used + with other operations. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._post( + f"/print-mail/v1/mailing_lists/{id}/jobs", + body=await async_maybe_transform( + { + "add_contacts": add_contacts, + "add_mailing_list_imports": add_mailing_list_imports, + "remove_contacts": remove_contacts, + "remove_mailing_list_imports": remove_mailing_list_imports, + }, + mailing_list_jobs_params.MailingListJobsParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=MailingList, + ) + + +class MailingListsResourceWithRawResponse: + def __init__(self, mailing_lists: MailingListsResource) -> None: + self._mailing_lists = mailing_lists + + self.create = to_raw_response_wrapper( + mailing_lists.create, + ) + self.retrieve = to_raw_response_wrapper( + mailing_lists.retrieve, + ) + self.update = to_raw_response_wrapper( + mailing_lists.update, + ) + self.list = to_raw_response_wrapper( + mailing_lists.list, + ) + self.delete = to_raw_response_wrapper( + mailing_lists.delete, + ) + self.jobs = to_raw_response_wrapper( + mailing_lists.jobs, + ) + + +class AsyncMailingListsResourceWithRawResponse: + def __init__(self, mailing_lists: AsyncMailingListsResource) -> None: + self._mailing_lists = mailing_lists + + self.create = async_to_raw_response_wrapper( + mailing_lists.create, + ) + self.retrieve = async_to_raw_response_wrapper( + mailing_lists.retrieve, + ) + self.update = async_to_raw_response_wrapper( + mailing_lists.update, + ) + self.list = async_to_raw_response_wrapper( + mailing_lists.list, + ) + self.delete = async_to_raw_response_wrapper( + mailing_lists.delete, + ) + self.jobs = async_to_raw_response_wrapper( + mailing_lists.jobs, + ) + + +class MailingListsResourceWithStreamingResponse: + def __init__(self, mailing_lists: MailingListsResource) -> None: + self._mailing_lists = mailing_lists + + self.create = to_streamed_response_wrapper( + mailing_lists.create, + ) + self.retrieve = to_streamed_response_wrapper( + mailing_lists.retrieve, + ) + self.update = to_streamed_response_wrapper( + mailing_lists.update, + ) + self.list = to_streamed_response_wrapper( + mailing_lists.list, + ) + self.delete = to_streamed_response_wrapper( + mailing_lists.delete, + ) + self.jobs = to_streamed_response_wrapper( + mailing_lists.jobs, + ) + + +class AsyncMailingListsResourceWithStreamingResponse: + def __init__(self, mailing_lists: AsyncMailingListsResource) -> None: + self._mailing_lists = mailing_lists + + self.create = async_to_streamed_response_wrapper( + mailing_lists.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + mailing_lists.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + mailing_lists.update, + ) + self.list = async_to_streamed_response_wrapper( + mailing_lists.list, + ) + self.delete = async_to_streamed_response_wrapper( + mailing_lists.delete, + ) + self.jobs = async_to_streamed_response_wrapper( + mailing_lists.jobs, + ) diff --git a/src/postgrid/resources/print_mail/order_profiles/__init__.py b/src/postgrid/resources/print_mail/order_profiles/__init__.py new file mode 100644 index 0000000..6f7e6b7 --- /dev/null +++ b/src/postgrid/resources/print_mail/order_profiles/__init__.py @@ -0,0 +1,75 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .cheques import ( + ChequesResource, + AsyncChequesResource, + ChequesResourceWithRawResponse, + AsyncChequesResourceWithRawResponse, + ChequesResourceWithStreamingResponse, + AsyncChequesResourceWithStreamingResponse, +) +from .letters import ( + LettersResource, + AsyncLettersResource, + LettersResourceWithRawResponse, + AsyncLettersResourceWithRawResponse, + LettersResourceWithStreamingResponse, + AsyncLettersResourceWithStreamingResponse, +) +from .postcards import ( + PostcardsResource, + AsyncPostcardsResource, + PostcardsResourceWithRawResponse, + AsyncPostcardsResourceWithRawResponse, + PostcardsResourceWithStreamingResponse, + AsyncPostcardsResourceWithStreamingResponse, +) +from .self_mailers import ( + SelfMailersResource, + AsyncSelfMailersResource, + SelfMailersResourceWithRawResponse, + AsyncSelfMailersResourceWithRawResponse, + SelfMailersResourceWithStreamingResponse, + AsyncSelfMailersResourceWithStreamingResponse, +) +from .order_profiles import ( + OrderProfilesResource, + AsyncOrderProfilesResource, + OrderProfilesResourceWithRawResponse, + AsyncOrderProfilesResourceWithRawResponse, + OrderProfilesResourceWithStreamingResponse, + AsyncOrderProfilesResourceWithStreamingResponse, +) + +__all__ = [ + "ChequesResource", + "AsyncChequesResource", + "ChequesResourceWithRawResponse", + "AsyncChequesResourceWithRawResponse", + "ChequesResourceWithStreamingResponse", + "AsyncChequesResourceWithStreamingResponse", + "LettersResource", + "AsyncLettersResource", + "LettersResourceWithRawResponse", + "AsyncLettersResourceWithRawResponse", + "LettersResourceWithStreamingResponse", + "AsyncLettersResourceWithStreamingResponse", + "PostcardsResource", + "AsyncPostcardsResource", + "PostcardsResourceWithRawResponse", + "AsyncPostcardsResourceWithRawResponse", + "PostcardsResourceWithStreamingResponse", + "AsyncPostcardsResourceWithStreamingResponse", + "SelfMailersResource", + "AsyncSelfMailersResource", + "SelfMailersResourceWithRawResponse", + "AsyncSelfMailersResourceWithRawResponse", + "SelfMailersResourceWithStreamingResponse", + "AsyncSelfMailersResourceWithStreamingResponse", + "OrderProfilesResource", + "AsyncOrderProfilesResource", + "OrderProfilesResourceWithRawResponse", + "AsyncOrderProfilesResourceWithRawResponse", + "OrderProfilesResourceWithStreamingResponse", + "AsyncOrderProfilesResourceWithStreamingResponse", +] diff --git a/src/postgrid/resources/print_mail/order_profiles/cheques.py b/src/postgrid/resources/print_mail/order_profiles/cheques.py new file mode 100644 index 0000000..64961f6 --- /dev/null +++ b/src/postgrid/resources/print_mail/order_profiles/cheques.py @@ -0,0 +1,814 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, List, Union, Optional + +import httpx + +from ...._types import ( + NOT_GIVEN, + Body, + Query, + Headers, + NotGiven, + Base64FileInput, +) +from ...._utils import ( + maybe_transform, + async_maybe_transform, +) +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ....pagination import SyncSkipLimit, AsyncSkipLimit +from ...._base_client import AsyncPaginator, make_request_options +from ....types.print_mail import ChequeSize, OrderMailingClass +from ....types.print_mail.cheque_size import ChequeSize +from ....types.print_mail.order_profiles import ( + CurrencyCode, + cheque_list_params, + cheque_create_params, + cheque_update_params, + cheque_retrieve_params, +) +from ....types.print_mail.order_mailing_class import OrderMailingClass +from ....types.print_mail.order_profiles.currency_code import CurrencyCode +from ....types.print_mail.order_profiles.cheque_profile import ChequeProfile +from ....types.print_mail.order_profiles.cheque_list_response import ChequeListResponse +from ....types.print_mail.order_profiles.cheque_delete_response import ChequeDeleteResponse + +__all__ = ["ChequesResource", "AsyncChequesResource"] + + +class ChequesResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ChequesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + """ + return ChequesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ChequesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + """ + return ChequesResourceWithStreamingResponse(self) + + def create( + self, + *, + bank_account: str, + size: ChequeSize, + expand: List[str] | NotGiven = NOT_GIVEN, + currency_code: CurrencyCode | NotGiven = NOT_GIVEN, + description: Optional[str] | NotGiven = NOT_GIVEN, + letter_pdf: Union[str, Base64FileInput] | NotGiven = NOT_GIVEN, + letter_template: str | NotGiven = NOT_GIVEN, + logo: Optional[str] | NotGiven = NOT_GIVEN, + mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, + memo: Optional[str] | NotGiven = NOT_GIVEN, + merge_variables: Optional[Dict[str, object]] | NotGiven = NOT_GIVEN, + message: Optional[str] | NotGiven = NOT_GIVEN, + metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ChequeProfile: + """Creates a new Cheque Profile. + + Requires a `bankAccount` ID. Can optionally + include an attached letter via `letterHTML`, `letterTemplate`, or `letterPDF`. + If providing `letterPDF` or `logo` (if logo needs upload, though schema suggests + URL), use `multipart/form-data`. + + Args: + bank_account: ID of the bank account to use for the cheque. Required for creation. + + size: Enum representing the supported cheque sizes. + + expand: Optional list of related resources to expand in the response. + + currency_code: Enum representing the supported currency codes. + + description: An optional description for the profile. Set to `null` to remove during update. + + letter_pdf: PDF file for an optional attached letter. Cannot be used with `letterHTML` or + `letterTemplate`. Input only. + + letter_template: ID of a template for an optional attached letter. Cannot be used with + `letterHTML` or `letterPDF`. + + logo: A publicly accessible URL for the logo to print on the cheque. Set to `null` to + remove during update. + + mailing_class: Mailing class. Generally must be first class (or equivalent for destination + country) for cheques. + + memo: Memo line text for the cheque. Set to `null` to remove during update. + + merge_variables: Default merge variables for orders created using this profile. + + message: Message included on the cheque stub. Set to `null` to remove during update. + + metadata: Optional key-value metadata. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/print-mail/v1/order_profiles/cheques", + body=maybe_transform( + { + "bank_account": bank_account, + "size": size, + "currency_code": currency_code, + "description": description, + "letter_pdf": letter_pdf, + "letter_template": letter_template, + "logo": logo, + "mailing_class": mailing_class, + "memo": memo, + "merge_variables": merge_variables, + "message": message, + "metadata": metadata, + }, + cheque_create_params.ChequeCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"expand": expand}, cheque_create_params.ChequeCreateParams), + ), + cast_to=ChequeProfile, + ) + + def retrieve( + self, + id: str, + *, + expand: List[str] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ChequeProfile: + """ + Retrieves the details of a specific Cheque Profile. + + Args: + expand: Optional list of related resources to expand in the response. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._get( + f"/print-mail/v1/order_profiles/cheques/{id}", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"expand": expand}, cheque_retrieve_params.ChequeRetrieveParams), + ), + cast_to=ChequeProfile, + ) + + def update( + self, + id: str, + *, + bank_account: str, + size: ChequeSize, + expand: List[str] | NotGiven = NOT_GIVEN, + currency_code: CurrencyCode | NotGiven = NOT_GIVEN, + description: Optional[str] | NotGiven = NOT_GIVEN, + letter_pdf: Union[str, Base64FileInput] | NotGiven = NOT_GIVEN, + letter_template: str | NotGiven = NOT_GIVEN, + logo: Optional[str] | NotGiven = NOT_GIVEN, + mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, + memo: Optional[str] | NotGiven = NOT_GIVEN, + merge_variables: Optional[Dict[str, object]] | NotGiven = NOT_GIVEN, + message: Optional[str] | NotGiven = NOT_GIVEN, + metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ChequeProfile: + """Updates specific fields of an existing Cheque Profile. + + If providing `letterPDF` + or `logo`, use `multipart/form-data`. + + Args: + bank_account: ID of the bank account to use for the cheque. Required for creation. + + size: Enum representing the supported cheque sizes. + + expand: Optional list of related resources to expand in the response. + + currency_code: Enum representing the supported currency codes. + + description: An optional description for the profile. Set to `null` to remove during update. + + letter_pdf: PDF file for an optional attached letter. Cannot be used with `letterHTML` or + `letterTemplate`. Input only. + + letter_template: ID of a template for an optional attached letter. Cannot be used with + `letterHTML` or `letterPDF`. + + logo: A publicly accessible URL for the logo to print on the cheque. Set to `null` to + remove during update. + + mailing_class: Mailing class. Generally must be first class (or equivalent for destination + country) for cheques. + + memo: Memo line text for the cheque. Set to `null` to remove during update. + + merge_variables: Default merge variables for orders created using this profile. + + message: Message included on the cheque stub. Set to `null` to remove during update. + + metadata: Optional key-value metadata. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._post( + f"/print-mail/v1/order_profiles/cheques/{id}", + body=maybe_transform( + { + "bank_account": bank_account, + "size": size, + "currency_code": currency_code, + "description": description, + "letter_pdf": letter_pdf, + "letter_template": letter_template, + "logo": logo, + "mailing_class": mailing_class, + "memo": memo, + "merge_variables": merge_variables, + "message": message, + "metadata": metadata, + }, + cheque_update_params.ChequeUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"expand": expand}, cheque_update_params.ChequeUpdateParams), + ), + cast_to=ChequeProfile, + ) + + def list( + self, + *, + limit: int | NotGiven = NOT_GIVEN, + search: str | NotGiven = NOT_GIVEN, + skip: int | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SyncSkipLimit[ChequeListResponse]: + """ + Retrieves a list of Cheque Profiles. + + Args: + search: You can supply any string to help narrow down the list of resources. For + example, if you pass `"New York"` (quoted), it will return resources that have + that string present somewhere in their response. Alternatively, you can supply a + structured search query. See the documentation on `StructuredSearchQuery` for + more details. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/print-mail/v1/order_profiles/cheques", + page=SyncSkipLimit[ChequeListResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "limit": limit, + "search": search, + "skip": skip, + }, + cheque_list_params.ChequeListParams, + ), + ), + model=ChequeListResponse, + ) + + def delete( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ChequeDeleteResponse: + """ + Deletes a Cheque Profile. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._delete( + f"/print-mail/v1/order_profiles/cheques/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ChequeDeleteResponse, + ) + + +class AsyncChequesResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncChequesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + """ + return AsyncChequesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncChequesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + """ + return AsyncChequesResourceWithStreamingResponse(self) + + async def create( + self, + *, + bank_account: str, + size: ChequeSize, + expand: List[str] | NotGiven = NOT_GIVEN, + currency_code: CurrencyCode | NotGiven = NOT_GIVEN, + description: Optional[str] | NotGiven = NOT_GIVEN, + letter_pdf: Union[str, Base64FileInput] | NotGiven = NOT_GIVEN, + letter_template: str | NotGiven = NOT_GIVEN, + logo: Optional[str] | NotGiven = NOT_GIVEN, + mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, + memo: Optional[str] | NotGiven = NOT_GIVEN, + merge_variables: Optional[Dict[str, object]] | NotGiven = NOT_GIVEN, + message: Optional[str] | NotGiven = NOT_GIVEN, + metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ChequeProfile: + """Creates a new Cheque Profile. + + Requires a `bankAccount` ID. Can optionally + include an attached letter via `letterHTML`, `letterTemplate`, or `letterPDF`. + If providing `letterPDF` or `logo` (if logo needs upload, though schema suggests + URL), use `multipart/form-data`. + + Args: + bank_account: ID of the bank account to use for the cheque. Required for creation. + + size: Enum representing the supported cheque sizes. + + expand: Optional list of related resources to expand in the response. + + currency_code: Enum representing the supported currency codes. + + description: An optional description for the profile. Set to `null` to remove during update. + + letter_pdf: PDF file for an optional attached letter. Cannot be used with `letterHTML` or + `letterTemplate`. Input only. + + letter_template: ID of a template for an optional attached letter. Cannot be used with + `letterHTML` or `letterPDF`. + + logo: A publicly accessible URL for the logo to print on the cheque. Set to `null` to + remove during update. + + mailing_class: Mailing class. Generally must be first class (or equivalent for destination + country) for cheques. + + memo: Memo line text for the cheque. Set to `null` to remove during update. + + merge_variables: Default merge variables for orders created using this profile. + + message: Message included on the cheque stub. Set to `null` to remove during update. + + metadata: Optional key-value metadata. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/print-mail/v1/order_profiles/cheques", + body=await async_maybe_transform( + { + "bank_account": bank_account, + "size": size, + "currency_code": currency_code, + "description": description, + "letter_pdf": letter_pdf, + "letter_template": letter_template, + "logo": logo, + "mailing_class": mailing_class, + "memo": memo, + "merge_variables": merge_variables, + "message": message, + "metadata": metadata, + }, + cheque_create_params.ChequeCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"expand": expand}, cheque_create_params.ChequeCreateParams), + ), + cast_to=ChequeProfile, + ) + + async def retrieve( + self, + id: str, + *, + expand: List[str] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ChequeProfile: + """ + Retrieves the details of a specific Cheque Profile. + + Args: + expand: Optional list of related resources to expand in the response. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._get( + f"/print-mail/v1/order_profiles/cheques/{id}", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"expand": expand}, cheque_retrieve_params.ChequeRetrieveParams), + ), + cast_to=ChequeProfile, + ) + + async def update( + self, + id: str, + *, + bank_account: str, + size: ChequeSize, + expand: List[str] | NotGiven = NOT_GIVEN, + currency_code: CurrencyCode | NotGiven = NOT_GIVEN, + description: Optional[str] | NotGiven = NOT_GIVEN, + letter_pdf: Union[str, Base64FileInput] | NotGiven = NOT_GIVEN, + letter_template: str | NotGiven = NOT_GIVEN, + logo: Optional[str] | NotGiven = NOT_GIVEN, + mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, + memo: Optional[str] | NotGiven = NOT_GIVEN, + merge_variables: Optional[Dict[str, object]] | NotGiven = NOT_GIVEN, + message: Optional[str] | NotGiven = NOT_GIVEN, + metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ChequeProfile: + """Updates specific fields of an existing Cheque Profile. + + If providing `letterPDF` + or `logo`, use `multipart/form-data`. + + Args: + bank_account: ID of the bank account to use for the cheque. Required for creation. + + size: Enum representing the supported cheque sizes. + + expand: Optional list of related resources to expand in the response. + + currency_code: Enum representing the supported currency codes. + + description: An optional description for the profile. Set to `null` to remove during update. + + letter_pdf: PDF file for an optional attached letter. Cannot be used with `letterHTML` or + `letterTemplate`. Input only. + + letter_template: ID of a template for an optional attached letter. Cannot be used with + `letterHTML` or `letterPDF`. + + logo: A publicly accessible URL for the logo to print on the cheque. Set to `null` to + remove during update. + + mailing_class: Mailing class. Generally must be first class (or equivalent for destination + country) for cheques. + + memo: Memo line text for the cheque. Set to `null` to remove during update. + + merge_variables: Default merge variables for orders created using this profile. + + message: Message included on the cheque stub. Set to `null` to remove during update. + + metadata: Optional key-value metadata. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._post( + f"/print-mail/v1/order_profiles/cheques/{id}", + body=await async_maybe_transform( + { + "bank_account": bank_account, + "size": size, + "currency_code": currency_code, + "description": description, + "letter_pdf": letter_pdf, + "letter_template": letter_template, + "logo": logo, + "mailing_class": mailing_class, + "memo": memo, + "merge_variables": merge_variables, + "message": message, + "metadata": metadata, + }, + cheque_update_params.ChequeUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"expand": expand}, cheque_update_params.ChequeUpdateParams), + ), + cast_to=ChequeProfile, + ) + + def list( + self, + *, + limit: int | NotGiven = NOT_GIVEN, + search: str | NotGiven = NOT_GIVEN, + skip: int | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncPaginator[ChequeListResponse, AsyncSkipLimit[ChequeListResponse]]: + """ + Retrieves a list of Cheque Profiles. + + Args: + search: You can supply any string to help narrow down the list of resources. For + example, if you pass `"New York"` (quoted), it will return resources that have + that string present somewhere in their response. Alternatively, you can supply a + structured search query. See the documentation on `StructuredSearchQuery` for + more details. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/print-mail/v1/order_profiles/cheques", + page=AsyncSkipLimit[ChequeListResponse], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "limit": limit, + "search": search, + "skip": skip, + }, + cheque_list_params.ChequeListParams, + ), + ), + model=ChequeListResponse, + ) + + async def delete( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ChequeDeleteResponse: + """ + Deletes a Cheque Profile. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._delete( + f"/print-mail/v1/order_profiles/cheques/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ChequeDeleteResponse, + ) + + +class ChequesResourceWithRawResponse: + def __init__(self, cheques: ChequesResource) -> None: + self._cheques = cheques + + self.create = to_raw_response_wrapper( + cheques.create, + ) + self.retrieve = to_raw_response_wrapper( + cheques.retrieve, + ) + self.update = to_raw_response_wrapper( + cheques.update, + ) + self.list = to_raw_response_wrapper( + cheques.list, + ) + self.delete = to_raw_response_wrapper( + cheques.delete, + ) + + +class AsyncChequesResourceWithRawResponse: + def __init__(self, cheques: AsyncChequesResource) -> None: + self._cheques = cheques + + self.create = async_to_raw_response_wrapper( + cheques.create, + ) + self.retrieve = async_to_raw_response_wrapper( + cheques.retrieve, + ) + self.update = async_to_raw_response_wrapper( + cheques.update, + ) + self.list = async_to_raw_response_wrapper( + cheques.list, + ) + self.delete = async_to_raw_response_wrapper( + cheques.delete, + ) + + +class ChequesResourceWithStreamingResponse: + def __init__(self, cheques: ChequesResource) -> None: + self._cheques = cheques + + self.create = to_streamed_response_wrapper( + cheques.create, + ) + self.retrieve = to_streamed_response_wrapper( + cheques.retrieve, + ) + self.update = to_streamed_response_wrapper( + cheques.update, + ) + self.list = to_streamed_response_wrapper( + cheques.list, + ) + self.delete = to_streamed_response_wrapper( + cheques.delete, + ) + + +class AsyncChequesResourceWithStreamingResponse: + def __init__(self, cheques: AsyncChequesResource) -> None: + self._cheques = cheques + + self.create = async_to_streamed_response_wrapper( + cheques.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + cheques.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + cheques.update, + ) + self.list = async_to_streamed_response_wrapper( + cheques.list, + ) + self.delete = async_to_streamed_response_wrapper( + cheques.delete, + ) diff --git a/src/postgrid/resources/print_mail/order_profiles/letters.py b/src/postgrid/resources/print_mail/order_profiles/letters.py new file mode 100644 index 0000000..62999ee --- /dev/null +++ b/src/postgrid/resources/print_mail/order_profiles/letters.py @@ -0,0 +1,823 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, List, Optional +from typing_extensions import Literal + +import httpx + +from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ...._utils import ( + maybe_transform, + async_maybe_transform, +) +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ....pagination import SyncSkipLimit, AsyncSkipLimit +from ...._base_client import AsyncPaginator, make_request_options +from ....types.print_mail import LetterSize, AddressPlacement, OrderMailingClass +from ....types.print_mail.letter_size import LetterSize +from ....types.print_mail.order_profiles import ( + letter_list_params, + letter_create_params, + letter_update_params, + letter_retrieve_params, +) +from ....types.print_mail.address_placement import AddressPlacement +from ....types.print_mail.attached_pdf_param import AttachedPdfParam +from ....types.print_mail.order_mailing_class import OrderMailingClass +from ....types.print_mail.order_profiles.letter_profile import LetterProfile +from ....types.print_mail.order_profiles.letter_delete_response import LetterDeleteResponse + +__all__ = ["LettersResource", "AsyncLettersResource"] + + +class LettersResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> LettersResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + """ + return LettersResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> LettersResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + """ + return LettersResourceWithStreamingResponse(self) + + def create( + self, + *, + size: LetterSize, + expand: List[str] | NotGiven = NOT_GIVEN, + address_placement: AddressPlacement | NotGiven = NOT_GIVEN, + attached_pdf: AttachedPdfParam | NotGiven = NOT_GIVEN, + color: bool | NotGiven = NOT_GIVEN, + description: Optional[str] | NotGiven = NOT_GIVEN, + double_sided: bool | NotGiven = NOT_GIVEN, + envelope: str | NotGiven = NOT_GIVEN, + mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, + merge_variables: Optional[Dict[str, object]] | NotGiven = NOT_GIVEN, + metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, + pdf: str | NotGiven = NOT_GIVEN, + perforated_page: Literal[1] | NotGiven = NOT_GIVEN, + return_envelope: str | NotGiven = NOT_GIVEN, + template: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> LetterProfile: + """Creates a new Letter Profile. + + You must provide either a `template` ID or upload + a `pdf` file for the content. If providing PDF files (`pdf` or + `attachedPDFFile`), the request `Content-Type` must be `multipart/form-data`. + + Args: + size: Enum representing the supported letter sizes. + + expand: Optional list of related resources to expand in the response. + + address_placement: Enum representing the placement of the address on the letter. + + attached_pdf: Model representing an attached PDF. + + color: Specifies whether to print in color (true) or black and white (false). + + description: An optional description for the profile. Set to `null` to remove during update. + + double_sided: Specifies whether to print on both sides of the paper. + + envelope: ID of a custom envelope to use. + + mailing_class: Mailing class. + + merge_variables: Default merge variables for orders created using this profile. + + metadata: Optional key-value metadata. + + pdf: A PDF file containing the letter content. Cannot be used with `template`. + + perforated_page: Specifies which page number should be perforated (if any). + + return_envelope: ID of a return envelope to include. + + template: ID of a template to use for the letter content. Cannot be used with `pdf`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/print-mail/v1/order_profiles/letters", + body=maybe_transform( + { + "size": size, + "address_placement": address_placement, + "attached_pdf": attached_pdf, + "color": color, + "description": description, + "double_sided": double_sided, + "envelope": envelope, + "mailing_class": mailing_class, + "merge_variables": merge_variables, + "metadata": metadata, + "pdf": pdf, + "perforated_page": perforated_page, + "return_envelope": return_envelope, + "template": template, + }, + letter_create_params.LetterCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"expand": expand}, letter_create_params.LetterCreateParams), + ), + cast_to=LetterProfile, + ) + + def retrieve( + self, + id: str, + *, + expand: List[str] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> LetterProfile: + """ + Retrieves the details of a specific Letter Profile by its ID. + + Args: + expand: Optional list of related resources to expand in the response. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._get( + f"/print-mail/v1/order_profiles/letters/{id}", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"expand": expand}, letter_retrieve_params.LetterRetrieveParams), + ), + cast_to=LetterProfile, + ) + + def update( + self, + id: str, + *, + expand: List[str] | NotGiven = NOT_GIVEN, + address_placement: AddressPlacement | NotGiven = NOT_GIVEN, + attached_pdf: AttachedPdfParam | NotGiven = NOT_GIVEN, + color: bool | NotGiven = NOT_GIVEN, + description: Optional[str] | NotGiven = NOT_GIVEN, + double_sided: bool | NotGiven = NOT_GIVEN, + envelope: str | NotGiven = NOT_GIVEN, + mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, + merge_variables: Optional[Dict[str, object]] | NotGiven = NOT_GIVEN, + metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, + pdf: str | NotGiven = NOT_GIVEN, + perforated_page: Literal[1] | NotGiven = NOT_GIVEN, + return_envelope: str | NotGiven = NOT_GIVEN, + template: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> LetterProfile: + """Updates specific fields of an existing Letter Profile. + + Only the fields provided + in the request body will be updated. If providing PDF files (`pdf` or + `attachedPDFFile`), the request `Content-Type` must be `multipart/form-data`. + + Args: + expand: Optional list of related resources to expand in the response. + + address_placement: Enum representing the placement of the address on the letter. + + attached_pdf: Model representing an attached PDF. + + color: Specifies whether to print in color (true) or black and white (false). + + description: An optional description for the profile. Set to `null` to remove during update. + + double_sided: Specifies whether to print on both sides of the paper. + + envelope: ID of a custom envelope to use. + + mailing_class: Mailing class. + + merge_variables: Default merge variables for orders created using this profile. + + metadata: Optional key-value metadata. + + pdf: A PDF file containing the letter content. Cannot be used with `template`. + + perforated_page: Specifies which page number should be perforated (if any). + + return_envelope: ID of a return envelope to include. + + template: ID of a template to use for the letter content. Cannot be used with `pdf`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._post( + f"/print-mail/v1/order_profiles/letters/{id}", + body=maybe_transform( + { + "address_placement": address_placement, + "attached_pdf": attached_pdf, + "color": color, + "description": description, + "double_sided": double_sided, + "envelope": envelope, + "mailing_class": mailing_class, + "merge_variables": merge_variables, + "metadata": metadata, + "pdf": pdf, + "perforated_page": perforated_page, + "return_envelope": return_envelope, + "template": template, + }, + letter_update_params.LetterUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"expand": expand}, letter_update_params.LetterUpdateParams), + ), + cast_to=LetterProfile, + ) + + def list( + self, + *, + limit: int | NotGiven = NOT_GIVEN, + search: str | NotGiven = NOT_GIVEN, + skip: int | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SyncSkipLimit[LetterProfile]: + """Retrieves a list of Letter Profiles. + + The profiles are returned sorted by + creation date, with the most recent appearing first. + + Args: + search: You can supply any string to help narrow down the list of resources. For + example, if you pass `"New York"` (quoted), it will return resources that have + that string present somewhere in their response. Alternatively, you can supply a + structured search query. See the documentation on `StructuredSearchQuery` for + more details. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/print-mail/v1/order_profiles/letters", + page=SyncSkipLimit[LetterProfile], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "limit": limit, + "search": search, + "skip": skip, + }, + letter_list_params.LetterListParams, + ), + ), + model=LetterProfile, + ) + + def delete( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> LetterDeleteResponse: + """Deletes a Letter Profile. + + This action cannot be undone. Orders previously + created using this profile are not affected. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._delete( + f"/print-mail/v1/order_profiles/letters/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=LetterDeleteResponse, + ) + + +class AsyncLettersResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncLettersResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + """ + return AsyncLettersResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncLettersResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + """ + return AsyncLettersResourceWithStreamingResponse(self) + + async def create( + self, + *, + size: LetterSize, + expand: List[str] | NotGiven = NOT_GIVEN, + address_placement: AddressPlacement | NotGiven = NOT_GIVEN, + attached_pdf: AttachedPdfParam | NotGiven = NOT_GIVEN, + color: bool | NotGiven = NOT_GIVEN, + description: Optional[str] | NotGiven = NOT_GIVEN, + double_sided: bool | NotGiven = NOT_GIVEN, + envelope: str | NotGiven = NOT_GIVEN, + mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, + merge_variables: Optional[Dict[str, object]] | NotGiven = NOT_GIVEN, + metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, + pdf: str | NotGiven = NOT_GIVEN, + perforated_page: Literal[1] | NotGiven = NOT_GIVEN, + return_envelope: str | NotGiven = NOT_GIVEN, + template: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> LetterProfile: + """Creates a new Letter Profile. + + You must provide either a `template` ID or upload + a `pdf` file for the content. If providing PDF files (`pdf` or + `attachedPDFFile`), the request `Content-Type` must be `multipart/form-data`. + + Args: + size: Enum representing the supported letter sizes. + + expand: Optional list of related resources to expand in the response. + + address_placement: Enum representing the placement of the address on the letter. + + attached_pdf: Model representing an attached PDF. + + color: Specifies whether to print in color (true) or black and white (false). + + description: An optional description for the profile. Set to `null` to remove during update. + + double_sided: Specifies whether to print on both sides of the paper. + + envelope: ID of a custom envelope to use. + + mailing_class: Mailing class. + + merge_variables: Default merge variables for orders created using this profile. + + metadata: Optional key-value metadata. + + pdf: A PDF file containing the letter content. Cannot be used with `template`. + + perforated_page: Specifies which page number should be perforated (if any). + + return_envelope: ID of a return envelope to include. + + template: ID of a template to use for the letter content. Cannot be used with `pdf`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/print-mail/v1/order_profiles/letters", + body=await async_maybe_transform( + { + "size": size, + "address_placement": address_placement, + "attached_pdf": attached_pdf, + "color": color, + "description": description, + "double_sided": double_sided, + "envelope": envelope, + "mailing_class": mailing_class, + "merge_variables": merge_variables, + "metadata": metadata, + "pdf": pdf, + "perforated_page": perforated_page, + "return_envelope": return_envelope, + "template": template, + }, + letter_create_params.LetterCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"expand": expand}, letter_create_params.LetterCreateParams), + ), + cast_to=LetterProfile, + ) + + async def retrieve( + self, + id: str, + *, + expand: List[str] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> LetterProfile: + """ + Retrieves the details of a specific Letter Profile by its ID. + + Args: + expand: Optional list of related resources to expand in the response. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._get( + f"/print-mail/v1/order_profiles/letters/{id}", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"expand": expand}, letter_retrieve_params.LetterRetrieveParams), + ), + cast_to=LetterProfile, + ) + + async def update( + self, + id: str, + *, + expand: List[str] | NotGiven = NOT_GIVEN, + address_placement: AddressPlacement | NotGiven = NOT_GIVEN, + attached_pdf: AttachedPdfParam | NotGiven = NOT_GIVEN, + color: bool | NotGiven = NOT_GIVEN, + description: Optional[str] | NotGiven = NOT_GIVEN, + double_sided: bool | NotGiven = NOT_GIVEN, + envelope: str | NotGiven = NOT_GIVEN, + mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, + merge_variables: Optional[Dict[str, object]] | NotGiven = NOT_GIVEN, + metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, + pdf: str | NotGiven = NOT_GIVEN, + perforated_page: Literal[1] | NotGiven = NOT_GIVEN, + return_envelope: str | NotGiven = NOT_GIVEN, + template: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> LetterProfile: + """Updates specific fields of an existing Letter Profile. + + Only the fields provided + in the request body will be updated. If providing PDF files (`pdf` or + `attachedPDFFile`), the request `Content-Type` must be `multipart/form-data`. + + Args: + expand: Optional list of related resources to expand in the response. + + address_placement: Enum representing the placement of the address on the letter. + + attached_pdf: Model representing an attached PDF. + + color: Specifies whether to print in color (true) or black and white (false). + + description: An optional description for the profile. Set to `null` to remove during update. + + double_sided: Specifies whether to print on both sides of the paper. + + envelope: ID of a custom envelope to use. + + mailing_class: Mailing class. + + merge_variables: Default merge variables for orders created using this profile. + + metadata: Optional key-value metadata. + + pdf: A PDF file containing the letter content. Cannot be used with `template`. + + perforated_page: Specifies which page number should be perforated (if any). + + return_envelope: ID of a return envelope to include. + + template: ID of a template to use for the letter content. Cannot be used with `pdf`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._post( + f"/print-mail/v1/order_profiles/letters/{id}", + body=await async_maybe_transform( + { + "address_placement": address_placement, + "attached_pdf": attached_pdf, + "color": color, + "description": description, + "double_sided": double_sided, + "envelope": envelope, + "mailing_class": mailing_class, + "merge_variables": merge_variables, + "metadata": metadata, + "pdf": pdf, + "perforated_page": perforated_page, + "return_envelope": return_envelope, + "template": template, + }, + letter_update_params.LetterUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"expand": expand}, letter_update_params.LetterUpdateParams), + ), + cast_to=LetterProfile, + ) + + def list( + self, + *, + limit: int | NotGiven = NOT_GIVEN, + search: str | NotGiven = NOT_GIVEN, + skip: int | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncPaginator[LetterProfile, AsyncSkipLimit[LetterProfile]]: + """Retrieves a list of Letter Profiles. + + The profiles are returned sorted by + creation date, with the most recent appearing first. + + Args: + search: You can supply any string to help narrow down the list of resources. For + example, if you pass `"New York"` (quoted), it will return resources that have + that string present somewhere in their response. Alternatively, you can supply a + structured search query. See the documentation on `StructuredSearchQuery` for + more details. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/print-mail/v1/order_profiles/letters", + page=AsyncSkipLimit[LetterProfile], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "limit": limit, + "search": search, + "skip": skip, + }, + letter_list_params.LetterListParams, + ), + ), + model=LetterProfile, + ) + + async def delete( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> LetterDeleteResponse: + """Deletes a Letter Profile. + + This action cannot be undone. Orders previously + created using this profile are not affected. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._delete( + f"/print-mail/v1/order_profiles/letters/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=LetterDeleteResponse, + ) + + +class LettersResourceWithRawResponse: + def __init__(self, letters: LettersResource) -> None: + self._letters = letters + + self.create = to_raw_response_wrapper( + letters.create, + ) + self.retrieve = to_raw_response_wrapper( + letters.retrieve, + ) + self.update = to_raw_response_wrapper( + letters.update, + ) + self.list = to_raw_response_wrapper( + letters.list, + ) + self.delete = to_raw_response_wrapper( + letters.delete, + ) + + +class AsyncLettersResourceWithRawResponse: + def __init__(self, letters: AsyncLettersResource) -> None: + self._letters = letters + + self.create = async_to_raw_response_wrapper( + letters.create, + ) + self.retrieve = async_to_raw_response_wrapper( + letters.retrieve, + ) + self.update = async_to_raw_response_wrapper( + letters.update, + ) + self.list = async_to_raw_response_wrapper( + letters.list, + ) + self.delete = async_to_raw_response_wrapper( + letters.delete, + ) + + +class LettersResourceWithStreamingResponse: + def __init__(self, letters: LettersResource) -> None: + self._letters = letters + + self.create = to_streamed_response_wrapper( + letters.create, + ) + self.retrieve = to_streamed_response_wrapper( + letters.retrieve, + ) + self.update = to_streamed_response_wrapper( + letters.update, + ) + self.list = to_streamed_response_wrapper( + letters.list, + ) + self.delete = to_streamed_response_wrapper( + letters.delete, + ) + + +class AsyncLettersResourceWithStreamingResponse: + def __init__(self, letters: AsyncLettersResource) -> None: + self._letters = letters + + self.create = async_to_streamed_response_wrapper( + letters.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + letters.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + letters.update, + ) + self.list = async_to_streamed_response_wrapper( + letters.list, + ) + self.delete = async_to_streamed_response_wrapper( + letters.delete, + ) diff --git a/src/postgrid/resources/print_mail/order_profiles/order_profiles.py b/src/postgrid/resources/print_mail/order_profiles/order_profiles.py new file mode 100644 index 0000000..8f339b4 --- /dev/null +++ b/src/postgrid/resources/print_mail/order_profiles/order_profiles.py @@ -0,0 +1,198 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .cheques import ( + ChequesResource, + AsyncChequesResource, + ChequesResourceWithRawResponse, + AsyncChequesResourceWithRawResponse, + ChequesResourceWithStreamingResponse, + AsyncChequesResourceWithStreamingResponse, +) +from .letters import ( + LettersResource, + AsyncLettersResource, + LettersResourceWithRawResponse, + AsyncLettersResourceWithRawResponse, + LettersResourceWithStreamingResponse, + AsyncLettersResourceWithStreamingResponse, +) +from .postcards import ( + PostcardsResource, + AsyncPostcardsResource, + PostcardsResourceWithRawResponse, + AsyncPostcardsResourceWithRawResponse, + PostcardsResourceWithStreamingResponse, + AsyncPostcardsResourceWithStreamingResponse, +) +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from .self_mailers import ( + SelfMailersResource, + AsyncSelfMailersResource, + SelfMailersResourceWithRawResponse, + AsyncSelfMailersResourceWithRawResponse, + SelfMailersResourceWithStreamingResponse, + AsyncSelfMailersResourceWithStreamingResponse, +) + +__all__ = ["OrderProfilesResource", "AsyncOrderProfilesResource"] + + +class OrderProfilesResource(SyncAPIResource): + @cached_property + def cheques(self) -> ChequesResource: + return ChequesResource(self._client) + + @cached_property + def letters(self) -> LettersResource: + return LettersResource(self._client) + + @cached_property + def postcards(self) -> PostcardsResource: + return PostcardsResource(self._client) + + @cached_property + def self_mailers(self) -> SelfMailersResource: + return SelfMailersResource(self._client) + + @cached_property + def with_raw_response(self) -> OrderProfilesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + """ + return OrderProfilesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> OrderProfilesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + """ + return OrderProfilesResourceWithStreamingResponse(self) + + +class AsyncOrderProfilesResource(AsyncAPIResource): + @cached_property + def cheques(self) -> AsyncChequesResource: + return AsyncChequesResource(self._client) + + @cached_property + def letters(self) -> AsyncLettersResource: + return AsyncLettersResource(self._client) + + @cached_property + def postcards(self) -> AsyncPostcardsResource: + return AsyncPostcardsResource(self._client) + + @cached_property + def self_mailers(self) -> AsyncSelfMailersResource: + return AsyncSelfMailersResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncOrderProfilesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + """ + return AsyncOrderProfilesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncOrderProfilesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + """ + return AsyncOrderProfilesResourceWithStreamingResponse(self) + + +class OrderProfilesResourceWithRawResponse: + def __init__(self, order_profiles: OrderProfilesResource) -> None: + self._order_profiles = order_profiles + + @cached_property + def cheques(self) -> ChequesResourceWithRawResponse: + return ChequesResourceWithRawResponse(self._order_profiles.cheques) + + @cached_property + def letters(self) -> LettersResourceWithRawResponse: + return LettersResourceWithRawResponse(self._order_profiles.letters) + + @cached_property + def postcards(self) -> PostcardsResourceWithRawResponse: + return PostcardsResourceWithRawResponse(self._order_profiles.postcards) + + @cached_property + def self_mailers(self) -> SelfMailersResourceWithRawResponse: + return SelfMailersResourceWithRawResponse(self._order_profiles.self_mailers) + + +class AsyncOrderProfilesResourceWithRawResponse: + def __init__(self, order_profiles: AsyncOrderProfilesResource) -> None: + self._order_profiles = order_profiles + + @cached_property + def cheques(self) -> AsyncChequesResourceWithRawResponse: + return AsyncChequesResourceWithRawResponse(self._order_profiles.cheques) + + @cached_property + def letters(self) -> AsyncLettersResourceWithRawResponse: + return AsyncLettersResourceWithRawResponse(self._order_profiles.letters) + + @cached_property + def postcards(self) -> AsyncPostcardsResourceWithRawResponse: + return AsyncPostcardsResourceWithRawResponse(self._order_profiles.postcards) + + @cached_property + def self_mailers(self) -> AsyncSelfMailersResourceWithRawResponse: + return AsyncSelfMailersResourceWithRawResponse(self._order_profiles.self_mailers) + + +class OrderProfilesResourceWithStreamingResponse: + def __init__(self, order_profiles: OrderProfilesResource) -> None: + self._order_profiles = order_profiles + + @cached_property + def cheques(self) -> ChequesResourceWithStreamingResponse: + return ChequesResourceWithStreamingResponse(self._order_profiles.cheques) + + @cached_property + def letters(self) -> LettersResourceWithStreamingResponse: + return LettersResourceWithStreamingResponse(self._order_profiles.letters) + + @cached_property + def postcards(self) -> PostcardsResourceWithStreamingResponse: + return PostcardsResourceWithStreamingResponse(self._order_profiles.postcards) + + @cached_property + def self_mailers(self) -> SelfMailersResourceWithStreamingResponse: + return SelfMailersResourceWithStreamingResponse(self._order_profiles.self_mailers) + + +class AsyncOrderProfilesResourceWithStreamingResponse: + def __init__(self, order_profiles: AsyncOrderProfilesResource) -> None: + self._order_profiles = order_profiles + + @cached_property + def cheques(self) -> AsyncChequesResourceWithStreamingResponse: + return AsyncChequesResourceWithStreamingResponse(self._order_profiles.cheques) + + @cached_property + def letters(self) -> AsyncLettersResourceWithStreamingResponse: + return AsyncLettersResourceWithStreamingResponse(self._order_profiles.letters) + + @cached_property + def postcards(self) -> AsyncPostcardsResourceWithStreamingResponse: + return AsyncPostcardsResourceWithStreamingResponse(self._order_profiles.postcards) + + @cached_property + def self_mailers(self) -> AsyncSelfMailersResourceWithStreamingResponse: + return AsyncSelfMailersResourceWithStreamingResponse(self._order_profiles.self_mailers) diff --git a/src/postgrid/resources/print_mail/order_profiles/postcards.py b/src/postgrid/resources/print_mail/order_profiles/postcards.py new file mode 100644 index 0000000..0908fa7 --- /dev/null +++ b/src/postgrid/resources/print_mail/order_profiles/postcards.py @@ -0,0 +1,723 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, List, Optional + +import httpx + +from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ...._utils import ( + maybe_transform, + async_maybe_transform, +) +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ....pagination import SyncSkipLimit, AsyncSkipLimit +from ...._base_client import AsyncPaginator, make_request_options +from ....types.print_mail import OrderMailingClass +from ....types.print_mail.order_profiles import ( + PostcardSize, + postcard_list_params, + postcard_create_params, + postcard_update_params, + postcard_retrieve_params, +) +from ....types.print_mail.order_mailing_class import OrderMailingClass +from ....types.print_mail.order_profiles.postcard_size import PostcardSize +from ....types.print_mail.order_profiles.postcard_profile import PostcardProfile +from ....types.print_mail.order_profiles.postcard_delete_response import PostcardDeleteResponse + +__all__ = ["PostcardsResource", "AsyncPostcardsResource"] + + +class PostcardsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> PostcardsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + """ + return PostcardsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> PostcardsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + """ + return PostcardsResourceWithStreamingResponse(self) + + def create( + self, + *, + size: PostcardSize, + expand: List[str] | NotGiven = NOT_GIVEN, + back_template: str | NotGiven = NOT_GIVEN, + description: Optional[str] | NotGiven = NOT_GIVEN, + front_template: str | NotGiven = NOT_GIVEN, + mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, + merge_variables: Optional[Dict[str, object]] | NotGiven = NOT_GIVEN, + metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, + pdf: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> PostcardProfile: + """Creates a new Postcard Profile. + + Provide either `frontTemplate` and + `backTemplate` IDs, or upload a 2-page `pdf`. If providing a `pdf`, the request + `Content-Type` must be `multipart/form-data`. + + Args: + size: Enum representing the supported postcard sizes. + + expand: Optional list of related resources to expand in the response. + + back_template: ID of the template for the back side. Required unless `pdf` is provided. + + description: An optional description for the profile. Set to `null` to remove during update. + + front_template: ID of the template for the front side. Required unless `pdf` is provided. + + mailing_class: Mailing class (cannot include extra services like `certified` or `registered` + for postcards, though). + + merge_variables: Default merge variables for orders created using this profile. + + metadata: Optional key-value metadata. + + pdf: A 2-page PDF file containing the postcard content (front and back). Cannot be + used with `frontTemplate`/`backTemplate`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/print-mail/v1/order_profiles/postcards", + body=maybe_transform( + { + "size": size, + "back_template": back_template, + "description": description, + "front_template": front_template, + "mailing_class": mailing_class, + "merge_variables": merge_variables, + "metadata": metadata, + "pdf": pdf, + }, + postcard_create_params.PostcardCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"expand": expand}, postcard_create_params.PostcardCreateParams), + ), + cast_to=PostcardProfile, + ) + + def retrieve( + self, + id: str, + *, + expand: List[str] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> PostcardProfile: + """ + Retrieves the details of a specific Postcard Profile. + + Args: + expand: Optional list of related resources to expand in the response. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._get( + f"/print-mail/v1/order_profiles/postcards/{id}", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"expand": expand}, postcard_retrieve_params.PostcardRetrieveParams), + ), + cast_to=PostcardProfile, + ) + + def update( + self, + id: str, + *, + expand: List[str] | NotGiven = NOT_GIVEN, + back_template: str | NotGiven = NOT_GIVEN, + description: Optional[str] | NotGiven = NOT_GIVEN, + front_template: str | NotGiven = NOT_GIVEN, + mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, + merge_variables: Optional[Dict[str, object]] | NotGiven = NOT_GIVEN, + metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, + pdf: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> PostcardProfile: + """Updates specific fields of an existing Postcard Profile. + + If providing a `pdf`, + the request `Content-Type` must be `multipart/form-data`. + + Args: + expand: Optional list of related resources to expand in the response. + + back_template: ID of the template for the back side. Required unless `pdf` is provided. + + description: An optional description for the profile. Set to `null` to remove during update. + + front_template: ID of the template for the front side. Required unless `pdf` is provided. + + mailing_class: Mailing class (cannot include extra services like `certified` or `registered` + for postcards, though). + + merge_variables: Default merge variables for orders created using this profile. + + metadata: Optional key-value metadata. + + pdf: A 2-page PDF file containing the postcard content (front and back). Cannot be + used with `frontTemplate`/`backTemplate`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._post( + f"/print-mail/v1/order_profiles/postcards/{id}", + body=maybe_transform( + { + "back_template": back_template, + "description": description, + "front_template": front_template, + "mailing_class": mailing_class, + "merge_variables": merge_variables, + "metadata": metadata, + "pdf": pdf, + }, + postcard_update_params.PostcardUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"expand": expand}, postcard_update_params.PostcardUpdateParams), + ), + cast_to=PostcardProfile, + ) + + def list( + self, + *, + limit: int | NotGiven = NOT_GIVEN, + search: str | NotGiven = NOT_GIVEN, + skip: int | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SyncSkipLimit[PostcardProfile]: + """ + Retrieves a list of Postcard Profiles. + + Args: + search: You can supply any string to help narrow down the list of resources. For + example, if you pass `"New York"` (quoted), it will return resources that have + that string present somewhere in their response. Alternatively, you can supply a + structured search query. See the documentation on `StructuredSearchQuery` for + more details. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/print-mail/v1/order_profiles/postcards", + page=SyncSkipLimit[PostcardProfile], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "limit": limit, + "search": search, + "skip": skip, + }, + postcard_list_params.PostcardListParams, + ), + ), + model=PostcardProfile, + ) + + def delete( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> PostcardDeleteResponse: + """ + Deletes a Postcard Profile. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._delete( + f"/print-mail/v1/order_profiles/postcards/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=PostcardDeleteResponse, + ) + + +class AsyncPostcardsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncPostcardsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + """ + return AsyncPostcardsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncPostcardsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + """ + return AsyncPostcardsResourceWithStreamingResponse(self) + + async def create( + self, + *, + size: PostcardSize, + expand: List[str] | NotGiven = NOT_GIVEN, + back_template: str | NotGiven = NOT_GIVEN, + description: Optional[str] | NotGiven = NOT_GIVEN, + front_template: str | NotGiven = NOT_GIVEN, + mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, + merge_variables: Optional[Dict[str, object]] | NotGiven = NOT_GIVEN, + metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, + pdf: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> PostcardProfile: + """Creates a new Postcard Profile. + + Provide either `frontTemplate` and + `backTemplate` IDs, or upload a 2-page `pdf`. If providing a `pdf`, the request + `Content-Type` must be `multipart/form-data`. + + Args: + size: Enum representing the supported postcard sizes. + + expand: Optional list of related resources to expand in the response. + + back_template: ID of the template for the back side. Required unless `pdf` is provided. + + description: An optional description for the profile. Set to `null` to remove during update. + + front_template: ID of the template for the front side. Required unless `pdf` is provided. + + mailing_class: Mailing class (cannot include extra services like `certified` or `registered` + for postcards, though). + + merge_variables: Default merge variables for orders created using this profile. + + metadata: Optional key-value metadata. + + pdf: A 2-page PDF file containing the postcard content (front and back). Cannot be + used with `frontTemplate`/`backTemplate`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/print-mail/v1/order_profiles/postcards", + body=await async_maybe_transform( + { + "size": size, + "back_template": back_template, + "description": description, + "front_template": front_template, + "mailing_class": mailing_class, + "merge_variables": merge_variables, + "metadata": metadata, + "pdf": pdf, + }, + postcard_create_params.PostcardCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"expand": expand}, postcard_create_params.PostcardCreateParams), + ), + cast_to=PostcardProfile, + ) + + async def retrieve( + self, + id: str, + *, + expand: List[str] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> PostcardProfile: + """ + Retrieves the details of a specific Postcard Profile. + + Args: + expand: Optional list of related resources to expand in the response. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._get( + f"/print-mail/v1/order_profiles/postcards/{id}", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"expand": expand}, postcard_retrieve_params.PostcardRetrieveParams), + ), + cast_to=PostcardProfile, + ) + + async def update( + self, + id: str, + *, + expand: List[str] | NotGiven = NOT_GIVEN, + back_template: str | NotGiven = NOT_GIVEN, + description: Optional[str] | NotGiven = NOT_GIVEN, + front_template: str | NotGiven = NOT_GIVEN, + mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, + merge_variables: Optional[Dict[str, object]] | NotGiven = NOT_GIVEN, + metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, + pdf: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> PostcardProfile: + """Updates specific fields of an existing Postcard Profile. + + If providing a `pdf`, + the request `Content-Type` must be `multipart/form-data`. + + Args: + expand: Optional list of related resources to expand in the response. + + back_template: ID of the template for the back side. Required unless `pdf` is provided. + + description: An optional description for the profile. Set to `null` to remove during update. + + front_template: ID of the template for the front side. Required unless `pdf` is provided. + + mailing_class: Mailing class (cannot include extra services like `certified` or `registered` + for postcards, though). + + merge_variables: Default merge variables for orders created using this profile. + + metadata: Optional key-value metadata. + + pdf: A 2-page PDF file containing the postcard content (front and back). Cannot be + used with `frontTemplate`/`backTemplate`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._post( + f"/print-mail/v1/order_profiles/postcards/{id}", + body=await async_maybe_transform( + { + "back_template": back_template, + "description": description, + "front_template": front_template, + "mailing_class": mailing_class, + "merge_variables": merge_variables, + "metadata": metadata, + "pdf": pdf, + }, + postcard_update_params.PostcardUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"expand": expand}, postcard_update_params.PostcardUpdateParams), + ), + cast_to=PostcardProfile, + ) + + def list( + self, + *, + limit: int | NotGiven = NOT_GIVEN, + search: str | NotGiven = NOT_GIVEN, + skip: int | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncPaginator[PostcardProfile, AsyncSkipLimit[PostcardProfile]]: + """ + Retrieves a list of Postcard Profiles. + + Args: + search: You can supply any string to help narrow down the list of resources. For + example, if you pass `"New York"` (quoted), it will return resources that have + that string present somewhere in their response. Alternatively, you can supply a + structured search query. See the documentation on `StructuredSearchQuery` for + more details. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/print-mail/v1/order_profiles/postcards", + page=AsyncSkipLimit[PostcardProfile], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "limit": limit, + "search": search, + "skip": skip, + }, + postcard_list_params.PostcardListParams, + ), + ), + model=PostcardProfile, + ) + + async def delete( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> PostcardDeleteResponse: + """ + Deletes a Postcard Profile. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._delete( + f"/print-mail/v1/order_profiles/postcards/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=PostcardDeleteResponse, + ) + + +class PostcardsResourceWithRawResponse: + def __init__(self, postcards: PostcardsResource) -> None: + self._postcards = postcards + + self.create = to_raw_response_wrapper( + postcards.create, + ) + self.retrieve = to_raw_response_wrapper( + postcards.retrieve, + ) + self.update = to_raw_response_wrapper( + postcards.update, + ) + self.list = to_raw_response_wrapper( + postcards.list, + ) + self.delete = to_raw_response_wrapper( + postcards.delete, + ) + + +class AsyncPostcardsResourceWithRawResponse: + def __init__(self, postcards: AsyncPostcardsResource) -> None: + self._postcards = postcards + + self.create = async_to_raw_response_wrapper( + postcards.create, + ) + self.retrieve = async_to_raw_response_wrapper( + postcards.retrieve, + ) + self.update = async_to_raw_response_wrapper( + postcards.update, + ) + self.list = async_to_raw_response_wrapper( + postcards.list, + ) + self.delete = async_to_raw_response_wrapper( + postcards.delete, + ) + + +class PostcardsResourceWithStreamingResponse: + def __init__(self, postcards: PostcardsResource) -> None: + self._postcards = postcards + + self.create = to_streamed_response_wrapper( + postcards.create, + ) + self.retrieve = to_streamed_response_wrapper( + postcards.retrieve, + ) + self.update = to_streamed_response_wrapper( + postcards.update, + ) + self.list = to_streamed_response_wrapper( + postcards.list, + ) + self.delete = to_streamed_response_wrapper( + postcards.delete, + ) + + +class AsyncPostcardsResourceWithStreamingResponse: + def __init__(self, postcards: AsyncPostcardsResource) -> None: + self._postcards = postcards + + self.create = async_to_streamed_response_wrapper( + postcards.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + postcards.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + postcards.update, + ) + self.list = async_to_streamed_response_wrapper( + postcards.list, + ) + self.delete = async_to_streamed_response_wrapper( + postcards.delete, + ) diff --git a/src/postgrid/resources/print_mail/order_profiles/self_mailers.py b/src/postgrid/resources/print_mail/order_profiles/self_mailers.py new file mode 100644 index 0000000..8cd1a21 --- /dev/null +++ b/src/postgrid/resources/print_mail/order_profiles/self_mailers.py @@ -0,0 +1,729 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, List, Optional + +import httpx + +from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ...._utils import ( + maybe_transform, + async_maybe_transform, +) +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ....pagination import SyncSkipLimit, AsyncSkipLimit +from ...._base_client import AsyncPaginator, make_request_options +from ....types.print_mail import OrderMailingClass +from ....types.print_mail.order_profiles import ( + SelfMailerSize, + self_mailer_list_params, + self_mailer_create_params, + self_mailer_update_params, + self_mailer_retrieve_params, +) +from ....types.print_mail.order_mailing_class import OrderMailingClass +from ....types.print_mail.order_profiles.self_mailer_size import SelfMailerSize +from ....types.print_mail.order_profiles.self_mailer_profile import SelfMailerProfile +from ....types.print_mail.order_profiles.self_mailer_delete_response import SelfMailerDeleteResponse + +__all__ = ["SelfMailersResource", "AsyncSelfMailersResource"] + + +class SelfMailersResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> SelfMailersResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + """ + return SelfMailersResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> SelfMailersResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + """ + return SelfMailersResourceWithStreamingResponse(self) + + def create( + self, + *, + size: SelfMailerSize, + expand: List[str] | NotGiven = NOT_GIVEN, + description: Optional[str] | NotGiven = NOT_GIVEN, + inside_template: str | NotGiven = NOT_GIVEN, + mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, + merge_variables: Optional[Dict[str, object]] | NotGiven = NOT_GIVEN, + metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, + outside_template: str | NotGiven = NOT_GIVEN, + pdf: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SelfMailerProfile: + """Creates a new Self-Mailer Profile. + + Provide either `insideTemplate` and + `outsideTemplate` IDs, or upload a 2-page `pdf`. If providing a `pdf`, the + request `Content-Type` must be `multipart/form-data`. + + Args: + size: Enum representing the supported self-mailer sizes. + + expand: Optional list of related resources to expand in the response. + + description: An optional description for the profile. Set to `null` to remove during update. + + inside_template: ID of the template for the inside. Required unless `pdf` is provided. + + mailing_class: Mailing class (cannot include extra services for self-mailers). + + merge_variables: Default merge variables for orders created using this profile. + + metadata: Optional key-value metadata. + + outside_template: ID of the template for the outside. Required unless `pdf` is provided. + + pdf: A 2-page PDF file containing the self-mailer content (inside and outside). + Cannot be used with `insideTemplate`/`outsideTemplate`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/print-mail/v1/order_profiles/self_mailers", + body=maybe_transform( + { + "size": size, + "description": description, + "inside_template": inside_template, + "mailing_class": mailing_class, + "merge_variables": merge_variables, + "metadata": metadata, + "outside_template": outside_template, + "pdf": pdf, + }, + self_mailer_create_params.SelfMailerCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"expand": expand}, self_mailer_create_params.SelfMailerCreateParams), + ), + cast_to=SelfMailerProfile, + ) + + def retrieve( + self, + id: str, + *, + expand: List[str] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SelfMailerProfile: + """ + Retrieves the details of a specific Self-Mailer Profile. + + Args: + expand: Optional list of related resources to expand in the response. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._get( + f"/print-mail/v1/order_profiles/self_mailers/{id}", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"expand": expand}, self_mailer_retrieve_params.SelfMailerRetrieveParams), + ), + cast_to=SelfMailerProfile, + ) + + def update( + self, + id: str, + *, + size: SelfMailerSize, + expand: List[str] | NotGiven = NOT_GIVEN, + description: Optional[str] | NotGiven = NOT_GIVEN, + inside_template: str | NotGiven = NOT_GIVEN, + mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, + merge_variables: Optional[Dict[str, object]] | NotGiven = NOT_GIVEN, + metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, + outside_template: str | NotGiven = NOT_GIVEN, + pdf: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SelfMailerProfile: + """Updates specific fields of an existing Self-Mailer Profile. + + If providing a + `pdf`, the request `Content-Type` must be `multipart/form-data`. + + Args: + size: Enum representing the supported self-mailer sizes. + + expand: Optional list of related resources to expand in the response. + + description: An optional description for the profile. Set to `null` to remove during update. + + inside_template: ID of the template for the inside. Required unless `pdf` is provided. + + mailing_class: Mailing class (cannot include extra services for self-mailers). + + merge_variables: Default merge variables for orders created using this profile. + + metadata: Optional key-value metadata. + + outside_template: ID of the template for the outside. Required unless `pdf` is provided. + + pdf: A 2-page PDF file containing the self-mailer content (inside and outside). + Cannot be used with `insideTemplate`/`outsideTemplate`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._post( + f"/print-mail/v1/order_profiles/self_mailers/{id}", + body=maybe_transform( + { + "size": size, + "description": description, + "inside_template": inside_template, + "mailing_class": mailing_class, + "merge_variables": merge_variables, + "metadata": metadata, + "outside_template": outside_template, + "pdf": pdf, + }, + self_mailer_update_params.SelfMailerUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"expand": expand}, self_mailer_update_params.SelfMailerUpdateParams), + ), + cast_to=SelfMailerProfile, + ) + + def list( + self, + *, + limit: int | NotGiven = NOT_GIVEN, + search: str | NotGiven = NOT_GIVEN, + skip: int | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SyncSkipLimit[SelfMailerProfile]: + """ + Retrieves a list of Self-Mailer Profiles. + + Args: + search: You can supply any string to help narrow down the list of resources. For + example, if you pass `"New York"` (quoted), it will return resources that have + that string present somewhere in their response. Alternatively, you can supply a + structured search query. See the documentation on `StructuredSearchQuery` for + more details. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/print-mail/v1/order_profiles/self_mailers", + page=SyncSkipLimit[SelfMailerProfile], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "limit": limit, + "search": search, + "skip": skip, + }, + self_mailer_list_params.SelfMailerListParams, + ), + ), + model=SelfMailerProfile, + ) + + def delete( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SelfMailerDeleteResponse: + """ + Deletes a Self-Mailer Profile. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._delete( + f"/print-mail/v1/order_profiles/self_mailers/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=SelfMailerDeleteResponse, + ) + + +class AsyncSelfMailersResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncSelfMailersResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + """ + return AsyncSelfMailersResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncSelfMailersResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + """ + return AsyncSelfMailersResourceWithStreamingResponse(self) + + async def create( + self, + *, + size: SelfMailerSize, + expand: List[str] | NotGiven = NOT_GIVEN, + description: Optional[str] | NotGiven = NOT_GIVEN, + inside_template: str | NotGiven = NOT_GIVEN, + mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, + merge_variables: Optional[Dict[str, object]] | NotGiven = NOT_GIVEN, + metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, + outside_template: str | NotGiven = NOT_GIVEN, + pdf: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SelfMailerProfile: + """Creates a new Self-Mailer Profile. + + Provide either `insideTemplate` and + `outsideTemplate` IDs, or upload a 2-page `pdf`. If providing a `pdf`, the + request `Content-Type` must be `multipart/form-data`. + + Args: + size: Enum representing the supported self-mailer sizes. + + expand: Optional list of related resources to expand in the response. + + description: An optional description for the profile. Set to `null` to remove during update. + + inside_template: ID of the template for the inside. Required unless `pdf` is provided. + + mailing_class: Mailing class (cannot include extra services for self-mailers). + + merge_variables: Default merge variables for orders created using this profile. + + metadata: Optional key-value metadata. + + outside_template: ID of the template for the outside. Required unless `pdf` is provided. + + pdf: A 2-page PDF file containing the self-mailer content (inside and outside). + Cannot be used with `insideTemplate`/`outsideTemplate`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/print-mail/v1/order_profiles/self_mailers", + body=await async_maybe_transform( + { + "size": size, + "description": description, + "inside_template": inside_template, + "mailing_class": mailing_class, + "merge_variables": merge_variables, + "metadata": metadata, + "outside_template": outside_template, + "pdf": pdf, + }, + self_mailer_create_params.SelfMailerCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"expand": expand}, self_mailer_create_params.SelfMailerCreateParams), + ), + cast_to=SelfMailerProfile, + ) + + async def retrieve( + self, + id: str, + *, + expand: List[str] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SelfMailerProfile: + """ + Retrieves the details of a specific Self-Mailer Profile. + + Args: + expand: Optional list of related resources to expand in the response. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._get( + f"/print-mail/v1/order_profiles/self_mailers/{id}", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"expand": expand}, self_mailer_retrieve_params.SelfMailerRetrieveParams + ), + ), + cast_to=SelfMailerProfile, + ) + + async def update( + self, + id: str, + *, + size: SelfMailerSize, + expand: List[str] | NotGiven = NOT_GIVEN, + description: Optional[str] | NotGiven = NOT_GIVEN, + inside_template: str | NotGiven = NOT_GIVEN, + mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, + merge_variables: Optional[Dict[str, object]] | NotGiven = NOT_GIVEN, + metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, + outside_template: str | NotGiven = NOT_GIVEN, + pdf: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SelfMailerProfile: + """Updates specific fields of an existing Self-Mailer Profile. + + If providing a + `pdf`, the request `Content-Type` must be `multipart/form-data`. + + Args: + size: Enum representing the supported self-mailer sizes. + + expand: Optional list of related resources to expand in the response. + + description: An optional description for the profile. Set to `null` to remove during update. + + inside_template: ID of the template for the inside. Required unless `pdf` is provided. + + mailing_class: Mailing class (cannot include extra services for self-mailers). + + merge_variables: Default merge variables for orders created using this profile. + + metadata: Optional key-value metadata. + + outside_template: ID of the template for the outside. Required unless `pdf` is provided. + + pdf: A 2-page PDF file containing the self-mailer content (inside and outside). + Cannot be used with `insideTemplate`/`outsideTemplate`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._post( + f"/print-mail/v1/order_profiles/self_mailers/{id}", + body=await async_maybe_transform( + { + "size": size, + "description": description, + "inside_template": inside_template, + "mailing_class": mailing_class, + "merge_variables": merge_variables, + "metadata": metadata, + "outside_template": outside_template, + "pdf": pdf, + }, + self_mailer_update_params.SelfMailerUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"expand": expand}, self_mailer_update_params.SelfMailerUpdateParams), + ), + cast_to=SelfMailerProfile, + ) + + def list( + self, + *, + limit: int | NotGiven = NOT_GIVEN, + search: str | NotGiven = NOT_GIVEN, + skip: int | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncPaginator[SelfMailerProfile, AsyncSkipLimit[SelfMailerProfile]]: + """ + Retrieves a list of Self-Mailer Profiles. + + Args: + search: You can supply any string to help narrow down the list of resources. For + example, if you pass `"New York"` (quoted), it will return resources that have + that string present somewhere in their response. Alternatively, you can supply a + structured search query. See the documentation on `StructuredSearchQuery` for + more details. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/print-mail/v1/order_profiles/self_mailers", + page=AsyncSkipLimit[SelfMailerProfile], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "limit": limit, + "search": search, + "skip": skip, + }, + self_mailer_list_params.SelfMailerListParams, + ), + ), + model=SelfMailerProfile, + ) + + async def delete( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SelfMailerDeleteResponse: + """ + Deletes a Self-Mailer Profile. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._delete( + f"/print-mail/v1/order_profiles/self_mailers/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=SelfMailerDeleteResponse, + ) + + +class SelfMailersResourceWithRawResponse: + def __init__(self, self_mailers: SelfMailersResource) -> None: + self._self_mailers = self_mailers + + self.create = to_raw_response_wrapper( + self_mailers.create, + ) + self.retrieve = to_raw_response_wrapper( + self_mailers.retrieve, + ) + self.update = to_raw_response_wrapper( + self_mailers.update, + ) + self.list = to_raw_response_wrapper( + self_mailers.list, + ) + self.delete = to_raw_response_wrapper( + self_mailers.delete, + ) + + +class AsyncSelfMailersResourceWithRawResponse: + def __init__(self, self_mailers: AsyncSelfMailersResource) -> None: + self._self_mailers = self_mailers + + self.create = async_to_raw_response_wrapper( + self_mailers.create, + ) + self.retrieve = async_to_raw_response_wrapper( + self_mailers.retrieve, + ) + self.update = async_to_raw_response_wrapper( + self_mailers.update, + ) + self.list = async_to_raw_response_wrapper( + self_mailers.list, + ) + self.delete = async_to_raw_response_wrapper( + self_mailers.delete, + ) + + +class SelfMailersResourceWithStreamingResponse: + def __init__(self, self_mailers: SelfMailersResource) -> None: + self._self_mailers = self_mailers + + self.create = to_streamed_response_wrapper( + self_mailers.create, + ) + self.retrieve = to_streamed_response_wrapper( + self_mailers.retrieve, + ) + self.update = to_streamed_response_wrapper( + self_mailers.update, + ) + self.list = to_streamed_response_wrapper( + self_mailers.list, + ) + self.delete = to_streamed_response_wrapper( + self_mailers.delete, + ) + + +class AsyncSelfMailersResourceWithStreamingResponse: + def __init__(self, self_mailers: AsyncSelfMailersResource) -> None: + self._self_mailers = self_mailers + + self.create = async_to_streamed_response_wrapper( + self_mailers.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + self_mailers.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + self_mailers.update, + ) + self.list = async_to_streamed_response_wrapper( + self_mailers.list, + ) + self.delete = async_to_streamed_response_wrapper( + self_mailers.delete, + ) diff --git a/src/postgrid/resources/print_mail/postcards.py b/src/postgrid/resources/print_mail/postcards.py new file mode 100644 index 0000000..950196d --- /dev/null +++ b/src/postgrid/resources/print_mail/postcards.py @@ -0,0 +1,1097 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union +from datetime import datetime +from typing_extensions import overload + +import httpx + +from ..._types import ( + NOT_GIVEN, + Body, + Query, + Headers, + NotGiven, + Base64FileInput, +) +from ..._utils import ( + required_args, + maybe_transform, + async_maybe_transform, +) +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...pagination import SyncSkipLimit, AsyncSkipLimit +from ..._base_client import AsyncPaginator, make_request_options +from ...types.print_mail import OrderMailingClass, postcard_list_params, postcard_create_params +from ...types.print_mail.postcard import Postcard +from ...types.print_mail.order_profiles import PostcardSize +from ...types.print_mail.order_mailing_class import OrderMailingClass +from ...types.print_mail.order_profiles.postcard_size import PostcardSize +from ...types.print_mail.postcard_retrieve_url_response import PostcardRetrieveURLResponse + +__all__ = ["PostcardsResource", "AsyncPostcardsResource"] + + +class PostcardsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> PostcardsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + """ + return PostcardsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> PostcardsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + """ + return PostcardsResourceWithStreamingResponse(self) + + @overload + def create( + self, + *, + back_html: str, + front_html: str, + size: PostcardSize, + to: postcard_create_params.PostcardCreateWithHTMLTo, + description: str | NotGiven = NOT_GIVEN, + from_: postcard_create_params.PostcardCreateWithHTMLFrom | NotGiven = NOT_GIVEN, + mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, + merge_variables: Dict[str, object] | NotGiven = NOT_GIVEN, + metadata: Dict[str, object] | NotGiven = NOT_GIVEN, + send_date: Union[str, datetime] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Postcard: + """Create a postcard. + + Note that you can supply one of the following: + + - HTML content for the front and back of the postcard + - A template ID for the front and back of the postcard + - A URL or file for a 2 page PDF where the first page is the front of the + postcard and the second page is the back + - Upload the aforementioned PDF file via a multipart form upload request + + Args: + back_html: The HTML content for the back of the postcard. You can supply _either_ this or + `backTemplate` but not both. + + front_html: The HTML content for the front of the postcard. You can supply _either_ this or + `frontTemplate` but not both. + + size: Enum representing the supported postcard sizes. + + to: The recipient of this order. You can either supply the contact information + inline here or provide a contact ID. PostGrid will automatically deduplicate + contacts regardless of whether you provide the information inline here or call + the contact creation endpoint. + + description: An optional string describing this resource. Will be visible in the API and the + dashboard. + + from_: The contact information of the sender. You can pass contact information inline + here just like you can for the `to`. Unlike other order types, the sender + address is optional for postcards. + + mailing_class: The mailing class of this order. If not provided, automatically set to + `first_class`. + + merge_variables: These will be merged with the variables in the template or HTML you create this + order with. The keys in this object should match the variable names in the + template _exactly_ as they are case-sensitive. Note that these _do not_ apply to + PDFs uploaded with the order. + + metadata: See the section on Metadata. + + send_date: This order will transition from `ready` to `printing` on the day after this + date. You can use this parameter to schedule orders for a future date. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def create( + self, + *, + back_template: str, + front_template: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Postcard: + """Create a postcard. + + Note that you can supply one of the following: + + - HTML content for the front and back of the postcard + - A template ID for the front and back of the postcard + - A URL or file for a 2 page PDF where the first page is the front of the + postcard and the second page is the back + - Upload the aforementioned PDF file via a multipart form upload request + + Args: + back_template: The template ID for the back of the postcard. You can supply _either_ this or + `backHTML` but not both. + + front_template: The template ID for the front of the postcard. You can supply _either_ this or + `frontHTML` but not both. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def create( + self, + *, + pdf: str, + size: PostcardSize, + to: postcard_create_params.PostcardCreateWithPdfurlTo, + description: str | NotGiven = NOT_GIVEN, + from_: postcard_create_params.PostcardCreateWithPdfurlFrom | NotGiven = NOT_GIVEN, + mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, + merge_variables: Dict[str, object] | NotGiven = NOT_GIVEN, + metadata: Dict[str, object] | NotGiven = NOT_GIVEN, + send_date: Union[str, datetime] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Postcard: + """Create a postcard. + + Note that you can supply one of the following: + + - HTML content for the front and back of the postcard + - A template ID for the front and back of the postcard + - A URL or file for a 2 page PDF where the first page is the front of the + postcard and the second page is the back + - Upload the aforementioned PDF file via a multipart form upload request + + Args: + pdf: A URL pointing to a 2 page PDF file. The first page is the front of the postcard + and the second page is the back (where the address will be stamped on). + + size: Enum representing the supported postcard sizes. + + to: The recipient of this order. You can either supply the contact information + inline here or provide a contact ID. PostGrid will automatically deduplicate + contacts regardless of whether you provide the information inline here or call + the contact creation endpoint. + + description: An optional string describing this resource. Will be visible in the API and the + dashboard. + + from_: The contact information of the sender. You can pass contact information inline + here just like you can for the `to`. Unlike other order types, the sender + address is optional for postcards. + + mailing_class: The mailing class of this order. If not provided, automatically set to + `first_class`. + + merge_variables: These will be merged with the variables in the template or HTML you create this + order with. The keys in this object should match the variable names in the + template _exactly_ as they are case-sensitive. Note that these _do not_ apply to + PDFs uploaded with the order. + + metadata: See the section on Metadata. + + send_date: This order will transition from `ready` to `printing` on the day after this + date. You can use this parameter to schedule orders for a future date. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def create( + self, + *, + pdf: Union[str, Base64FileInput], + size: PostcardSize, + to: postcard_create_params.PostcardCreateWithPdfFileTo, + description: str | NotGiven = NOT_GIVEN, + from_: postcard_create_params.PostcardCreateWithPdfFileFrom | NotGiven = NOT_GIVEN, + mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, + merge_variables: Dict[str, object] | NotGiven = NOT_GIVEN, + metadata: Dict[str, object] | NotGiven = NOT_GIVEN, + send_date: Union[str, datetime] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Postcard: + """Create a postcard. + + Note that you can supply one of the following: + + - HTML content for the front and back of the postcard + - A template ID for the front and back of the postcard + - A URL or file for a 2 page PDF where the first page is the front of the + postcard and the second page is the back + - Upload the aforementioned PDF file via a multipart form upload request + + Args: + pdf: A 2 page PDF file. The first page is the front of the postcard and the second + page is the back (where the address will be stamped on). + + size: Enum representing the supported postcard sizes. + + to: The recipient of this order. You can either supply the contact information + inline here or provide a contact ID. PostGrid will automatically deduplicate + contacts regardless of whether you provide the information inline here or call + the contact creation endpoint. + + description: An optional string describing this resource. Will be visible in the API and the + dashboard. + + from_: The contact information of the sender. You can pass contact information inline + here just like you can for the `to`. Unlike other order types, the sender + address is optional for postcards. + + mailing_class: The mailing class of this order. If not provided, automatically set to + `first_class`. + + merge_variables: These will be merged with the variables in the template or HTML you create this + order with. The keys in this object should match the variable names in the + template _exactly_ as they are case-sensitive. Note that these _do not_ apply to + PDFs uploaded with the order. + + metadata: See the section on Metadata. + + send_date: This order will transition from `ready` to `printing` on the day after this + date. You can use this parameter to schedule orders for a future date. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args( + ["back_html", "front_html", "size", "to"], ["back_template", "front_template"], ["pdf", "size", "to"] + ) + def create( + self, + *, + back_html: str | NotGiven = NOT_GIVEN, + front_html: str | NotGiven = NOT_GIVEN, + size: PostcardSize | NotGiven = NOT_GIVEN, + to: postcard_create_params.PostcardCreateWithHTMLTo | NotGiven = NOT_GIVEN, + description: str | NotGiven = NOT_GIVEN, + from_: postcard_create_params.PostcardCreateWithHTMLFrom | NotGiven = NOT_GIVEN, + mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, + merge_variables: Dict[str, object] | NotGiven = NOT_GIVEN, + metadata: Dict[str, object] | NotGiven = NOT_GIVEN, + send_date: Union[str, datetime] | NotGiven = NOT_GIVEN, + back_template: str | NotGiven = NOT_GIVEN, + front_template: str | NotGiven = NOT_GIVEN, + pdf: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Postcard: + return self._post( + "/print-mail/v1/postcards", + body=maybe_transform( + { + "back_html": back_html, + "front_html": front_html, + "size": size, + "to": to, + "description": description, + "from_": from_, + "mailing_class": mailing_class, + "merge_variables": merge_variables, + "metadata": metadata, + "send_date": send_date, + "back_template": back_template, + "front_template": front_template, + "pdf": pdf, + }, + postcard_create_params.PostcardCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Postcard, + ) + + def retrieve( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Postcard: + """ + Retrieve a postcard by ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._get( + f"/print-mail/v1/postcards/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Postcard, + ) + + def list( + self, + *, + limit: int | NotGiven = NOT_GIVEN, + search: str | NotGiven = NOT_GIVEN, + skip: int | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SyncSkipLimit[Postcard]: + """ + Get a list of postcards. + + Args: + search: You can supply any string to help narrow down the list of resources. For + example, if you pass `"New York"` (quoted), it will return resources that have + that string present somewhere in their response. Alternatively, you can supply a + structured search query. See the documentation on `StructuredSearchQuery` for + more details. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/print-mail/v1/postcards", + page=SyncSkipLimit[Postcard], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "limit": limit, + "search": search, + "skip": skip, + }, + postcard_list_params.PostcardListParams, + ), + ), + model=Postcard, + ) + + def delete( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Postcard: + """Cancel a postcard by ID. + + Note that this operation cannot be undone. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._delete( + f"/print-mail/v1/postcards/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Postcard, + ) + + def retrieve_url( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> PostcardRetrieveURLResponse: + """ + Retrieve a postcard preview URL. + + This is only available for customers with our document management addon, which + offers document generation and hosting capabilities. This endpoint has a much + higher rate limit than the regular order retrieval endpoint, so it is suitable + for customer-facing use-cases. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._get( + f"/print-mail/v1/postcards/{id}/url", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=PostcardRetrieveURLResponse, + ) + + +class AsyncPostcardsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncPostcardsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + """ + return AsyncPostcardsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncPostcardsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + """ + return AsyncPostcardsResourceWithStreamingResponse(self) + + @overload + async def create( + self, + *, + back_html: str, + front_html: str, + size: PostcardSize, + to: postcard_create_params.PostcardCreateWithHTMLTo, + description: str | NotGiven = NOT_GIVEN, + from_: postcard_create_params.PostcardCreateWithHTMLFrom | NotGiven = NOT_GIVEN, + mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, + merge_variables: Dict[str, object] | NotGiven = NOT_GIVEN, + metadata: Dict[str, object] | NotGiven = NOT_GIVEN, + send_date: Union[str, datetime] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Postcard: + """Create a postcard. + + Note that you can supply one of the following: + + - HTML content for the front and back of the postcard + - A template ID for the front and back of the postcard + - A URL or file for a 2 page PDF where the first page is the front of the + postcard and the second page is the back + - Upload the aforementioned PDF file via a multipart form upload request + + Args: + back_html: The HTML content for the back of the postcard. You can supply _either_ this or + `backTemplate` but not both. + + front_html: The HTML content for the front of the postcard. You can supply _either_ this or + `frontTemplate` but not both. + + size: Enum representing the supported postcard sizes. + + to: The recipient of this order. You can either supply the contact information + inline here or provide a contact ID. PostGrid will automatically deduplicate + contacts regardless of whether you provide the information inline here or call + the contact creation endpoint. + + description: An optional string describing this resource. Will be visible in the API and the + dashboard. + + from_: The contact information of the sender. You can pass contact information inline + here just like you can for the `to`. Unlike other order types, the sender + address is optional for postcards. + + mailing_class: The mailing class of this order. If not provided, automatically set to + `first_class`. + + merge_variables: These will be merged with the variables in the template or HTML you create this + order with. The keys in this object should match the variable names in the + template _exactly_ as they are case-sensitive. Note that these _do not_ apply to + PDFs uploaded with the order. + + metadata: See the section on Metadata. + + send_date: This order will transition from `ready` to `printing` on the day after this + date. You can use this parameter to schedule orders for a future date. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def create( + self, + *, + back_template: str, + front_template: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Postcard: + """Create a postcard. + + Note that you can supply one of the following: + + - HTML content for the front and back of the postcard + - A template ID for the front and back of the postcard + - A URL or file for a 2 page PDF where the first page is the front of the + postcard and the second page is the back + - Upload the aforementioned PDF file via a multipart form upload request + + Args: + back_template: The template ID for the back of the postcard. You can supply _either_ this or + `backHTML` but not both. + + front_template: The template ID for the front of the postcard. You can supply _either_ this or + `frontHTML` but not both. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def create( + self, + *, + pdf: str, + size: PostcardSize, + to: postcard_create_params.PostcardCreateWithPdfurlTo, + description: str | NotGiven = NOT_GIVEN, + from_: postcard_create_params.PostcardCreateWithPdfurlFrom | NotGiven = NOT_GIVEN, + mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, + merge_variables: Dict[str, object] | NotGiven = NOT_GIVEN, + metadata: Dict[str, object] | NotGiven = NOT_GIVEN, + send_date: Union[str, datetime] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Postcard: + """Create a postcard. + + Note that you can supply one of the following: + + - HTML content for the front and back of the postcard + - A template ID for the front and back of the postcard + - A URL or file for a 2 page PDF where the first page is the front of the + postcard and the second page is the back + - Upload the aforementioned PDF file via a multipart form upload request + + Args: + pdf: A URL pointing to a 2 page PDF file. The first page is the front of the postcard + and the second page is the back (where the address will be stamped on). + + size: Enum representing the supported postcard sizes. + + to: The recipient of this order. You can either supply the contact information + inline here or provide a contact ID. PostGrid will automatically deduplicate + contacts regardless of whether you provide the information inline here or call + the contact creation endpoint. + + description: An optional string describing this resource. Will be visible in the API and the + dashboard. + + from_: The contact information of the sender. You can pass contact information inline + here just like you can for the `to`. Unlike other order types, the sender + address is optional for postcards. + + mailing_class: The mailing class of this order. If not provided, automatically set to + `first_class`. + + merge_variables: These will be merged with the variables in the template or HTML you create this + order with. The keys in this object should match the variable names in the + template _exactly_ as they are case-sensitive. Note that these _do not_ apply to + PDFs uploaded with the order. + + metadata: See the section on Metadata. + + send_date: This order will transition from `ready` to `printing` on the day after this + date. You can use this parameter to schedule orders for a future date. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def create( + self, + *, + pdf: Union[str, Base64FileInput], + size: PostcardSize, + to: postcard_create_params.PostcardCreateWithPdfFileTo, + description: str | NotGiven = NOT_GIVEN, + from_: postcard_create_params.PostcardCreateWithPdfFileFrom | NotGiven = NOT_GIVEN, + mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, + merge_variables: Dict[str, object] | NotGiven = NOT_GIVEN, + metadata: Dict[str, object] | NotGiven = NOT_GIVEN, + send_date: Union[str, datetime] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Postcard: + """Create a postcard. + + Note that you can supply one of the following: + + - HTML content for the front and back of the postcard + - A template ID for the front and back of the postcard + - A URL or file for a 2 page PDF where the first page is the front of the + postcard and the second page is the back + - Upload the aforementioned PDF file via a multipart form upload request + + Args: + pdf: A 2 page PDF file. The first page is the front of the postcard and the second + page is the back (where the address will be stamped on). + + size: Enum representing the supported postcard sizes. + + to: The recipient of this order. You can either supply the contact information + inline here or provide a contact ID. PostGrid will automatically deduplicate + contacts regardless of whether you provide the information inline here or call + the contact creation endpoint. + + description: An optional string describing this resource. Will be visible in the API and the + dashboard. + + from_: The contact information of the sender. You can pass contact information inline + here just like you can for the `to`. Unlike other order types, the sender + address is optional for postcards. + + mailing_class: The mailing class of this order. If not provided, automatically set to + `first_class`. + + merge_variables: These will be merged with the variables in the template or HTML you create this + order with. The keys in this object should match the variable names in the + template _exactly_ as they are case-sensitive. Note that these _do not_ apply to + PDFs uploaded with the order. + + metadata: See the section on Metadata. + + send_date: This order will transition from `ready` to `printing` on the day after this + date. You can use this parameter to schedule orders for a future date. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args( + ["back_html", "front_html", "size", "to"], ["back_template", "front_template"], ["pdf", "size", "to"] + ) + async def create( + self, + *, + back_html: str | NotGiven = NOT_GIVEN, + front_html: str | NotGiven = NOT_GIVEN, + size: PostcardSize | NotGiven = NOT_GIVEN, + to: postcard_create_params.PostcardCreateWithHTMLTo | NotGiven = NOT_GIVEN, + description: str | NotGiven = NOT_GIVEN, + from_: postcard_create_params.PostcardCreateWithHTMLFrom | NotGiven = NOT_GIVEN, + mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, + merge_variables: Dict[str, object] | NotGiven = NOT_GIVEN, + metadata: Dict[str, object] | NotGiven = NOT_GIVEN, + send_date: Union[str, datetime] | NotGiven = NOT_GIVEN, + back_template: str | NotGiven = NOT_GIVEN, + front_template: str | NotGiven = NOT_GIVEN, + pdf: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Postcard: + return await self._post( + "/print-mail/v1/postcards", + body=await async_maybe_transform( + { + "back_html": back_html, + "front_html": front_html, + "size": size, + "to": to, + "description": description, + "from_": from_, + "mailing_class": mailing_class, + "merge_variables": merge_variables, + "metadata": metadata, + "send_date": send_date, + "back_template": back_template, + "front_template": front_template, + "pdf": pdf, + }, + postcard_create_params.PostcardCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Postcard, + ) + + async def retrieve( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Postcard: + """ + Retrieve a postcard by ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._get( + f"/print-mail/v1/postcards/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Postcard, + ) + + def list( + self, + *, + limit: int | NotGiven = NOT_GIVEN, + search: str | NotGiven = NOT_GIVEN, + skip: int | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncPaginator[Postcard, AsyncSkipLimit[Postcard]]: + """ + Get a list of postcards. + + Args: + search: You can supply any string to help narrow down the list of resources. For + example, if you pass `"New York"` (quoted), it will return resources that have + that string present somewhere in their response. Alternatively, you can supply a + structured search query. See the documentation on `StructuredSearchQuery` for + more details. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/print-mail/v1/postcards", + page=AsyncSkipLimit[Postcard], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "limit": limit, + "search": search, + "skip": skip, + }, + postcard_list_params.PostcardListParams, + ), + ), + model=Postcard, + ) + + async def delete( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Postcard: + """Cancel a postcard by ID. + + Note that this operation cannot be undone. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._delete( + f"/print-mail/v1/postcards/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Postcard, + ) + + async def retrieve_url( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> PostcardRetrieveURLResponse: + """ + Retrieve a postcard preview URL. + + This is only available for customers with our document management addon, which + offers document generation and hosting capabilities. This endpoint has a much + higher rate limit than the regular order retrieval endpoint, so it is suitable + for customer-facing use-cases. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._get( + f"/print-mail/v1/postcards/{id}/url", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=PostcardRetrieveURLResponse, + ) + + +class PostcardsResourceWithRawResponse: + def __init__(self, postcards: PostcardsResource) -> None: + self._postcards = postcards + + self.create = to_raw_response_wrapper( + postcards.create, + ) + self.retrieve = to_raw_response_wrapper( + postcards.retrieve, + ) + self.list = to_raw_response_wrapper( + postcards.list, + ) + self.delete = to_raw_response_wrapper( + postcards.delete, + ) + self.retrieve_url = to_raw_response_wrapper( + postcards.retrieve_url, + ) + + +class AsyncPostcardsResourceWithRawResponse: + def __init__(self, postcards: AsyncPostcardsResource) -> None: + self._postcards = postcards + + self.create = async_to_raw_response_wrapper( + postcards.create, + ) + self.retrieve = async_to_raw_response_wrapper( + postcards.retrieve, + ) + self.list = async_to_raw_response_wrapper( + postcards.list, + ) + self.delete = async_to_raw_response_wrapper( + postcards.delete, + ) + self.retrieve_url = async_to_raw_response_wrapper( + postcards.retrieve_url, + ) + + +class PostcardsResourceWithStreamingResponse: + def __init__(self, postcards: PostcardsResource) -> None: + self._postcards = postcards + + self.create = to_streamed_response_wrapper( + postcards.create, + ) + self.retrieve = to_streamed_response_wrapper( + postcards.retrieve, + ) + self.list = to_streamed_response_wrapper( + postcards.list, + ) + self.delete = to_streamed_response_wrapper( + postcards.delete, + ) + self.retrieve_url = to_streamed_response_wrapper( + postcards.retrieve_url, + ) + + +class AsyncPostcardsResourceWithStreamingResponse: + def __init__(self, postcards: AsyncPostcardsResource) -> None: + self._postcards = postcards + + self.create = async_to_streamed_response_wrapper( + postcards.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + postcards.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + postcards.list, + ) + self.delete = async_to_streamed_response_wrapper( + postcards.delete, + ) + self.retrieve_url = async_to_streamed_response_wrapper( + postcards.retrieve_url, + ) diff --git a/src/postgrid/resources/print_mail/print_mail.py b/src/postgrid/resources/print_mail/print_mail.py new file mode 100644 index 0000000..9d926fe --- /dev/null +++ b/src/postgrid/resources/print_mail/print_mail.py @@ -0,0 +1,518 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .boxes import ( + BoxesResource, + AsyncBoxesResource, + BoxesResourceWithRawResponse, + AsyncBoxesResourceWithRawResponse, + BoxesResourceWithStreamingResponse, + AsyncBoxesResourceWithStreamingResponse, +) +from .cheques import ( + ChequesResource, + AsyncChequesResource, + ChequesResourceWithRawResponse, + AsyncChequesResourceWithRawResponse, + ChequesResourceWithStreamingResponse, + AsyncChequesResourceWithStreamingResponse, +) +from .letters import ( + LettersResource, + AsyncLettersResource, + LettersResourceWithRawResponse, + AsyncLettersResourceWithRawResponse, + LettersResourceWithStreamingResponse, + AsyncLettersResourceWithStreamingResponse, +) +from .contacts import ( + ContactsResource, + AsyncContactsResource, + ContactsResourceWithRawResponse, + AsyncContactsResourceWithRawResponse, + ContactsResourceWithStreamingResponse, + AsyncContactsResourceWithStreamingResponse, +) +from ..._compat import cached_property +from .campaigns import ( + CampaignsResource, + AsyncCampaignsResource, + CampaignsResourceWithRawResponse, + AsyncCampaignsResourceWithRawResponse, + CampaignsResourceWithStreamingResponse, + AsyncCampaignsResourceWithStreamingResponse, +) +from .postcards import ( + PostcardsResource, + AsyncPostcardsResource, + PostcardsResourceWithRawResponse, + AsyncPostcardsResourceWithRawResponse, + PostcardsResourceWithStreamingResponse, + AsyncPostcardsResourceWithStreamingResponse, +) +from .templates import ( + TemplatesResource, + AsyncTemplatesResource, + TemplatesResourceWithRawResponse, + AsyncTemplatesResourceWithRawResponse, + TemplatesResourceWithStreamingResponse, + AsyncTemplatesResourceWithStreamingResponse, +) +from ..._resource import SyncAPIResource, AsyncAPIResource +from .self_mailers import ( + SelfMailersResource, + AsyncSelfMailersResource, + SelfMailersResourceWithRawResponse, + AsyncSelfMailersResourceWithRawResponse, + SelfMailersResourceWithStreamingResponse, + AsyncSelfMailersResourceWithStreamingResponse, +) +from .bank_accounts import ( + BankAccountsResource, + AsyncBankAccountsResource, + BankAccountsResourceWithRawResponse, + AsyncBankAccountsResourceWithRawResponse, + BankAccountsResourceWithStreamingResponse, + AsyncBankAccountsResourceWithStreamingResponse, +) +from .mailing_lists import ( + MailingListsResource, + AsyncMailingListsResource, + MailingListsResourceWithRawResponse, + AsyncMailingListsResourceWithRawResponse, + MailingListsResourceWithStreamingResponse, + AsyncMailingListsResourceWithStreamingResponse, +) +from .reports.reports import ( + ReportsResource, + AsyncReportsResource, + ReportsResourceWithRawResponse, + AsyncReportsResourceWithRawResponse, + ReportsResourceWithStreamingResponse, + AsyncReportsResourceWithStreamingResponse, +) +from .sub_organizations import ( + SubOrganizationsResource, + AsyncSubOrganizationsResource, + SubOrganizationsResourceWithRawResponse, + AsyncSubOrganizationsResourceWithRawResponse, + SubOrganizationsResourceWithStreamingResponse, + AsyncSubOrganizationsResourceWithStreamingResponse, +) +from .mailing_list_imports import ( + MailingListImportsResource, + AsyncMailingListImportsResource, + MailingListImportsResourceWithRawResponse, + AsyncMailingListImportsResourceWithRawResponse, + MailingListImportsResourceWithStreamingResponse, + AsyncMailingListImportsResourceWithStreamingResponse, +) +from .order_profiles.order_profiles import ( + OrderProfilesResource, + AsyncOrderProfilesResource, + OrderProfilesResourceWithRawResponse, + AsyncOrderProfilesResourceWithRawResponse, + OrderProfilesResourceWithStreamingResponse, + AsyncOrderProfilesResourceWithStreamingResponse, +) + +__all__ = ["PrintMailResource", "AsyncPrintMailResource"] + + +class PrintMailResource(SyncAPIResource): + @cached_property + def bank_accounts(self) -> BankAccountsResource: + return BankAccountsResource(self._client) + + @cached_property + def boxes(self) -> BoxesResource: + return BoxesResource(self._client) + + @cached_property + def campaigns(self) -> CampaignsResource: + return CampaignsResource(self._client) + + @cached_property + def cheques(self) -> ChequesResource: + return ChequesResource(self._client) + + @cached_property + def contacts(self) -> ContactsResource: + return ContactsResource(self._client) + + @cached_property + def letters(self) -> LettersResource: + return LettersResource(self._client) + + @cached_property + def mailing_list_imports(self) -> MailingListImportsResource: + return MailingListImportsResource(self._client) + + @cached_property + def mailing_lists(self) -> MailingListsResource: + return MailingListsResource(self._client) + + @cached_property + def order_profiles(self) -> OrderProfilesResource: + return OrderProfilesResource(self._client) + + @cached_property + def postcards(self) -> PostcardsResource: + return PostcardsResource(self._client) + + @cached_property + def reports(self) -> ReportsResource: + return ReportsResource(self._client) + + @cached_property + def self_mailers(self) -> SelfMailersResource: + return SelfMailersResource(self._client) + + @cached_property + def sub_organizations(self) -> SubOrganizationsResource: + return SubOrganizationsResource(self._client) + + @cached_property + def templates(self) -> TemplatesResource: + return TemplatesResource(self._client) + + @cached_property + def with_raw_response(self) -> PrintMailResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + """ + return PrintMailResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> PrintMailResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + """ + return PrintMailResourceWithStreamingResponse(self) + + +class AsyncPrintMailResource(AsyncAPIResource): + @cached_property + def bank_accounts(self) -> AsyncBankAccountsResource: + return AsyncBankAccountsResource(self._client) + + @cached_property + def boxes(self) -> AsyncBoxesResource: + return AsyncBoxesResource(self._client) + + @cached_property + def campaigns(self) -> AsyncCampaignsResource: + return AsyncCampaignsResource(self._client) + + @cached_property + def cheques(self) -> AsyncChequesResource: + return AsyncChequesResource(self._client) + + @cached_property + def contacts(self) -> AsyncContactsResource: + return AsyncContactsResource(self._client) + + @cached_property + def letters(self) -> AsyncLettersResource: + return AsyncLettersResource(self._client) + + @cached_property + def mailing_list_imports(self) -> AsyncMailingListImportsResource: + return AsyncMailingListImportsResource(self._client) + + @cached_property + def mailing_lists(self) -> AsyncMailingListsResource: + return AsyncMailingListsResource(self._client) + + @cached_property + def order_profiles(self) -> AsyncOrderProfilesResource: + return AsyncOrderProfilesResource(self._client) + + @cached_property + def postcards(self) -> AsyncPostcardsResource: + return AsyncPostcardsResource(self._client) + + @cached_property + def reports(self) -> AsyncReportsResource: + return AsyncReportsResource(self._client) + + @cached_property + def self_mailers(self) -> AsyncSelfMailersResource: + return AsyncSelfMailersResource(self._client) + + @cached_property + def sub_organizations(self) -> AsyncSubOrganizationsResource: + return AsyncSubOrganizationsResource(self._client) + + @cached_property + def templates(self) -> AsyncTemplatesResource: + return AsyncTemplatesResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncPrintMailResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + """ + return AsyncPrintMailResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncPrintMailResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + """ + return AsyncPrintMailResourceWithStreamingResponse(self) + + +class PrintMailResourceWithRawResponse: + def __init__(self, print_mail: PrintMailResource) -> None: + self._print_mail = print_mail + + @cached_property + def bank_accounts(self) -> BankAccountsResourceWithRawResponse: + return BankAccountsResourceWithRawResponse(self._print_mail.bank_accounts) + + @cached_property + def boxes(self) -> BoxesResourceWithRawResponse: + return BoxesResourceWithRawResponse(self._print_mail.boxes) + + @cached_property + def campaigns(self) -> CampaignsResourceWithRawResponse: + return CampaignsResourceWithRawResponse(self._print_mail.campaigns) + + @cached_property + def cheques(self) -> ChequesResourceWithRawResponse: + return ChequesResourceWithRawResponse(self._print_mail.cheques) + + @cached_property + def contacts(self) -> ContactsResourceWithRawResponse: + return ContactsResourceWithRawResponse(self._print_mail.contacts) + + @cached_property + def letters(self) -> LettersResourceWithRawResponse: + return LettersResourceWithRawResponse(self._print_mail.letters) + + @cached_property + def mailing_list_imports(self) -> MailingListImportsResourceWithRawResponse: + return MailingListImportsResourceWithRawResponse(self._print_mail.mailing_list_imports) + + @cached_property + def mailing_lists(self) -> MailingListsResourceWithRawResponse: + return MailingListsResourceWithRawResponse(self._print_mail.mailing_lists) + + @cached_property + def order_profiles(self) -> OrderProfilesResourceWithRawResponse: + return OrderProfilesResourceWithRawResponse(self._print_mail.order_profiles) + + @cached_property + def postcards(self) -> PostcardsResourceWithRawResponse: + return PostcardsResourceWithRawResponse(self._print_mail.postcards) + + @cached_property + def reports(self) -> ReportsResourceWithRawResponse: + return ReportsResourceWithRawResponse(self._print_mail.reports) + + @cached_property + def self_mailers(self) -> SelfMailersResourceWithRawResponse: + return SelfMailersResourceWithRawResponse(self._print_mail.self_mailers) + + @cached_property + def sub_organizations(self) -> SubOrganizationsResourceWithRawResponse: + return SubOrganizationsResourceWithRawResponse(self._print_mail.sub_organizations) + + @cached_property + def templates(self) -> TemplatesResourceWithRawResponse: + return TemplatesResourceWithRawResponse(self._print_mail.templates) + + +class AsyncPrintMailResourceWithRawResponse: + def __init__(self, print_mail: AsyncPrintMailResource) -> None: + self._print_mail = print_mail + + @cached_property + def bank_accounts(self) -> AsyncBankAccountsResourceWithRawResponse: + return AsyncBankAccountsResourceWithRawResponse(self._print_mail.bank_accounts) + + @cached_property + def boxes(self) -> AsyncBoxesResourceWithRawResponse: + return AsyncBoxesResourceWithRawResponse(self._print_mail.boxes) + + @cached_property + def campaigns(self) -> AsyncCampaignsResourceWithRawResponse: + return AsyncCampaignsResourceWithRawResponse(self._print_mail.campaigns) + + @cached_property + def cheques(self) -> AsyncChequesResourceWithRawResponse: + return AsyncChequesResourceWithRawResponse(self._print_mail.cheques) + + @cached_property + def contacts(self) -> AsyncContactsResourceWithRawResponse: + return AsyncContactsResourceWithRawResponse(self._print_mail.contacts) + + @cached_property + def letters(self) -> AsyncLettersResourceWithRawResponse: + return AsyncLettersResourceWithRawResponse(self._print_mail.letters) + + @cached_property + def mailing_list_imports(self) -> AsyncMailingListImportsResourceWithRawResponse: + return AsyncMailingListImportsResourceWithRawResponse(self._print_mail.mailing_list_imports) + + @cached_property + def mailing_lists(self) -> AsyncMailingListsResourceWithRawResponse: + return AsyncMailingListsResourceWithRawResponse(self._print_mail.mailing_lists) + + @cached_property + def order_profiles(self) -> AsyncOrderProfilesResourceWithRawResponse: + return AsyncOrderProfilesResourceWithRawResponse(self._print_mail.order_profiles) + + @cached_property + def postcards(self) -> AsyncPostcardsResourceWithRawResponse: + return AsyncPostcardsResourceWithRawResponse(self._print_mail.postcards) + + @cached_property + def reports(self) -> AsyncReportsResourceWithRawResponse: + return AsyncReportsResourceWithRawResponse(self._print_mail.reports) + + @cached_property + def self_mailers(self) -> AsyncSelfMailersResourceWithRawResponse: + return AsyncSelfMailersResourceWithRawResponse(self._print_mail.self_mailers) + + @cached_property + def sub_organizations(self) -> AsyncSubOrganizationsResourceWithRawResponse: + return AsyncSubOrganizationsResourceWithRawResponse(self._print_mail.sub_organizations) + + @cached_property + def templates(self) -> AsyncTemplatesResourceWithRawResponse: + return AsyncTemplatesResourceWithRawResponse(self._print_mail.templates) + + +class PrintMailResourceWithStreamingResponse: + def __init__(self, print_mail: PrintMailResource) -> None: + self._print_mail = print_mail + + @cached_property + def bank_accounts(self) -> BankAccountsResourceWithStreamingResponse: + return BankAccountsResourceWithStreamingResponse(self._print_mail.bank_accounts) + + @cached_property + def boxes(self) -> BoxesResourceWithStreamingResponse: + return BoxesResourceWithStreamingResponse(self._print_mail.boxes) + + @cached_property + def campaigns(self) -> CampaignsResourceWithStreamingResponse: + return CampaignsResourceWithStreamingResponse(self._print_mail.campaigns) + + @cached_property + def cheques(self) -> ChequesResourceWithStreamingResponse: + return ChequesResourceWithStreamingResponse(self._print_mail.cheques) + + @cached_property + def contacts(self) -> ContactsResourceWithStreamingResponse: + return ContactsResourceWithStreamingResponse(self._print_mail.contacts) + + @cached_property + def letters(self) -> LettersResourceWithStreamingResponse: + return LettersResourceWithStreamingResponse(self._print_mail.letters) + + @cached_property + def mailing_list_imports(self) -> MailingListImportsResourceWithStreamingResponse: + return MailingListImportsResourceWithStreamingResponse(self._print_mail.mailing_list_imports) + + @cached_property + def mailing_lists(self) -> MailingListsResourceWithStreamingResponse: + return MailingListsResourceWithStreamingResponse(self._print_mail.mailing_lists) + + @cached_property + def order_profiles(self) -> OrderProfilesResourceWithStreamingResponse: + return OrderProfilesResourceWithStreamingResponse(self._print_mail.order_profiles) + + @cached_property + def postcards(self) -> PostcardsResourceWithStreamingResponse: + return PostcardsResourceWithStreamingResponse(self._print_mail.postcards) + + @cached_property + def reports(self) -> ReportsResourceWithStreamingResponse: + return ReportsResourceWithStreamingResponse(self._print_mail.reports) + + @cached_property + def self_mailers(self) -> SelfMailersResourceWithStreamingResponse: + return SelfMailersResourceWithStreamingResponse(self._print_mail.self_mailers) + + @cached_property + def sub_organizations(self) -> SubOrganizationsResourceWithStreamingResponse: + return SubOrganizationsResourceWithStreamingResponse(self._print_mail.sub_organizations) + + @cached_property + def templates(self) -> TemplatesResourceWithStreamingResponse: + return TemplatesResourceWithStreamingResponse(self._print_mail.templates) + + +class AsyncPrintMailResourceWithStreamingResponse: + def __init__(self, print_mail: AsyncPrintMailResource) -> None: + self._print_mail = print_mail + + @cached_property + def bank_accounts(self) -> AsyncBankAccountsResourceWithStreamingResponse: + return AsyncBankAccountsResourceWithStreamingResponse(self._print_mail.bank_accounts) + + @cached_property + def boxes(self) -> AsyncBoxesResourceWithStreamingResponse: + return AsyncBoxesResourceWithStreamingResponse(self._print_mail.boxes) + + @cached_property + def campaigns(self) -> AsyncCampaignsResourceWithStreamingResponse: + return AsyncCampaignsResourceWithStreamingResponse(self._print_mail.campaigns) + + @cached_property + def cheques(self) -> AsyncChequesResourceWithStreamingResponse: + return AsyncChequesResourceWithStreamingResponse(self._print_mail.cheques) + + @cached_property + def contacts(self) -> AsyncContactsResourceWithStreamingResponse: + return AsyncContactsResourceWithStreamingResponse(self._print_mail.contacts) + + @cached_property + def letters(self) -> AsyncLettersResourceWithStreamingResponse: + return AsyncLettersResourceWithStreamingResponse(self._print_mail.letters) + + @cached_property + def mailing_list_imports(self) -> AsyncMailingListImportsResourceWithStreamingResponse: + return AsyncMailingListImportsResourceWithStreamingResponse(self._print_mail.mailing_list_imports) + + @cached_property + def mailing_lists(self) -> AsyncMailingListsResourceWithStreamingResponse: + return AsyncMailingListsResourceWithStreamingResponse(self._print_mail.mailing_lists) + + @cached_property + def order_profiles(self) -> AsyncOrderProfilesResourceWithStreamingResponse: + return AsyncOrderProfilesResourceWithStreamingResponse(self._print_mail.order_profiles) + + @cached_property + def postcards(self) -> AsyncPostcardsResourceWithStreamingResponse: + return AsyncPostcardsResourceWithStreamingResponse(self._print_mail.postcards) + + @cached_property + def reports(self) -> AsyncReportsResourceWithStreamingResponse: + return AsyncReportsResourceWithStreamingResponse(self._print_mail.reports) + + @cached_property + def self_mailers(self) -> AsyncSelfMailersResourceWithStreamingResponse: + return AsyncSelfMailersResourceWithStreamingResponse(self._print_mail.self_mailers) + + @cached_property + def sub_organizations(self) -> AsyncSubOrganizationsResourceWithStreamingResponse: + return AsyncSubOrganizationsResourceWithStreamingResponse(self._print_mail.sub_organizations) + + @cached_property + def templates(self) -> AsyncTemplatesResourceWithStreamingResponse: + return AsyncTemplatesResourceWithStreamingResponse(self._print_mail.templates) diff --git a/src/postgrid/resources/print_mail/reports/__init__.py b/src/postgrid/resources/print_mail/reports/__init__.py new file mode 100644 index 0000000..07b3076 --- /dev/null +++ b/src/postgrid/resources/print_mail/reports/__init__.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .exports import ( + ExportsResource, + AsyncExportsResource, + ExportsResourceWithRawResponse, + AsyncExportsResourceWithRawResponse, + ExportsResourceWithStreamingResponse, + AsyncExportsResourceWithStreamingResponse, +) +from .reports import ( + ReportsResource, + AsyncReportsResource, + ReportsResourceWithRawResponse, + AsyncReportsResourceWithRawResponse, + ReportsResourceWithStreamingResponse, + AsyncReportsResourceWithStreamingResponse, +) +from .samples import ( + SamplesResource, + AsyncSamplesResource, + SamplesResourceWithRawResponse, + AsyncSamplesResourceWithRawResponse, + SamplesResourceWithStreamingResponse, + AsyncSamplesResourceWithStreamingResponse, +) + +__all__ = [ + "SamplesResource", + "AsyncSamplesResource", + "SamplesResourceWithRawResponse", + "AsyncSamplesResourceWithRawResponse", + "SamplesResourceWithStreamingResponse", + "AsyncSamplesResourceWithStreamingResponse", + "ExportsResource", + "AsyncExportsResource", + "ExportsResourceWithRawResponse", + "AsyncExportsResourceWithRawResponse", + "ExportsResourceWithStreamingResponse", + "AsyncExportsResourceWithStreamingResponse", + "ReportsResource", + "AsyncReportsResource", + "ReportsResourceWithRawResponse", + "AsyncReportsResourceWithRawResponse", + "ReportsResourceWithStreamingResponse", + "AsyncReportsResourceWithStreamingResponse", +] diff --git a/src/postgrid/resources/print_mail/reports/exports.py b/src/postgrid/resources/print_mail/reports/exports.py new file mode 100644 index 0000000..5e781fb --- /dev/null +++ b/src/postgrid/resources/print_mail/reports/exports.py @@ -0,0 +1,389 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, List + +import httpx + +from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ...._utils import ( + maybe_transform, + async_maybe_transform, +) +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...._base_client import make_request_options +from ....types.print_mail.reports import export_create_params +from ....types.print_mail.deleted_response import DeletedResponse +from ....types.print_mail.reports.report_export import ReportExport + +__all__ = ["ExportsResource", "AsyncExportsResource"] + + +class ExportsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ExportsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + """ + return ExportsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ExportsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + """ + return ExportsResourceWithStreamingResponse(self) + + def create( + self, + report_id: str, + *, + description: str | NotGiven = NOT_GIVEN, + metadata: Dict[str, str] | NotGiven = NOT_GIVEN, + params: List[str] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ReportExport: + """Create a new export job for a saved report. + + This runs the report's query + asynchronously and generates a downloadable CSV file with the full results. Your + queries can run for up to 13 minutes the resulting file can be up to 100mb + large. + + Args: + description: An optional user-friendly description for the export. + + metadata: Optional key-value metadata associated with the export. + + params: Optional parameters to bind to the SQL query of the associated report. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not report_id: + raise ValueError(f"Expected a non-empty value for `report_id` but received {report_id!r}") + return self._post( + f"/print-mail/v1/reports/{report_id}/exports", + body=maybe_transform( + { + "description": description, + "metadata": metadata, + "params": params, + }, + export_create_params.ExportCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ReportExport, + ) + + def retrieve( + self, + export_id: str, + *, + report_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ReportExport: + """Retrieve the status and details of a specific report export job. + + Check the + `outputURL` property for the download link once generation is complete. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not report_id: + raise ValueError(f"Expected a non-empty value for `report_id` but received {report_id!r}") + if not export_id: + raise ValueError(f"Expected a non-empty value for `export_id` but received {export_id!r}") + return self._get( + f"/print-mail/v1/reports/{report_id}/exports/{export_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ReportExport, + ) + + def delete( + self, + export_id: str, + *, + report_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> DeletedResponse: + """ + Delete a completed or failed report export job and its associated output file + (if any). This action cannot be undone. You cannot delete an export that is + still generating. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not report_id: + raise ValueError(f"Expected a non-empty value for `report_id` but received {report_id!r}") + if not export_id: + raise ValueError(f"Expected a non-empty value for `export_id` but received {export_id!r}") + return self._delete( + f"/print-mail/v1/reports/{report_id}/exports/{export_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DeletedResponse, + ) + + +class AsyncExportsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncExportsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + """ + return AsyncExportsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncExportsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + """ + return AsyncExportsResourceWithStreamingResponse(self) + + async def create( + self, + report_id: str, + *, + description: str | NotGiven = NOT_GIVEN, + metadata: Dict[str, str] | NotGiven = NOT_GIVEN, + params: List[str] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ReportExport: + """Create a new export job for a saved report. + + This runs the report's query + asynchronously and generates a downloadable CSV file with the full results. Your + queries can run for up to 13 minutes the resulting file can be up to 100mb + large. + + Args: + description: An optional user-friendly description for the export. + + metadata: Optional key-value metadata associated with the export. + + params: Optional parameters to bind to the SQL query of the associated report. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not report_id: + raise ValueError(f"Expected a non-empty value for `report_id` but received {report_id!r}") + return await self._post( + f"/print-mail/v1/reports/{report_id}/exports", + body=await async_maybe_transform( + { + "description": description, + "metadata": metadata, + "params": params, + }, + export_create_params.ExportCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ReportExport, + ) + + async def retrieve( + self, + export_id: str, + *, + report_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ReportExport: + """Retrieve the status and details of a specific report export job. + + Check the + `outputURL` property for the download link once generation is complete. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not report_id: + raise ValueError(f"Expected a non-empty value for `report_id` but received {report_id!r}") + if not export_id: + raise ValueError(f"Expected a non-empty value for `export_id` but received {export_id!r}") + return await self._get( + f"/print-mail/v1/reports/{report_id}/exports/{export_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ReportExport, + ) + + async def delete( + self, + export_id: str, + *, + report_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> DeletedResponse: + """ + Delete a completed or failed report export job and its associated output file + (if any). This action cannot be undone. You cannot delete an export that is + still generating. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not report_id: + raise ValueError(f"Expected a non-empty value for `report_id` but received {report_id!r}") + if not export_id: + raise ValueError(f"Expected a non-empty value for `export_id` but received {export_id!r}") + return await self._delete( + f"/print-mail/v1/reports/{report_id}/exports/{export_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DeletedResponse, + ) + + +class ExportsResourceWithRawResponse: + def __init__(self, exports: ExportsResource) -> None: + self._exports = exports + + self.create = to_raw_response_wrapper( + exports.create, + ) + self.retrieve = to_raw_response_wrapper( + exports.retrieve, + ) + self.delete = to_raw_response_wrapper( + exports.delete, + ) + + +class AsyncExportsResourceWithRawResponse: + def __init__(self, exports: AsyncExportsResource) -> None: + self._exports = exports + + self.create = async_to_raw_response_wrapper( + exports.create, + ) + self.retrieve = async_to_raw_response_wrapper( + exports.retrieve, + ) + self.delete = async_to_raw_response_wrapper( + exports.delete, + ) + + +class ExportsResourceWithStreamingResponse: + def __init__(self, exports: ExportsResource) -> None: + self._exports = exports + + self.create = to_streamed_response_wrapper( + exports.create, + ) + self.retrieve = to_streamed_response_wrapper( + exports.retrieve, + ) + self.delete = to_streamed_response_wrapper( + exports.delete, + ) + + +class AsyncExportsResourceWithStreamingResponse: + def __init__(self, exports: AsyncExportsResource) -> None: + self._exports = exports + + self.create = async_to_streamed_response_wrapper( + exports.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + exports.retrieve, + ) + self.delete = async_to_streamed_response_wrapper( + exports.delete, + ) diff --git a/src/postgrid/resources/print_mail/reports/reports.py b/src/postgrid/resources/print_mail/reports/reports.py new file mode 100644 index 0000000..16a5270 --- /dev/null +++ b/src/postgrid/resources/print_mail/reports/reports.py @@ -0,0 +1,777 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, List, Optional + +import httpx + +from .exports import ( + ExportsResource, + AsyncExportsResource, + ExportsResourceWithRawResponse, + AsyncExportsResourceWithRawResponse, + ExportsResourceWithStreamingResponse, + AsyncExportsResourceWithStreamingResponse, +) +from .samples import ( + SamplesResource, + AsyncSamplesResource, + SamplesResourceWithRawResponse, + AsyncSamplesResourceWithRawResponse, + SamplesResourceWithStreamingResponse, + AsyncSamplesResourceWithStreamingResponse, +) +from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ...._utils import ( + maybe_transform, + async_maybe_transform, +) +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ....pagination import SyncSkipLimit, AsyncSkipLimit +from ...._base_client import AsyncPaginator, make_request_options +from ....types.print_mail import report_list_params, report_create_params, report_sample_params, report_update_params +from ....types.print_mail.report import Report +from ....types.print_mail.deleted_response import DeletedResponse +from ....types.print_mail.reports.report_sample import ReportSample + +__all__ = ["ReportsResource", "AsyncReportsResource"] + + +class ReportsResource(SyncAPIResource): + @cached_property + def samples(self) -> SamplesResource: + return SamplesResource(self._client) + + @cached_property + def exports(self) -> ExportsResource: + return ExportsResource(self._client) + + @cached_property + def with_raw_response(self) -> ReportsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + """ + return ReportsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ReportsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + """ + return ReportsResourceWithStreamingResponse(self) + + def create( + self, + *, + sql_query: str, + description: str | NotGiven = NOT_GIVEN, + metadata: Dict[str, str] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Report: + """Create a new saved report definition. + + Saved reports are SQL queries that can be + executed later to generate full exports or samples. + + If you just want to do ad-hoc queries, you should use the `/reports/samples` + endpoint. + + Args: + sql_query: The SQL query defining the report. + + description: An optional user-friendly description for the report. + + metadata: Optional key-value metadata associated with the report. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/print-mail/v1/reports", + body=maybe_transform( + { + "sql_query": sql_query, + "description": description, + "metadata": metadata, + }, + report_create_params.ReportCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Report, + ) + + def retrieve( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Report: + """ + Retrieve the details of a specific saved report by its ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._get( + f"/print-mail/v1/reports/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Report, + ) + + def update( + self, + id: str, + *, + description: Optional[str] | NotGiven = NOT_GIVEN, + metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, + sql_query: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Report: + """Update an existing saved report definition. + + You can change the query, + description, or metadata. + + Args: + description: An optional user-friendly description for the report. Set to null to remove. + + metadata: Optional key-value metadata associated with the report. Set to null to remove. + + sql_query: The SQL query defining the report. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._post( + f"/print-mail/v1/reports/{id}", + body=maybe_transform( + { + "description": description, + "metadata": metadata, + "sql_query": sql_query, + }, + report_update_params.ReportUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Report, + ) + + def list( + self, + *, + limit: int | NotGiven = NOT_GIVEN, + search: str | NotGiven = NOT_GIVEN, + skip: int | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SyncSkipLimit[Report]: + """ + Retrieve a list of saved reports for your organization. + + Args: + search: You can supply any string to help narrow down the list of resources. For + example, if you pass `"New York"` (quoted), it will return resources that have + that string present somewhere in their response. Alternatively, you can supply a + structured search query. See the documentation on `StructuredSearchQuery` for + more details. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/print-mail/v1/reports", + page=SyncSkipLimit[Report], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "limit": limit, + "search": search, + "skip": skip, + }, + report_list_params.ReportListParams, + ), + ), + model=Report, + ) + + def delete( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> DeletedResponse: + """Delete a saved report definition. + + This action cannot be undone. Associated + exports are not automatically deleted. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._delete( + f"/print-mail/v1/reports/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DeletedResponse, + ) + + def sample( + self, + *, + sql_query: str, + limit: int | NotGiven = NOT_GIVEN, + params: List[str] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ReportSample: + """ + Run an ad-hoc SQL query against your data lake and get a sample of the results. + This is useful for quickly testing queries without saving them as a report. The + query execution time and result size are limited. + + Args: + sql_query: The SQL query to execute for the sample. + + limit: Maximum number of rows to return in the sample. + + params: Optional parameters to bind to the SQL query (e.g., for placeholders like ? or + $1). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/print-mail/v1/reports/samples", + body=maybe_transform( + { + "sql_query": sql_query, + "limit": limit, + "params": params, + }, + report_sample_params.ReportSampleParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ReportSample, + ) + + +class AsyncReportsResource(AsyncAPIResource): + @cached_property + def samples(self) -> AsyncSamplesResource: + return AsyncSamplesResource(self._client) + + @cached_property + def exports(self) -> AsyncExportsResource: + return AsyncExportsResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncReportsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + """ + return AsyncReportsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncReportsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + """ + return AsyncReportsResourceWithStreamingResponse(self) + + async def create( + self, + *, + sql_query: str, + description: str | NotGiven = NOT_GIVEN, + metadata: Dict[str, str] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Report: + """Create a new saved report definition. + + Saved reports are SQL queries that can be + executed later to generate full exports or samples. + + If you just want to do ad-hoc queries, you should use the `/reports/samples` + endpoint. + + Args: + sql_query: The SQL query defining the report. + + description: An optional user-friendly description for the report. + + metadata: Optional key-value metadata associated with the report. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/print-mail/v1/reports", + body=await async_maybe_transform( + { + "sql_query": sql_query, + "description": description, + "metadata": metadata, + }, + report_create_params.ReportCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Report, + ) + + async def retrieve( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Report: + """ + Retrieve the details of a specific saved report by its ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._get( + f"/print-mail/v1/reports/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Report, + ) + + async def update( + self, + id: str, + *, + description: Optional[str] | NotGiven = NOT_GIVEN, + metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, + sql_query: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Report: + """Update an existing saved report definition. + + You can change the query, + description, or metadata. + + Args: + description: An optional user-friendly description for the report. Set to null to remove. + + metadata: Optional key-value metadata associated with the report. Set to null to remove. + + sql_query: The SQL query defining the report. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._post( + f"/print-mail/v1/reports/{id}", + body=await async_maybe_transform( + { + "description": description, + "metadata": metadata, + "sql_query": sql_query, + }, + report_update_params.ReportUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Report, + ) + + def list( + self, + *, + limit: int | NotGiven = NOT_GIVEN, + search: str | NotGiven = NOT_GIVEN, + skip: int | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncPaginator[Report, AsyncSkipLimit[Report]]: + """ + Retrieve a list of saved reports for your organization. + + Args: + search: You can supply any string to help narrow down the list of resources. For + example, if you pass `"New York"` (quoted), it will return resources that have + that string present somewhere in their response. Alternatively, you can supply a + structured search query. See the documentation on `StructuredSearchQuery` for + more details. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/print-mail/v1/reports", + page=AsyncSkipLimit[Report], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "limit": limit, + "search": search, + "skip": skip, + }, + report_list_params.ReportListParams, + ), + ), + model=Report, + ) + + async def delete( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> DeletedResponse: + """Delete a saved report definition. + + This action cannot be undone. Associated + exports are not automatically deleted. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._delete( + f"/print-mail/v1/reports/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DeletedResponse, + ) + + async def sample( + self, + *, + sql_query: str, + limit: int | NotGiven = NOT_GIVEN, + params: List[str] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ReportSample: + """ + Run an ad-hoc SQL query against your data lake and get a sample of the results. + This is useful for quickly testing queries without saving them as a report. The + query execution time and result size are limited. + + Args: + sql_query: The SQL query to execute for the sample. + + limit: Maximum number of rows to return in the sample. + + params: Optional parameters to bind to the SQL query (e.g., for placeholders like ? or + $1). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/print-mail/v1/reports/samples", + body=await async_maybe_transform( + { + "sql_query": sql_query, + "limit": limit, + "params": params, + }, + report_sample_params.ReportSampleParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ReportSample, + ) + + +class ReportsResourceWithRawResponse: + def __init__(self, reports: ReportsResource) -> None: + self._reports = reports + + self.create = to_raw_response_wrapper( + reports.create, + ) + self.retrieve = to_raw_response_wrapper( + reports.retrieve, + ) + self.update = to_raw_response_wrapper( + reports.update, + ) + self.list = to_raw_response_wrapper( + reports.list, + ) + self.delete = to_raw_response_wrapper( + reports.delete, + ) + self.sample = to_raw_response_wrapper( + reports.sample, + ) + + @cached_property + def samples(self) -> SamplesResourceWithRawResponse: + return SamplesResourceWithRawResponse(self._reports.samples) + + @cached_property + def exports(self) -> ExportsResourceWithRawResponse: + return ExportsResourceWithRawResponse(self._reports.exports) + + +class AsyncReportsResourceWithRawResponse: + def __init__(self, reports: AsyncReportsResource) -> None: + self._reports = reports + + self.create = async_to_raw_response_wrapper( + reports.create, + ) + self.retrieve = async_to_raw_response_wrapper( + reports.retrieve, + ) + self.update = async_to_raw_response_wrapper( + reports.update, + ) + self.list = async_to_raw_response_wrapper( + reports.list, + ) + self.delete = async_to_raw_response_wrapper( + reports.delete, + ) + self.sample = async_to_raw_response_wrapper( + reports.sample, + ) + + @cached_property + def samples(self) -> AsyncSamplesResourceWithRawResponse: + return AsyncSamplesResourceWithRawResponse(self._reports.samples) + + @cached_property + def exports(self) -> AsyncExportsResourceWithRawResponse: + return AsyncExportsResourceWithRawResponse(self._reports.exports) + + +class ReportsResourceWithStreamingResponse: + def __init__(self, reports: ReportsResource) -> None: + self._reports = reports + + self.create = to_streamed_response_wrapper( + reports.create, + ) + self.retrieve = to_streamed_response_wrapper( + reports.retrieve, + ) + self.update = to_streamed_response_wrapper( + reports.update, + ) + self.list = to_streamed_response_wrapper( + reports.list, + ) + self.delete = to_streamed_response_wrapper( + reports.delete, + ) + self.sample = to_streamed_response_wrapper( + reports.sample, + ) + + @cached_property + def samples(self) -> SamplesResourceWithStreamingResponse: + return SamplesResourceWithStreamingResponse(self._reports.samples) + + @cached_property + def exports(self) -> ExportsResourceWithStreamingResponse: + return ExportsResourceWithStreamingResponse(self._reports.exports) + + +class AsyncReportsResourceWithStreamingResponse: + def __init__(self, reports: AsyncReportsResource) -> None: + self._reports = reports + + self.create = async_to_streamed_response_wrapper( + reports.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + reports.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + reports.update, + ) + self.list = async_to_streamed_response_wrapper( + reports.list, + ) + self.delete = async_to_streamed_response_wrapper( + reports.delete, + ) + self.sample = async_to_streamed_response_wrapper( + reports.sample, + ) + + @cached_property + def samples(self) -> AsyncSamplesResourceWithStreamingResponse: + return AsyncSamplesResourceWithStreamingResponse(self._reports.samples) + + @cached_property + def exports(self) -> AsyncExportsResourceWithStreamingResponse: + return AsyncExportsResourceWithStreamingResponse(self._reports.exports) diff --git a/src/postgrid/resources/print_mail/reports/samples.py b/src/postgrid/resources/print_mail/reports/samples.py new file mode 100644 index 0000000..6d8bf8a --- /dev/null +++ b/src/postgrid/resources/print_mail/reports/samples.py @@ -0,0 +1,204 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List + +import httpx + +from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ...._utils import ( + maybe_transform, + async_maybe_transform, +) +from ...._compat import cached_property +from ...._resource import SyncAPIResource, AsyncAPIResource +from ...._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...._base_client import make_request_options +from ....types.print_mail.reports import sample_create_params +from ....types.print_mail.reports.report_sample import ReportSample + +__all__ = ["SamplesResource", "AsyncSamplesResource"] + + +class SamplesResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> SamplesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + """ + return SamplesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> SamplesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + """ + return SamplesResourceWithStreamingResponse(self) + + def create( + self, + id: str, + *, + limit: int | NotGiven = NOT_GIVEN, + params: List[str] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ReportSample: + """ + Run the query associated with a saved report and get a sample of the results. + This allows getting up to 1000 rows of resutls but the runtime of the query is + limited to 30 seconds. If you need more rows or longer runtime, you should + create an export from this report. + + Args: + limit: Maximum number of rows to return in the sample. + + params: Optional parameters to bind to the SQL query (e.g., for placeholders like ? or + $1). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._post( + f"/print-mail/v1/reports/{id}/samples", + body=maybe_transform( + { + "limit": limit, + "params": params, + }, + sample_create_params.SampleCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ReportSample, + ) + + +class AsyncSamplesResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncSamplesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + """ + return AsyncSamplesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncSamplesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + """ + return AsyncSamplesResourceWithStreamingResponse(self) + + async def create( + self, + id: str, + *, + limit: int | NotGiven = NOT_GIVEN, + params: List[str] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> ReportSample: + """ + Run the query associated with a saved report and get a sample of the results. + This allows getting up to 1000 rows of resutls but the runtime of the query is + limited to 30 seconds. If you need more rows or longer runtime, you should + create an export from this report. + + Args: + limit: Maximum number of rows to return in the sample. + + params: Optional parameters to bind to the SQL query (e.g., for placeholders like ? or + $1). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._post( + f"/print-mail/v1/reports/{id}/samples", + body=await async_maybe_transform( + { + "limit": limit, + "params": params, + }, + sample_create_params.SampleCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ReportSample, + ) + + +class SamplesResourceWithRawResponse: + def __init__(self, samples: SamplesResource) -> None: + self._samples = samples + + self.create = to_raw_response_wrapper( + samples.create, + ) + + +class AsyncSamplesResourceWithRawResponse: + def __init__(self, samples: AsyncSamplesResource) -> None: + self._samples = samples + + self.create = async_to_raw_response_wrapper( + samples.create, + ) + + +class SamplesResourceWithStreamingResponse: + def __init__(self, samples: SamplesResource) -> None: + self._samples = samples + + self.create = to_streamed_response_wrapper( + samples.create, + ) + + +class AsyncSamplesResourceWithStreamingResponse: + def __init__(self, samples: AsyncSamplesResource) -> None: + self._samples = samples + + self.create = async_to_streamed_response_wrapper( + samples.create, + ) diff --git a/src/postgrid/resources/print_mail/self_mailers.py b/src/postgrid/resources/print_mail/self_mailers.py new file mode 100644 index 0000000..94ce717 --- /dev/null +++ b/src/postgrid/resources/print_mail/self_mailers.py @@ -0,0 +1,1097 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union +from datetime import datetime +from typing_extensions import overload + +import httpx + +from ..._types import ( + NOT_GIVEN, + Body, + Query, + Headers, + NotGiven, + Base64FileInput, +) +from ..._utils import ( + required_args, + maybe_transform, + async_maybe_transform, +) +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...pagination import SyncSkipLimit, AsyncSkipLimit +from ..._base_client import AsyncPaginator, make_request_options +from ...types.print_mail import OrderMailingClass, self_mailer_list_params, self_mailer_create_params +from ...types.print_mail.self_mailer import SelfMailer +from ...types.print_mail.order_profiles import SelfMailerSize +from ...types.print_mail.order_mailing_class import OrderMailingClass +from ...types.print_mail.order_profiles.self_mailer_size import SelfMailerSize +from ...types.print_mail.self_mailer_retrieve_url_response import SelfMailerRetrieveURLResponse + +__all__ = ["SelfMailersResource", "AsyncSelfMailersResource"] + + +class SelfMailersResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> SelfMailersResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + """ + return SelfMailersResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> SelfMailersResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + """ + return SelfMailersResourceWithStreamingResponse(self) + + @overload + def create( + self, + *, + from_: self_mailer_create_params.SelfMailerCreateWithHTMLFrom, + inside_html: str, + outside_html: str, + size: SelfMailerSize, + to: self_mailer_create_params.SelfMailerCreateWithHTMLTo, + description: str | NotGiven = NOT_GIVEN, + mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, + merge_variables: Dict[str, object] | NotGiven = NOT_GIVEN, + metadata: Dict[str, object] | NotGiven = NOT_GIVEN, + send_date: Union[str, datetime] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SelfMailer: + """Create a self-mailer. + + Note that you can supply one of the following: + + - HTML content for the inside and outside of the self-mailer + - A template ID for the inside and outside of the self-mailer + - A URL or file for a 2 page PDF where the first page is the outside of the + self-mailer and the second page is the inside + - Upload the aforementioned PDF file via a multipart form upload request + + Args: + from_: The contact information of the sender. You can pass contact information inline + here just like you can for the `to`. + + inside_html: The HTML content for the inside of the self-mailer. You can supply _either_ this + or `insideTemplate` but not both. + + outside_html: The HTML content for the outside of the self-mailer. You can supply _either_ + this or `outsideTemplate` but not both. + + size: Enum representing the supported self-mailer sizes. + + to: The recipient of this order. You can either supply the contact information + inline here or provide a contact ID. PostGrid will automatically deduplicate + contacts regardless of whether you provide the information inline here or call + the contact creation endpoint. + + description: An optional string describing this resource. Will be visible in the API and the + dashboard. + + mailing_class: The mailing class of this order. If not provided, automatically set to + `first_class`. + + merge_variables: These will be merged with the variables in the template or HTML you create this + order with. The keys in this object should match the variable names in the + template _exactly_ as they are case-sensitive. Note that these _do not_ apply to + PDFs uploaded with the order. + + metadata: See the section on Metadata. + + send_date: This order will transition from `ready` to `printing` on the day after this + date. You can use this parameter to schedule orders for a future date. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def create( + self, + *, + inside_template: str, + outside_template: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SelfMailer: + """Create a self-mailer. + + Note that you can supply one of the following: + + - HTML content for the inside and outside of the self-mailer + - A template ID for the inside and outside of the self-mailer + - A URL or file for a 2 page PDF where the first page is the outside of the + self-mailer and the second page is the inside + - Upload the aforementioned PDF file via a multipart form upload request + + Args: + inside_template: The template ID for the inside of the self-mailer. You can supply _either_ this + or `insideHTML` but not both. + + outside_template: The template ID for the outside of the self-mailer. You can supply _either_ this + or `outsideHTML` but not both. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def create( + self, + *, + from_: self_mailer_create_params.SelfMailerCreateWithPdfurlFrom, + pdf: str, + size: SelfMailerSize, + to: self_mailer_create_params.SelfMailerCreateWithPdfurlTo, + description: str | NotGiven = NOT_GIVEN, + mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, + merge_variables: Dict[str, object] | NotGiven = NOT_GIVEN, + metadata: Dict[str, object] | NotGiven = NOT_GIVEN, + send_date: Union[str, datetime] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SelfMailer: + """Create a self-mailer. + + Note that you can supply one of the following: + + - HTML content for the inside and outside of the self-mailer + - A template ID for the inside and outside of the self-mailer + - A URL or file for a 2 page PDF where the first page is the outside of the + self-mailer and the second page is the inside + - Upload the aforementioned PDF file via a multipart form upload request + + Args: + from_: The contact information of the sender. You can pass contact information inline + here just like you can for the `to`. + + pdf: A URL pointing to a 2 page PDF file. The first page is the inside of the + self-mailer and the second page is the outside (where the address will be + stamped on). + + size: Enum representing the supported self-mailer sizes. + + to: The recipient of this order. You can either supply the contact information + inline here or provide a contact ID. PostGrid will automatically deduplicate + contacts regardless of whether you provide the information inline here or call + the contact creation endpoint. + + description: An optional string describing this resource. Will be visible in the API and the + dashboard. + + mailing_class: The mailing class of this order. If not provided, automatically set to + `first_class`. + + merge_variables: These will be merged with the variables in the template or HTML you create this + order with. The keys in this object should match the variable names in the + template _exactly_ as they are case-sensitive. Note that these _do not_ apply to + PDFs uploaded with the order. + + metadata: See the section on Metadata. + + send_date: This order will transition from `ready` to `printing` on the day after this + date. You can use this parameter to schedule orders for a future date. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + def create( + self, + *, + from_: self_mailer_create_params.SelfMailerCreateWithPdfFileFrom, + pdf: Union[str, Base64FileInput], + size: SelfMailerSize, + to: self_mailer_create_params.SelfMailerCreateWithPdfFileTo, + description: str | NotGiven = NOT_GIVEN, + mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, + merge_variables: Dict[str, object] | NotGiven = NOT_GIVEN, + metadata: Dict[str, object] | NotGiven = NOT_GIVEN, + send_date: Union[str, datetime] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SelfMailer: + """Create a self-mailer. + + Note that you can supply one of the following: + + - HTML content for the inside and outside of the self-mailer + - A template ID for the inside and outside of the self-mailer + - A URL or file for a 2 page PDF where the first page is the outside of the + self-mailer and the second page is the inside + - Upload the aforementioned PDF file via a multipart form upload request + + Args: + from_: The contact information of the sender. You can pass contact information inline + here just like you can for the `to`. + + pdf: A 2 page PDF file. The first page is the inside of the self-mailer and the + second page is the outside (where the address will be stamped on). + + size: Enum representing the supported self-mailer sizes. + + to: The recipient of this order. You can either supply the contact information + inline here or provide a contact ID. PostGrid will automatically deduplicate + contacts regardless of whether you provide the information inline here or call + the contact creation endpoint. + + description: An optional string describing this resource. Will be visible in the API and the + dashboard. + + mailing_class: The mailing class of this order. If not provided, automatically set to + `first_class`. + + merge_variables: These will be merged with the variables in the template or HTML you create this + order with. The keys in this object should match the variable names in the + template _exactly_ as they are case-sensitive. Note that these _do not_ apply to + PDFs uploaded with the order. + + metadata: See the section on Metadata. + + send_date: This order will transition from `ready` to `printing` on the day after this + date. You can use this parameter to schedule orders for a future date. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args( + ["from_", "inside_html", "outside_html", "size", "to"], + ["inside_template", "outside_template"], + ["from_", "pdf", "size", "to"], + ) + def create( + self, + *, + from_: self_mailer_create_params.SelfMailerCreateWithHTMLFrom | NotGiven = NOT_GIVEN, + inside_html: str | NotGiven = NOT_GIVEN, + outside_html: str | NotGiven = NOT_GIVEN, + size: SelfMailerSize | NotGiven = NOT_GIVEN, + to: self_mailer_create_params.SelfMailerCreateWithHTMLTo | NotGiven = NOT_GIVEN, + description: str | NotGiven = NOT_GIVEN, + mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, + merge_variables: Dict[str, object] | NotGiven = NOT_GIVEN, + metadata: Dict[str, object] | NotGiven = NOT_GIVEN, + send_date: Union[str, datetime] | NotGiven = NOT_GIVEN, + inside_template: str | NotGiven = NOT_GIVEN, + outside_template: str | NotGiven = NOT_GIVEN, + pdf: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SelfMailer: + return self._post( + "/print-mail/v1/self_mailers", + body=maybe_transform( + { + "from_": from_, + "inside_html": inside_html, + "outside_html": outside_html, + "size": size, + "to": to, + "description": description, + "mailing_class": mailing_class, + "merge_variables": merge_variables, + "metadata": metadata, + "send_date": send_date, + "inside_template": inside_template, + "outside_template": outside_template, + "pdf": pdf, + }, + self_mailer_create_params.SelfMailerCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=SelfMailer, + ) + + def retrieve( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SelfMailer: + """ + Retrieve a self-mailer by ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._get( + f"/print-mail/v1/self_mailers/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=SelfMailer, + ) + + def list( + self, + *, + limit: int | NotGiven = NOT_GIVEN, + search: str | NotGiven = NOT_GIVEN, + skip: int | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SyncSkipLimit[SelfMailer]: + """ + Get a list of self-mailers. + + Args: + search: You can supply any string to help narrow down the list of resources. For + example, if you pass `"New York"` (quoted), it will return resources that have + that string present somewhere in their response. Alternatively, you can supply a + structured search query. See the documentation on `StructuredSearchQuery` for + more details. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/print-mail/v1/self_mailers", + page=SyncSkipLimit[SelfMailer], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "limit": limit, + "search": search, + "skip": skip, + }, + self_mailer_list_params.SelfMailerListParams, + ), + ), + model=SelfMailer, + ) + + def delete( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SelfMailer: + """Cancel a self-mailer by ID. + + Note that this operation cannot be undone. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._delete( + f"/print-mail/v1/self_mailers/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=SelfMailer, + ) + + def retrieve_url( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SelfMailerRetrieveURLResponse: + """ + Retrieve a self-mailer preview URL. + + This is only available for customers with our document management addon, which + offers document generation and hosting capabilities. This endpoint has a much + higher rate limit than the regular order retrieval endpoint, so it is suitable + for customer-facing use-cases. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._get( + f"/print-mail/v1/self_mailers/{id}/url", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=SelfMailerRetrieveURLResponse, + ) + + +class AsyncSelfMailersResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncSelfMailersResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + """ + return AsyncSelfMailersResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncSelfMailersResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + """ + return AsyncSelfMailersResourceWithStreamingResponse(self) + + @overload + async def create( + self, + *, + from_: self_mailer_create_params.SelfMailerCreateWithHTMLFrom, + inside_html: str, + outside_html: str, + size: SelfMailerSize, + to: self_mailer_create_params.SelfMailerCreateWithHTMLTo, + description: str | NotGiven = NOT_GIVEN, + mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, + merge_variables: Dict[str, object] | NotGiven = NOT_GIVEN, + metadata: Dict[str, object] | NotGiven = NOT_GIVEN, + send_date: Union[str, datetime] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SelfMailer: + """Create a self-mailer. + + Note that you can supply one of the following: + + - HTML content for the inside and outside of the self-mailer + - A template ID for the inside and outside of the self-mailer + - A URL or file for a 2 page PDF where the first page is the outside of the + self-mailer and the second page is the inside + - Upload the aforementioned PDF file via a multipart form upload request + + Args: + from_: The contact information of the sender. You can pass contact information inline + here just like you can for the `to`. + + inside_html: The HTML content for the inside of the self-mailer. You can supply _either_ this + or `insideTemplate` but not both. + + outside_html: The HTML content for the outside of the self-mailer. You can supply _either_ + this or `outsideTemplate` but not both. + + size: Enum representing the supported self-mailer sizes. + + to: The recipient of this order. You can either supply the contact information + inline here or provide a contact ID. PostGrid will automatically deduplicate + contacts regardless of whether you provide the information inline here or call + the contact creation endpoint. + + description: An optional string describing this resource. Will be visible in the API and the + dashboard. + + mailing_class: The mailing class of this order. If not provided, automatically set to + `first_class`. + + merge_variables: These will be merged with the variables in the template or HTML you create this + order with. The keys in this object should match the variable names in the + template _exactly_ as they are case-sensitive. Note that these _do not_ apply to + PDFs uploaded with the order. + + metadata: See the section on Metadata. + + send_date: This order will transition from `ready` to `printing` on the day after this + date. You can use this parameter to schedule orders for a future date. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def create( + self, + *, + inside_template: str, + outside_template: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SelfMailer: + """Create a self-mailer. + + Note that you can supply one of the following: + + - HTML content for the inside and outside of the self-mailer + - A template ID for the inside and outside of the self-mailer + - A URL or file for a 2 page PDF where the first page is the outside of the + self-mailer and the second page is the inside + - Upload the aforementioned PDF file via a multipart form upload request + + Args: + inside_template: The template ID for the inside of the self-mailer. You can supply _either_ this + or `insideHTML` but not both. + + outside_template: The template ID for the outside of the self-mailer. You can supply _either_ this + or `outsideHTML` but not both. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def create( + self, + *, + from_: self_mailer_create_params.SelfMailerCreateWithPdfurlFrom, + pdf: str, + size: SelfMailerSize, + to: self_mailer_create_params.SelfMailerCreateWithPdfurlTo, + description: str | NotGiven = NOT_GIVEN, + mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, + merge_variables: Dict[str, object] | NotGiven = NOT_GIVEN, + metadata: Dict[str, object] | NotGiven = NOT_GIVEN, + send_date: Union[str, datetime] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SelfMailer: + """Create a self-mailer. + + Note that you can supply one of the following: + + - HTML content for the inside and outside of the self-mailer + - A template ID for the inside and outside of the self-mailer + - A URL or file for a 2 page PDF where the first page is the outside of the + self-mailer and the second page is the inside + - Upload the aforementioned PDF file via a multipart form upload request + + Args: + from_: The contact information of the sender. You can pass contact information inline + here just like you can for the `to`. + + pdf: A URL pointing to a 2 page PDF file. The first page is the inside of the + self-mailer and the second page is the outside (where the address will be + stamped on). + + size: Enum representing the supported self-mailer sizes. + + to: The recipient of this order. You can either supply the contact information + inline here or provide a contact ID. PostGrid will automatically deduplicate + contacts regardless of whether you provide the information inline here or call + the contact creation endpoint. + + description: An optional string describing this resource. Will be visible in the API and the + dashboard. + + mailing_class: The mailing class of this order. If not provided, automatically set to + `first_class`. + + merge_variables: These will be merged with the variables in the template or HTML you create this + order with. The keys in this object should match the variable names in the + template _exactly_ as they are case-sensitive. Note that these _do not_ apply to + PDFs uploaded with the order. + + metadata: See the section on Metadata. + + send_date: This order will transition from `ready` to `printing` on the day after this + date. You can use this parameter to schedule orders for a future date. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @overload + async def create( + self, + *, + from_: self_mailer_create_params.SelfMailerCreateWithPdfFileFrom, + pdf: Union[str, Base64FileInput], + size: SelfMailerSize, + to: self_mailer_create_params.SelfMailerCreateWithPdfFileTo, + description: str | NotGiven = NOT_GIVEN, + mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, + merge_variables: Dict[str, object] | NotGiven = NOT_GIVEN, + metadata: Dict[str, object] | NotGiven = NOT_GIVEN, + send_date: Union[str, datetime] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SelfMailer: + """Create a self-mailer. + + Note that you can supply one of the following: + + - HTML content for the inside and outside of the self-mailer + - A template ID for the inside and outside of the self-mailer + - A URL or file for a 2 page PDF where the first page is the outside of the + self-mailer and the second page is the inside + - Upload the aforementioned PDF file via a multipart form upload request + + Args: + from_: The contact information of the sender. You can pass contact information inline + here just like you can for the `to`. + + pdf: A 2 page PDF file. The first page is the inside of the self-mailer and the + second page is the outside (where the address will be stamped on). + + size: Enum representing the supported self-mailer sizes. + + to: The recipient of this order. You can either supply the contact information + inline here or provide a contact ID. PostGrid will automatically deduplicate + contacts regardless of whether you provide the information inline here or call + the contact creation endpoint. + + description: An optional string describing this resource. Will be visible in the API and the + dashboard. + + mailing_class: The mailing class of this order. If not provided, automatically set to + `first_class`. + + merge_variables: These will be merged with the variables in the template or HTML you create this + order with. The keys in this object should match the variable names in the + template _exactly_ as they are case-sensitive. Note that these _do not_ apply to + PDFs uploaded with the order. + + metadata: See the section on Metadata. + + send_date: This order will transition from `ready` to `printing` on the day after this + date. You can use this parameter to schedule orders for a future date. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + ... + + @required_args( + ["from_", "inside_html", "outside_html", "size", "to"], + ["inside_template", "outside_template"], + ["from_", "pdf", "size", "to"], + ) + async def create( + self, + *, + from_: self_mailer_create_params.SelfMailerCreateWithHTMLFrom | NotGiven = NOT_GIVEN, + inside_html: str | NotGiven = NOT_GIVEN, + outside_html: str | NotGiven = NOT_GIVEN, + size: SelfMailerSize | NotGiven = NOT_GIVEN, + to: self_mailer_create_params.SelfMailerCreateWithHTMLTo | NotGiven = NOT_GIVEN, + description: str | NotGiven = NOT_GIVEN, + mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, + merge_variables: Dict[str, object] | NotGiven = NOT_GIVEN, + metadata: Dict[str, object] | NotGiven = NOT_GIVEN, + send_date: Union[str, datetime] | NotGiven = NOT_GIVEN, + inside_template: str | NotGiven = NOT_GIVEN, + outside_template: str | NotGiven = NOT_GIVEN, + pdf: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SelfMailer: + return await self._post( + "/print-mail/v1/self_mailers", + body=await async_maybe_transform( + { + "from_": from_, + "inside_html": inside_html, + "outside_html": outside_html, + "size": size, + "to": to, + "description": description, + "mailing_class": mailing_class, + "merge_variables": merge_variables, + "metadata": metadata, + "send_date": send_date, + "inside_template": inside_template, + "outside_template": outside_template, + "pdf": pdf, + }, + self_mailer_create_params.SelfMailerCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=SelfMailer, + ) + + async def retrieve( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SelfMailer: + """ + Retrieve a self-mailer by ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._get( + f"/print-mail/v1/self_mailers/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=SelfMailer, + ) + + def list( + self, + *, + limit: int | NotGiven = NOT_GIVEN, + search: str | NotGiven = NOT_GIVEN, + skip: int | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncPaginator[SelfMailer, AsyncSkipLimit[SelfMailer]]: + """ + Get a list of self-mailers. + + Args: + search: You can supply any string to help narrow down the list of resources. For + example, if you pass `"New York"` (quoted), it will return resources that have + that string present somewhere in their response. Alternatively, you can supply a + structured search query. See the documentation on `StructuredSearchQuery` for + more details. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/print-mail/v1/self_mailers", + page=AsyncSkipLimit[SelfMailer], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "limit": limit, + "search": search, + "skip": skip, + }, + self_mailer_list_params.SelfMailerListParams, + ), + ), + model=SelfMailer, + ) + + async def delete( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SelfMailer: + """Cancel a self-mailer by ID. + + Note that this operation cannot be undone. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._delete( + f"/print-mail/v1/self_mailers/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=SelfMailer, + ) + + async def retrieve_url( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SelfMailerRetrieveURLResponse: + """ + Retrieve a self-mailer preview URL. + + This is only available for customers with our document management addon, which + offers document generation and hosting capabilities. This endpoint has a much + higher rate limit than the regular order retrieval endpoint, so it is suitable + for customer-facing use-cases. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._get( + f"/print-mail/v1/self_mailers/{id}/url", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=SelfMailerRetrieveURLResponse, + ) + + +class SelfMailersResourceWithRawResponse: + def __init__(self, self_mailers: SelfMailersResource) -> None: + self._self_mailers = self_mailers + + self.create = to_raw_response_wrapper( + self_mailers.create, + ) + self.retrieve = to_raw_response_wrapper( + self_mailers.retrieve, + ) + self.list = to_raw_response_wrapper( + self_mailers.list, + ) + self.delete = to_raw_response_wrapper( + self_mailers.delete, + ) + self.retrieve_url = to_raw_response_wrapper( + self_mailers.retrieve_url, + ) + + +class AsyncSelfMailersResourceWithRawResponse: + def __init__(self, self_mailers: AsyncSelfMailersResource) -> None: + self._self_mailers = self_mailers + + self.create = async_to_raw_response_wrapper( + self_mailers.create, + ) + self.retrieve = async_to_raw_response_wrapper( + self_mailers.retrieve, + ) + self.list = async_to_raw_response_wrapper( + self_mailers.list, + ) + self.delete = async_to_raw_response_wrapper( + self_mailers.delete, + ) + self.retrieve_url = async_to_raw_response_wrapper( + self_mailers.retrieve_url, + ) + + +class SelfMailersResourceWithStreamingResponse: + def __init__(self, self_mailers: SelfMailersResource) -> None: + self._self_mailers = self_mailers + + self.create = to_streamed_response_wrapper( + self_mailers.create, + ) + self.retrieve = to_streamed_response_wrapper( + self_mailers.retrieve, + ) + self.list = to_streamed_response_wrapper( + self_mailers.list, + ) + self.delete = to_streamed_response_wrapper( + self_mailers.delete, + ) + self.retrieve_url = to_streamed_response_wrapper( + self_mailers.retrieve_url, + ) + + +class AsyncSelfMailersResourceWithStreamingResponse: + def __init__(self, self_mailers: AsyncSelfMailersResource) -> None: + self._self_mailers = self_mailers + + self.create = async_to_streamed_response_wrapper( + self_mailers.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + self_mailers.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + self_mailers.list, + ) + self.delete = async_to_streamed_response_wrapper( + self_mailers.delete, + ) + self.retrieve_url = async_to_streamed_response_wrapper( + self_mailers.retrieve_url, + ) diff --git a/src/postgrid/resources/print_mail/sub_organizations.py b/src/postgrid/resources/print_mail/sub_organizations.py new file mode 100644 index 0000000..8fc95bd --- /dev/null +++ b/src/postgrid/resources/print_mail/sub_organizations.py @@ -0,0 +1,541 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._utils import ( + maybe_transform, + async_maybe_transform, +) +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...pagination import SyncSkipLimit, AsyncSkipLimit +from ..._base_client import AsyncPaginator, make_request_options +from ...types.print_mail import ( + sub_organization_list_params, + sub_organization_update_params, + sub_organization_retrieve_users_params, +) +from ...types.print_mail.sub_organization import SubOrganization +from ...types.print_mail.sub_organization_update_response import SubOrganizationUpdateResponse +from ...types.print_mail.sub_organization_retrieve_users_response import SubOrganizationRetrieveUsersResponse + +__all__ = ["SubOrganizationsResource", "AsyncSubOrganizationsResource"] + + +class SubOrganizationsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> SubOrganizationsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + """ + return SubOrganizationsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> SubOrganizationsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + """ + return SubOrganizationsResourceWithStreamingResponse(self) + + def retrieve( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SubOrganization: + """ + Get a sub-organization. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._get( + f"/print-mail/v1/sub_organizations/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=SubOrganization, + ) + + def update( + self, + *, + country_code: str, + email: str, + name: str, + organization_name: str, + password: str, + phone_number: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SubOrganizationUpdateResponse: + """ + When creating a user through the API, the verifiedEmail field will automatically + be set to true. However, if public signups are used, the email will need to be + verified by the user. + + Args: + country_code: The country code of the sub-organization. + + email: The email of the user to be created alongside the sub-organization. + + name: The name of the user to be created alongside the sub-organization. + + organization_name: The name of the sub-organization. + + password: The password of the user to be created alongside the sub-organization. + + phone_number: The phone number of the user to be created alongside the sub-organization. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/print-mail/v1/sub_organizations", + body=maybe_transform( + { + "country_code": country_code, + "email": email, + "name": name, + "organization_name": organization_name, + "password": password, + "phone_number": phone_number, + }, + sub_organization_update_params.SubOrganizationUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=SubOrganizationUpdateResponse, + ) + + def list( + self, + *, + limit: int | NotGiven = NOT_GIVEN, + search: str | NotGiven = NOT_GIVEN, + skip: int | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SyncSkipLimit[SubOrganization]: + """ + List sub-organizations. + + Args: + search: You can supply any string to help narrow down the list of resources. For + example, if you pass `"New York"` (quoted), it will return resources that have + that string present somewhere in their response. Alternatively, you can supply a + structured search query. See the documentation on `StructuredSearchQuery` for + more details. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/print-mail/v1/sub_organizations", + page=SyncSkipLimit[SubOrganization], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "limit": limit, + "search": search, + "skip": skip, + }, + sub_organization_list_params.SubOrganizationListParams, + ), + ), + model=SubOrganization, + ) + + def retrieve_users( + self, + id: str, + *, + limit: int | NotGiven = NOT_GIVEN, + search: str | NotGiven = NOT_GIVEN, + skip: int | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SubOrganizationRetrieveUsersResponse: + """ + List users for a sub-organization. + + Args: + search: You can supply any string to help narrow down the list of resources. For + example, if you pass `"New York"` (quoted), it will return resources that have + that string present somewhere in their response. Alternatively, you can supply a + structured search query. See the documentation on `StructuredSearchQuery` for + more details. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._get( + f"/print-mail/v1/sub_organizations/{id}/users", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "limit": limit, + "search": search, + "skip": skip, + }, + sub_organization_retrieve_users_params.SubOrganizationRetrieveUsersParams, + ), + ), + cast_to=SubOrganizationRetrieveUsersResponse, + ) + + +class AsyncSubOrganizationsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncSubOrganizationsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + """ + return AsyncSubOrganizationsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncSubOrganizationsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + """ + return AsyncSubOrganizationsResourceWithStreamingResponse(self) + + async def retrieve( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SubOrganization: + """ + Get a sub-organization. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._get( + f"/print-mail/v1/sub_organizations/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=SubOrganization, + ) + + async def update( + self, + *, + country_code: str, + email: str, + name: str, + organization_name: str, + password: str, + phone_number: str | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SubOrganizationUpdateResponse: + """ + When creating a user through the API, the verifiedEmail field will automatically + be set to true. However, if public signups are used, the email will need to be + verified by the user. + + Args: + country_code: The country code of the sub-organization. + + email: The email of the user to be created alongside the sub-organization. + + name: The name of the user to be created alongside the sub-organization. + + organization_name: The name of the sub-organization. + + password: The password of the user to be created alongside the sub-organization. + + phone_number: The phone number of the user to be created alongside the sub-organization. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/print-mail/v1/sub_organizations", + body=await async_maybe_transform( + { + "country_code": country_code, + "email": email, + "name": name, + "organization_name": organization_name, + "password": password, + "phone_number": phone_number, + }, + sub_organization_update_params.SubOrganizationUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=SubOrganizationUpdateResponse, + ) + + def list( + self, + *, + limit: int | NotGiven = NOT_GIVEN, + search: str | NotGiven = NOT_GIVEN, + skip: int | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncPaginator[SubOrganization, AsyncSkipLimit[SubOrganization]]: + """ + List sub-organizations. + + Args: + search: You can supply any string to help narrow down the list of resources. For + example, if you pass `"New York"` (quoted), it will return resources that have + that string present somewhere in their response. Alternatively, you can supply a + structured search query. See the documentation on `StructuredSearchQuery` for + more details. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/print-mail/v1/sub_organizations", + page=AsyncSkipLimit[SubOrganization], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "limit": limit, + "search": search, + "skip": skip, + }, + sub_organization_list_params.SubOrganizationListParams, + ), + ), + model=SubOrganization, + ) + + async def retrieve_users( + self, + id: str, + *, + limit: int | NotGiven = NOT_GIVEN, + search: str | NotGiven = NOT_GIVEN, + skip: int | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SubOrganizationRetrieveUsersResponse: + """ + List users for a sub-organization. + + Args: + search: You can supply any string to help narrow down the list of resources. For + example, if you pass `"New York"` (quoted), it will return resources that have + that string present somewhere in their response. Alternatively, you can supply a + structured search query. See the documentation on `StructuredSearchQuery` for + more details. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._get( + f"/print-mail/v1/sub_organizations/{id}/users", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "limit": limit, + "search": search, + "skip": skip, + }, + sub_organization_retrieve_users_params.SubOrganizationRetrieveUsersParams, + ), + ), + cast_to=SubOrganizationRetrieveUsersResponse, + ) + + +class SubOrganizationsResourceWithRawResponse: + def __init__(self, sub_organizations: SubOrganizationsResource) -> None: + self._sub_organizations = sub_organizations + + self.retrieve = to_raw_response_wrapper( + sub_organizations.retrieve, + ) + self.update = to_raw_response_wrapper( + sub_organizations.update, + ) + self.list = to_raw_response_wrapper( + sub_organizations.list, + ) + self.retrieve_users = to_raw_response_wrapper( + sub_organizations.retrieve_users, + ) + + +class AsyncSubOrganizationsResourceWithRawResponse: + def __init__(self, sub_organizations: AsyncSubOrganizationsResource) -> None: + self._sub_organizations = sub_organizations + + self.retrieve = async_to_raw_response_wrapper( + sub_organizations.retrieve, + ) + self.update = async_to_raw_response_wrapper( + sub_organizations.update, + ) + self.list = async_to_raw_response_wrapper( + sub_organizations.list, + ) + self.retrieve_users = async_to_raw_response_wrapper( + sub_organizations.retrieve_users, + ) + + +class SubOrganizationsResourceWithStreamingResponse: + def __init__(self, sub_organizations: SubOrganizationsResource) -> None: + self._sub_organizations = sub_organizations + + self.retrieve = to_streamed_response_wrapper( + sub_organizations.retrieve, + ) + self.update = to_streamed_response_wrapper( + sub_organizations.update, + ) + self.list = to_streamed_response_wrapper( + sub_organizations.list, + ) + self.retrieve_users = to_streamed_response_wrapper( + sub_organizations.retrieve_users, + ) + + +class AsyncSubOrganizationsResourceWithStreamingResponse: + def __init__(self, sub_organizations: AsyncSubOrganizationsResource) -> None: + self._sub_organizations = sub_organizations + + self.retrieve = async_to_streamed_response_wrapper( + sub_organizations.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + sub_organizations.update, + ) + self.list = async_to_streamed_response_wrapper( + sub_organizations.list, + ) + self.retrieve_users = async_to_streamed_response_wrapper( + sub_organizations.retrieve_users, + ) diff --git a/src/postgrid/resources/print_mail/templates.py b/src/postgrid/resources/print_mail/templates.py new file mode 100644 index 0000000..0f8fe14 --- /dev/null +++ b/src/postgrid/resources/print_mail/templates.py @@ -0,0 +1,592 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict + +import httpx + +from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven +from ..._utils import ( + maybe_transform, + async_maybe_transform, +) +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ...pagination import SyncSkipLimit, AsyncSkipLimit +from ..._base_client import AsyncPaginator, make_request_options +from ...types.print_mail import template_list_params, template_create_params, template_update_params +from ...types.print_mail.template import Template +from ...types.print_mail.template_delete_response import TemplateDeleteResponse + +__all__ = ["TemplatesResource", "AsyncTemplatesResource"] + + +class TemplatesResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> TemplatesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + """ + return TemplatesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> TemplatesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + """ + return TemplatesResourceWithStreamingResponse(self) + + def create( + self, + *, + description: str | NotGiven = NOT_GIVEN, + html: str | NotGiven = NOT_GIVEN, + metadata: Dict[str, object] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Template: + """Create a template. + + Note that if you want to create a template that works with + our template editor, you must use our dashboard. + + Args: + description: An optional string describing this resource. Will be visible in the API and the + dashboard. + + html: The HTML content of this template. + + metadata: See the section on Metadata. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/print-mail/v1/templates", + body=maybe_transform( + { + "description": description, + "html": html, + "metadata": metadata, + }, + template_create_params.TemplateCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Template, + ) + + def retrieve( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Template: + """ + Retrieve a template by ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._get( + f"/print-mail/v1/templates/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Template, + ) + + def update( + self, + id: str, + *, + description: str | NotGiven = NOT_GIVEN, + html: str | NotGiven = NOT_GIVEN, + metadata: Dict[str, object] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Template: + """ + Update a template by ID. + + Args: + description: An optional string describing this resource. Will be visible in the API and the + dashboard. + + html: The HTML content of this template. + + metadata: See the section on Metadata. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._post( + f"/print-mail/v1/templates/{id}", + body=maybe_transform( + { + "description": description, + "html": html, + "metadata": metadata, + }, + template_update_params.TemplateUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Template, + ) + + def list( + self, + *, + limit: int | NotGiven = NOT_GIVEN, + search: str | NotGiven = NOT_GIVEN, + skip: int | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> SyncSkipLimit[Template]: + """ + Get a list of templates. + + Args: + search: You can supply any string to help narrow down the list of resources. For + example, if you pass `"New York"` (quoted), it will return resources that have + that string present somewhere in their response. Alternatively, you can supply a + structured search query. See the documentation on `StructuredSearchQuery` for + more details. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/print-mail/v1/templates", + page=SyncSkipLimit[Template], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "limit": limit, + "search": search, + "skip": skip, + }, + template_list_params.TemplateListParams, + ), + ), + model=Template, + ) + + def delete( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> TemplateDeleteResponse: + """Delete a template by ID. + + Note that this operation cannot be undone. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._delete( + f"/print-mail/v1/templates/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=TemplateDeleteResponse, + ) + + +class AsyncTemplatesResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncTemplatesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + """ + return AsyncTemplatesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncTemplatesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + """ + return AsyncTemplatesResourceWithStreamingResponse(self) + + async def create( + self, + *, + description: str | NotGiven = NOT_GIVEN, + html: str | NotGiven = NOT_GIVEN, + metadata: Dict[str, object] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Template: + """Create a template. + + Note that if you want to create a template that works with + our template editor, you must use our dashboard. + + Args: + description: An optional string describing this resource. Will be visible in the API and the + dashboard. + + html: The HTML content of this template. + + metadata: See the section on Metadata. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/print-mail/v1/templates", + body=await async_maybe_transform( + { + "description": description, + "html": html, + "metadata": metadata, + }, + template_create_params.TemplateCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Template, + ) + + async def retrieve( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Template: + """ + Retrieve a template by ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._get( + f"/print-mail/v1/templates/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Template, + ) + + async def update( + self, + id: str, + *, + description: str | NotGiven = NOT_GIVEN, + html: str | NotGiven = NOT_GIVEN, + metadata: Dict[str, object] | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> Template: + """ + Update a template by ID. + + Args: + description: An optional string describing this resource. Will be visible in the API and the + dashboard. + + html: The HTML content of this template. + + metadata: See the section on Metadata. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._post( + f"/print-mail/v1/templates/{id}", + body=await async_maybe_transform( + { + "description": description, + "html": html, + "metadata": metadata, + }, + template_update_params.TemplateUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=Template, + ) + + def list( + self, + *, + limit: int | NotGiven = NOT_GIVEN, + search: str | NotGiven = NOT_GIVEN, + skip: int | NotGiven = NOT_GIVEN, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncPaginator[Template, AsyncSkipLimit[Template]]: + """ + Get a list of templates. + + Args: + search: You can supply any string to help narrow down the list of resources. For + example, if you pass `"New York"` (quoted), it will return resources that have + that string present somewhere in their response. Alternatively, you can supply a + structured search query. See the documentation on `StructuredSearchQuery` for + more details. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get_api_list( + "/print-mail/v1/templates", + page=AsyncSkipLimit[Template], + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "limit": limit, + "search": search, + "skip": skip, + }, + template_list_params.TemplateListParams, + ), + ), + model=Template, + ) + + async def delete( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> TemplateDeleteResponse: + """Delete a template by ID. + + Note that this operation cannot be undone. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._delete( + f"/print-mail/v1/templates/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=TemplateDeleteResponse, + ) + + +class TemplatesResourceWithRawResponse: + def __init__(self, templates: TemplatesResource) -> None: + self._templates = templates + + self.create = to_raw_response_wrapper( + templates.create, + ) + self.retrieve = to_raw_response_wrapper( + templates.retrieve, + ) + self.update = to_raw_response_wrapper( + templates.update, + ) + self.list = to_raw_response_wrapper( + templates.list, + ) + self.delete = to_raw_response_wrapper( + templates.delete, + ) + + +class AsyncTemplatesResourceWithRawResponse: + def __init__(self, templates: AsyncTemplatesResource) -> None: + self._templates = templates + + self.create = async_to_raw_response_wrapper( + templates.create, + ) + self.retrieve = async_to_raw_response_wrapper( + templates.retrieve, + ) + self.update = async_to_raw_response_wrapper( + templates.update, + ) + self.list = async_to_raw_response_wrapper( + templates.list, + ) + self.delete = async_to_raw_response_wrapper( + templates.delete, + ) + + +class TemplatesResourceWithStreamingResponse: + def __init__(self, templates: TemplatesResource) -> None: + self._templates = templates + + self.create = to_streamed_response_wrapper( + templates.create, + ) + self.retrieve = to_streamed_response_wrapper( + templates.retrieve, + ) + self.update = to_streamed_response_wrapper( + templates.update, + ) + self.list = to_streamed_response_wrapper( + templates.list, + ) + self.delete = to_streamed_response_wrapper( + templates.delete, + ) + + +class AsyncTemplatesResourceWithStreamingResponse: + def __init__(self, templates: AsyncTemplatesResource) -> None: + self._templates = templates + + self.create = async_to_streamed_response_wrapper( + templates.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + templates.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + templates.update, + ) + self.list = async_to_streamed_response_wrapper( + templates.list, + ) + self.delete = async_to_streamed_response_wrapper( + templates.delete, + ) diff --git a/src/postgrid/types/__init__.py b/src/postgrid/types/__init__.py new file mode 100644 index 0000000..a35a1a9 --- /dev/null +++ b/src/postgrid/types/__init__.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .errors import Errors as Errors +from .status import Status as Status +from .address_verification_verify_params import AddressVerificationVerifyParams as AddressVerificationVerifyParams +from .address_verification_verify_response import AddressVerificationVerifyResponse as AddressVerificationVerifyResponse +from .contact_create_with_first_name_param import ContactCreateWithFirstNameParam as ContactCreateWithFirstNameParam +from .contact_create_with_company_name_param import ( + ContactCreateWithCompanyNameParam as ContactCreateWithCompanyNameParam, +) +from .intl_address_verification_verify_params import ( + IntlAddressVerificationVerifyParams as IntlAddressVerificationVerifyParams, +) +from .intl_address_verification_verify_response import ( + IntlAddressVerificationVerifyResponse as IntlAddressVerificationVerifyResponse, +) diff --git a/src/postgrid/types/address_verification_verify_params.py b/src/postgrid/types/address_verification_verify_params.py new file mode 100644 index 0000000..e8f44f8 --- /dev/null +++ b/src/postgrid/types/address_verification_verify_params.py @@ -0,0 +1,62 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Literal, Required, Annotated, TypeAlias, TypedDict + +from .._utils import PropertyInfo + +__all__ = [ + "AddressVerificationVerifyParams", + "StandardFreeformAddressInput", + "StandardStructuredAddressInput", + "StandardStructuredAddressInputAddress", +] + + +class StandardFreeformAddressInput(TypedDict, total=False): + address: Required[str] + """The address you want to verify, written on a single line.""" + + geocode: bool + + include_details: Annotated[bool, PropertyInfo(alias="includeDetails")] + + proper_case: Annotated[bool, PropertyInfo(alias="properCase")] + + +class StandardStructuredAddressInput(TypedDict, total=False): + address: Required[StandardStructuredAddressInputAddress] + + geocode: bool + + include_details: Annotated[bool, PropertyInfo(alias="includeDetails")] + + proper_case: Annotated[bool, PropertyInfo(alias="properCase")] + + +class StandardStructuredAddressInputAddress(TypedDict, total=False): + city: Required[str] + """The city of the address.""" + + country: Required[Literal["ca", "us"]] + """The country of your address, one of `ca` or `us`.""" + + line1: Required[str] + """The first line of the address.""" + + postal_or_zip: Required[Annotated[str, PropertyInfo(alias="postalOrZip")]] + """The postal code or ZIP code of the address.""" + + province_or_state: Required[Annotated[str, PropertyInfo(alias="provinceOrState")]] + """The province or state of the address.""" + + line2: str + """The second line of the address.""" + + recipient: str + """The optional firm/recipient name.""" + + +AddressVerificationVerifyParams: TypeAlias = Union[StandardFreeformAddressInput, StandardStructuredAddressInput] diff --git a/src/postgrid/types/address_verification_verify_response.py b/src/postgrid/types/address_verification_verify_response.py new file mode 100644 index 0000000..47bdd2a --- /dev/null +++ b/src/postgrid/types/address_verification_verify_response.py @@ -0,0 +1,249 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from .errors import Errors +from .status import Status +from .._models import BaseModel + +__all__ = ["AddressVerificationVerifyResponse", "Data", "DataDetails", "DataGeocodeResult", "DataGeocodeResultLocation"] + + +class DataDetails(BaseModel): + box_id: Optional[str] = FieldInfo(alias="boxID", default=None) + """PO Box ID""" + + county: Optional[str] = None + """County in the United States (US address only)""" + + county_num: Optional[str] = FieldInfo(alias="countyNum", default=None) + """FIPS code for county (US address only)""" + + delivery_installation_area_name: Optional[str] = FieldInfo(alias="deliveryInstallationAreaName", default=None) + """Delivery installation area name""" + + delivery_installation_qualifier: Optional[str] = FieldInfo(alias="deliveryInstallationQualifier", default=None) + """Delivery installation qualifier""" + + delivery_installation_type: Optional[str] = FieldInfo(alias="deliveryInstallationType", default=None) + """Delivery installation type""" + + extra_info: Optional[str] = FieldInfo(alias="extraInfo", default=None) + """Any extra information relevant to the address""" + + post_direction: Optional[str] = FieldInfo(alias="postDirection", default=None) + """The post-direction of the street (after the street name, US addresses only)""" + + pre_direction: Optional[str] = FieldInfo(alias="preDirection", default=None) + """The pre-direction of the street (before the street name, US addresses only)""" + + residential: Optional[bool] = None + """Indicates that the address is residential (US address only)""" + + rural_route_number: Optional[str] = FieldInfo(alias="ruralRouteNumber", default=None) + """Rural route number""" + + rural_route_type: Optional[str] = FieldInfo(alias="ruralRouteType", default=None) + """Rural route type""" + + street_direction: Optional[str] = FieldInfo(alias="streetDirection", default=None) + """The direction of the street (N, S, E, W, etc)""" + + street_name: Optional[str] = FieldInfo(alias="streetName", default=None) + """Name of the street where the address is located""" + + street_number: Optional[str] = FieldInfo(alias="streetNumber", default=None) + """Street number (e.g. the 20 in 20 Bay St)""" + + street_type: Optional[str] = FieldInfo(alias="streetType", default=None) + """Type of the street (DR, ST, BLVD, etc)""" + + suite_id: Optional[str] = FieldInfo(alias="suiteID", default=None) + """The unit number/name""" + + suite_key: Optional[str] = FieldInfo(alias="suiteKey", default=None) + """The suite key""" + + us_census_block_number: Optional[str] = FieldInfo(alias="usCensusBlockNumber", default=None) + """US Census block number""" + + us_census_cmsa: Optional[str] = FieldInfo(alias="usCensusCMSA", default=None) + """US Census consolidated metropolitan statistical area""" + + us_census_ma: Optional[str] = FieldInfo(alias="usCensusMA", default=None) + """US Census metropolitan area""" + + us_census_msa: Optional[str] = FieldInfo(alias="usCensusMSA", default=None) + """US Census metropolitan statistical area""" + + us_census_pmsa: Optional[str] = FieldInfo(alias="usCensusPMSA", default=None) + """US Census primary metropolitan statistical area""" + + us_census_tract_number: Optional[str] = FieldInfo(alias="usCensusTractNumber", default=None) + """US Census tract number""" + + us_congressional_district_number: Optional[str] = FieldInfo(alias="usCongressionalDistrictNumber", default=None) + """US congressional district number""" + + us_has_daylight_savings: Optional[bool] = FieldInfo(alias="usHasDaylightSavings", default=None) + """True if address location recognizes DST""" + + us_mailing_check_digit: Optional[str] = FieldInfo(alias="usMailingCheckDigit", default=None) + """PostNet barcode digit""" + + us_mailings_carrier_route: Optional[str] = FieldInfo(alias="usMailingsCarrierRoute", default=None) + """4-character code assigned to mail delivery route within a 5 digit zip code""" + + us_mailings_default_flag: Optional[bool] = FieldInfo(alias="usMailingsDefaultFlag", default=None) + """ + True if US address matches a high-rise default or rural route default in the + USPS data + """ + + us_mailings_delivery_point: Optional[str] = FieldInfo(alias="usMailingsDeliveryPoint", default=None) + """Unique USPS identifier for the delivery point""" + + us_mailings_dpv_confirmation_indicator: Optional[str] = FieldInfo( + alias="usMailingsDpvConfirmationIndicator", default=None + ) + """See [USPS DPV](https://avdocs.postgrid.com/#usps-dpv)""" + + us_mailings_dpv_crma_indicator: Optional[str] = FieldInfo(alias="usMailingsDpvCrmaIndicator", default=None) + """Y if this is a commercial mail receiving agency, N otherwise""" + + us_mailings_dpv_footnote1: Optional[str] = FieldInfo(alias="usMailingsDpvFootnote1", default=None) + """See [USPS DPV](https://avdocs.postgrid.com/#usps-dpv)""" + + us_mailings_dpv_footnote2: Optional[str] = FieldInfo(alias="usMailingsDpvFootnote2", default=None) + """See [USPS DPV](https://avdocs.postgrid.com/#usps-dpv)""" + + us_mailings_dpv_footnote3: Optional[str] = FieldInfo(alias="usMailingsDpvFootnote3", default=None) + """See [USPS DPV](https://avdocs.postgrid.com/#usps-dpv)""" + + us_mailings_elot_asc_desc: Optional[str] = FieldInfo(alias="usMailingsElotAscDesc", default=None) + """A for ascending, D for descending""" + + us_mailings_elot_sequence_number: Optional[str] = FieldInfo(alias="usMailingsElotSequenceNumber", default=None) + """eLOT sequence number""" + + us_mailings_ews_flag: Optional[str] = FieldInfo(alias="usMailingsEWSFlag", default=None) + """Y if address is in early warning system database""" + + us_mailings_lacs_flag: Optional[str] = FieldInfo(alias="usMailingsLACSFlag", default=None) + """Y if address converted by LACS""" + + us_mailings_lacs_return_code: Optional[str] = FieldInfo(alias="usMailingsLACSReturnCode", default=None) + """Corresponds to USPS LACSLink return code""" + + us_mailings_record_type_code: Optional[str] = FieldInfo(alias="usMailingsRecordTypeCode", default=None) + """See [USPS DPV](https://avdocs.postgrid.com/#usps-dpv)""" + + us_mailings_suite_link_return_code: Optional[str] = FieldInfo(alias="usMailingsSuiteLinkReturnCode", default=None) + """See [USPS DPV](https://avdocs.postgrid.com/#usps-dpv)""" + + us_state_legislative_lower: Optional[str] = FieldInfo(alias="usStateLegislativeLower", default=None) + """Lower legislative district for the US address""" + + us_state_legislative_upper: Optional[str] = FieldInfo(alias="usStateLegislativeUpper", default=None) + """Upper legislative district for the US address""" + + us_time_zone: Optional[str] = FieldInfo(alias="usTimeZone", default=None) + """Time zone for the US address area""" + + vacant: Optional[bool] = None + """Indicates that the address is vacant according to the USPS (US address only)""" + + +class DataGeocodeResultLocation(BaseModel): + lat: float + + lng: float + + +class DataGeocodeResult(BaseModel): + accuracy: float + """ + A real number from 0.00 to 1.00 which represents an + [accuracy score](https://avdocs.postgrid.com/#accuracy-score) + """ + + accuracy_type: Literal[ + "rooftop", + "point", + "range_interpolation", + "nearest_rooftop_match", + "intersection", + "street_center", + "place", + "state", + ] = FieldInfo(alias="accuracyType") + """ + A string representing the + [accuracy type](https://avdocs.postgrid.com/#accuracy-type) + """ + + location: DataGeocodeResultLocation + """Object that contains `lat`, `lng` properties with number values""" + + +class Data(BaseModel): + city: str + """The city name of the address.""" + + country: str + """The country code of the address.""" + + line1: str + """The first line of the address.""" + + postal_or_zip: str = FieldInfo(alias="postalOrZip") + """The postal code or ZIP code of the address.""" + + province_or_state: str = FieldInfo(alias="provinceOrState") + """The province or state of the address.""" + + country_name: Optional[str] = FieldInfo(alias="countryName", default=None) + """The country name of the address.""" + + details: Optional[DataDetails] = None + """ + If you supply `includeDetails=true` as a query parameter, we will also populate + an additional `details` field that follows the + [Address Details](https://avdocs.postgrid.com/#address-details) schema. + """ + + errors: Optional[Errors] = None + """Errors encountered during address verification.""" + + firm_name: Optional[str] = FieldInfo(alias="firmName", default=None) + """The firm name of the address.""" + + geocode_result: Optional[DataGeocodeResult] = FieldInfo(alias="geocodeResult", default=None) + """ + If the `geocode=true` query parameter is supplied, the response will include a + geocodeResult which follows the + [Geocoding](https://avdocs.postgrid.com/#geocoding) schema. You can request this + feature be enabled by emailing `support@postgrid.com`. This includes our + verification, batch verification, suggestions, and POST /completions endpoint. + Note that you must supply country when geocoding to get the result successfully. + """ + + line2: Optional[str] = None + """The second line of the address.""" + + status: Optional[Status] = None + """The verification status of an address.""" + + zip_plus4: Optional[str] = FieldInfo(alias="zipPlus4", default=None) + """The zip plus 4 code of the address.""" + + +class AddressVerificationVerifyResponse(BaseModel): + data: Data + + message: str + + status: Literal["success", "error"] diff --git a/src/postgrid/types/contact_create_with_company_name_param.py b/src/postgrid/types/contact_create_with_company_name_param.py new file mode 100644 index 0000000..ffd12aa --- /dev/null +++ b/src/postgrid/types/contact_create_with_company_name_param.py @@ -0,0 +1,68 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict +from typing_extensions import Required, Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["ContactCreateWithCompanyNameParam"] + + +class ContactCreateWithCompanyNameParam(TypedDict, total=False): + address_line1: Required[Annotated[str, PropertyInfo(alias="addressLine1")]] + """The first line of the contact's address.""" + + company_name: Required[Annotated[str, PropertyInfo(alias="companyName")]] + + country_code: Required[Annotated[str, PropertyInfo(alias="countryCode")]] + """The ISO 3611-1 country code of the contact's address.""" + + address_line2: Annotated[str, PropertyInfo(alias="addressLine2")] + """Second line of the contact's address, if applicable.""" + + city: str + """The city of the contact's address.""" + + description: str + """An optional string describing this resource. + + Will be visible in the API and the dashboard. + """ + + email: str + """Email of the contact.""" + + first_name: Annotated[str, PropertyInfo(alias="firstName")] + """First name of the contact.""" + + force_verified_status: Annotated[bool, PropertyInfo(alias="forceVerifiedStatus")] + """ + If `true`, PostGrid will force this contact to have an `addressStatus` of + `verified` even if our address verification system says otherwise. + """ + + job_title: Annotated[str, PropertyInfo(alias="jobTitle")] + """Job title of the contact.""" + + last_name: Annotated[str, PropertyInfo(alias="lastName")] + """Last name of the contact.""" + + metadata: Dict[str, object] + """See the section on Metadata.""" + + phone_number: Annotated[str, PropertyInfo(alias="phoneNumber")] + """Phone number of the contact.""" + + postal_or_zip: Annotated[str, PropertyInfo(alias="postalOrZip")] + """The postal or ZIP code of the contact's address.""" + + province_or_state: Annotated[str, PropertyInfo(alias="provinceOrState")] + """Province or state of the contact's address.""" + + skip_verification: Annotated[bool, PropertyInfo(alias="skipVerification")] + """ + If `true`, PostGrid will skip running this contact's address through our address + verification system. + """ diff --git a/src/postgrid/types/contact_create_with_first_name_param.py b/src/postgrid/types/contact_create_with_first_name_param.py new file mode 100644 index 0000000..30f0279 --- /dev/null +++ b/src/postgrid/types/contact_create_with_first_name_param.py @@ -0,0 +1,68 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict +from typing_extensions import Required, Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["ContactCreateWithFirstNameParam"] + + +class ContactCreateWithFirstNameParam(TypedDict, total=False): + address_line1: Required[Annotated[str, PropertyInfo(alias="addressLine1")]] + """The first line of the contact's address.""" + + country_code: Required[Annotated[str, PropertyInfo(alias="countryCode")]] + """The ISO 3611-1 country code of the contact's address.""" + + first_name: Required[Annotated[str, PropertyInfo(alias="firstName")]] + + address_line2: Annotated[str, PropertyInfo(alias="addressLine2")] + """Second line of the contact's address, if applicable.""" + + city: str + """The city of the contact's address.""" + + company_name: Annotated[str, PropertyInfo(alias="companyName")] + """Company name of the contact.""" + + description: str + """An optional string describing this resource. + + Will be visible in the API and the dashboard. + """ + + email: str + """Email of the contact.""" + + force_verified_status: Annotated[bool, PropertyInfo(alias="forceVerifiedStatus")] + """ + If `true`, PostGrid will force this contact to have an `addressStatus` of + `verified` even if our address verification system says otherwise. + """ + + job_title: Annotated[str, PropertyInfo(alias="jobTitle")] + """Job title of the contact.""" + + last_name: Annotated[str, PropertyInfo(alias="lastName")] + """Last name of the contact.""" + + metadata: Dict[str, object] + """See the section on Metadata.""" + + phone_number: Annotated[str, PropertyInfo(alias="phoneNumber")] + """Phone number of the contact.""" + + postal_or_zip: Annotated[str, PropertyInfo(alias="postalOrZip")] + """The postal or ZIP code of the contact's address.""" + + province_or_state: Annotated[str, PropertyInfo(alias="provinceOrState")] + """Province or state of the contact's address.""" + + skip_verification: Annotated[bool, PropertyInfo(alias="skipVerification")] + """ + If `true`, PostGrid will skip running this contact's address through our address + verification system. + """ diff --git a/src/postgrid/types/errors.py b/src/postgrid/types/errors.py new file mode 100644 index 0000000..b6e5e81 --- /dev/null +++ b/src/postgrid/types/errors.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["Errors"] + + +class Errors(BaseModel): + city: Optional[List[str]] = None + """Errors related to the city.""" + + generic: Optional[List[str]] = None + """Generic errors not tied to a specific field.""" + + line1: Optional[List[str]] = None + """Errors related to the first address line.""" + + line2: Optional[List[str]] = None + """Errors related to the second address line.""" + + province_or_state: Optional[List[str]] = FieldInfo(alias="provinceOrState", default=None) + """Errors related to the province or state.""" diff --git a/src/postgrid/types/intl_address_verification_verify_params.py b/src/postgrid/types/intl_address_verification_verify_params.py new file mode 100644 index 0000000..bb4cd48 --- /dev/null +++ b/src/postgrid/types/intl_address_verification_verify_params.py @@ -0,0 +1,65 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import Required, Annotated, TypeAlias, TypedDict + +from .._utils import PropertyInfo + +__all__ = [ + "IntlAddressVerificationVerifyParams", + "StructuredAddressInput", + "StructuredAddressInputAddress", + "FreeformAddressInput", +] + + +class StructuredAddressInput(TypedDict, total=False): + address: Required[StructuredAddressInputAddress] + + geo_data: Annotated[bool, PropertyInfo(alias="geoData")] + + include_details: Annotated[bool, PropertyInfo(alias="includeDetails")] + + proper_case: Annotated[bool, PropertyInfo(alias="properCase")] + + +class StructuredAddressInputAddress(TypedDict, total=False): + country: Required[str] + """The country code (ISO 3166-1 alpha-2 or alpha-3).""" + + line1: Required[str] + """The first line of the address (e.g., street address, building, etc.).""" + + postal_or_zip: Required[Annotated[str, PropertyInfo(alias="postalOrZip")]] + """The postal or ZIP code.""" + + province_or_state: Required[Annotated[str, PropertyInfo(alias="provinceOrState")]] + """The administrative area (e.g., state, province, region).""" + + city: str + """The city, town, or locality of the address.""" + + line2: str + """The second line of the address (e.g., apartment, suite, etc.).""" + + line3: str + """The third line of the address (e.g., additional locality or delivery info).""" + + line4: str + """The fourth line of the address (e.g., further address details).""" + + +class FreeformAddressInput(TypedDict, total=False): + address: Required[str] + """The full address as a single string.""" + + geo_data: Annotated[bool, PropertyInfo(alias="geoData")] + + include_details: Annotated[bool, PropertyInfo(alias="includeDetails")] + + proper_case: Annotated[bool, PropertyInfo(alias="properCase")] + + +IntlAddressVerificationVerifyParams: TypeAlias = Union[StructuredAddressInput, FreeformAddressInput] diff --git a/src/postgrid/types/intl_address_verification_verify_response.py b/src/postgrid/types/intl_address_verification_verify_response.py new file mode 100644 index 0000000..150172e --- /dev/null +++ b/src/postgrid/types/intl_address_verification_verify_response.py @@ -0,0 +1,219 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from .errors import Errors +from .status import Status +from .._models import BaseModel + +__all__ = ["IntlAddressVerificationVerifyResponse", "Data", "DataDetails", "DataGeoData", "DataSummary"] + + +class DataDetails(BaseModel): + building: Optional[str] = None + """The building name or number.""" + + building_type: Optional[str] = FieldInfo(alias="buildingType", default=None) + """The type of building (e.g., apartment, office).""" + + city_name: Optional[str] = FieldInfo(alias="cityName", default=None) + """The full city name.""" + + city_secondary: Optional[str] = FieldInfo(alias="citySecondary", default=None) + """Secondary city information.""" + + city_type: Optional[str] = FieldInfo(alias="cityType", default=None) + """The type of city (e.g., city, town, village).""" + + delivery_address: Optional[str] = FieldInfo(alias="deliveryAddress", default=None) + """The full delivery address.""" + + dependent_locality: Optional[str] = FieldInfo(alias="dependentLocality", default=None) + """The dependent locality (UK addresses).""" + + double_dependent_locality: Optional[str] = FieldInfo(alias="doubleDependentLocality", default=None) + """The double dependent locality (UK addresses).""" + + organization: Optional[str] = None + """The organization or company name.""" + + postal_or_zip_primary: Optional[str] = FieldInfo(alias="postalOrZipPrimary", default=None) + """The primary part of the postal or ZIP code.""" + + postal_or_zip_secondary: Optional[str] = FieldInfo(alias="postalOrZipSecondary", default=None) + """The secondary part of the postal or ZIP code.""" + + post_box: Optional[str] = FieldInfo(alias="postBox", default=None) + """The post box number.""" + + premise: Optional[str] = None + """The premise name or number.""" + + premise_number: Optional[str] = FieldInfo(alias="premiseNumber", default=None) + """The premise number.""" + + premise_secondary: Optional[str] = FieldInfo(alias="premiseSecondary", default=None) + """Secondary premise information.""" + + premise_type: Optional[str] = FieldInfo(alias="premiseType", default=None) + """The type of premise (e.g., house, flat).""" + + province_or_state_name: Optional[str] = FieldInfo(alias="provinceOrStateName", default=None) + """The full name of the province or state.""" + + province_or_state_type: Optional[str] = FieldInfo(alias="provinceOrStateType", default=None) + """The type of province or state (e.g., province, state, region).""" + + street: Optional[str] = None + """The street name.""" + + street_post_direction: Optional[str] = FieldInfo(alias="streetPostDirection", default=None) + """The directional suffix for the street (e.g., N, S, E, W).""" + + street_pre_direction: Optional[str] = FieldInfo(alias="streetPreDirection", default=None) + """The directional prefix for the street (e.g., N, S, E, W).""" + + street_type: Optional[str] = FieldInfo(alias="streetType", default=None) + """The type of street (e.g., St, Ave, Blvd).""" + + sub_administrative_area: Optional[str] = FieldInfo(alias="subAdministrativeArea", default=None) + """The sub-administrative area.""" + + sub_building: Optional[str] = FieldInfo(alias="subBuilding", default=None) + """The sub-building name or number (e.g., unit, suite).""" + + sub_building_floor: Optional[str] = FieldInfo(alias="SubBuildingFloor", default=None) + """The floor of the sub-building.""" + + sub_building_number: Optional[str] = FieldInfo(alias="subBuildingNumber", default=None) + """The sub-building number.""" + + sub_building_type: Optional[str] = FieldInfo(alias="subBuildingType", default=None) + """The type of sub-building (e.g., floor, wing).""" + + sub_street: Optional[str] = FieldInfo(alias="subStreet", default=None) + """The sub-street name.""" + + sub_street_post_direction: Optional[str] = FieldInfo(alias="subStreetPostDirection", default=None) + """The directional suffix for the sub-street.""" + + sub_street_pre_direction: Optional[str] = FieldInfo(alias="subStreetPreDirection", default=None) + """The directional prefix for the sub-street.""" + + sub_street_type: Optional[str] = FieldInfo(alias="subStreetType", default=None) + """The type of sub-street.""" + + super_administrative_area: Optional[str] = FieldInfo(alias="superAdministrativeArea", default=None) + """The super-administrative area.""" + + telephone: Optional[str] = None + """The telephone number associated with the address.""" + + +class DataGeoData(BaseModel): + geo_accuracy: Optional[str] = FieldInfo(alias="geoAccuracy", default=None) + """The geocode accuracy.""" + + latitude: Optional[str] = None + """The latitude of the address.""" + + longitude: Optional[str] = None + """The longitude of the address.""" + + +class DataSummary(BaseModel): + context_identification_match_level: Optional[str] = FieldInfo(alias="contextIdentificationMatchLevel", default=None) + """Context identification match level.""" + + lexicon_identification_match_level: Optional[str] = FieldInfo(alias="lexiconIdentificationMatchLevel", default=None) + """Lexicon identification match level.""" + + match_score: Optional[float] = FieldInfo(alias="matchScore", default=None) + """The match score (0-100).""" + + message: Optional[str] = None + """Additional message about the verification.""" + + parsing_status: Optional[str] = FieldInfo(alias="parsingStatus", default=None) + """The parsing status of the address.""" + + post_code_status: Optional[str] = FieldInfo(alias="postCodeStatus", default=None) + """The status of the postal code.""" + + post_processed_verification_match_level: Optional[str] = FieldInfo( + alias="postProcessedVerificationMatchLevel", default=None + ) + """The match level after post-processing.""" + + pre_processed_verification_match_level: Optional[str] = FieldInfo( + alias="preProcessedVerificationMatchLevel", default=None + ) + """The match level before post-processing.""" + + verification_status: Optional[str] = FieldInfo(alias="verificationStatus", default=None) + """The overall verification status.""" + + +class Data(BaseModel): + city: str + """The city or locality.""" + + country: str + """The country code (ISO 3166-1 alpha-2).""" + + line1: str + """The first address line.""" + + postal_or_zip: str = FieldInfo(alias="postalOrZip") + """The postal or ZIP code.""" + + province_or_state: str = FieldInfo(alias="provinceOrState") + """The province or state.""" + + country_name: Optional[str] = FieldInfo(alias="countryName", default=None) + """The full country name.""" + + details: Optional[DataDetails] = None + """ + Additional details about the verified address, such as premise, thoroughfare, + and locality. + """ + + errors: Optional[Errors] = None + """Errors encountered during address verification.""" + + firm_name: Optional[str] = FieldInfo(alias="firmName", default=None) + """The firm or company name, if available.""" + + formatted_address: Optional[str] = FieldInfo(alias="formattedAddress", default=None) + """The formatted address string.""" + + geo_data: Optional[DataGeoData] = FieldInfo(alias="geoData", default=None) + """Geocoding result for the verified address.""" + + line2: Optional[str] = None + """The second address line.""" + + line3: Optional[str] = None + """The third address line, if available.""" + + status: Optional[Status] = None + """The verification status of an address.""" + + summary: Optional[DataSummary] = None + """A summary of the verification process and match levels.""" + + zip_plus4: Optional[str] = FieldInfo(alias="zipPlus4", default=None) + """The ZIP+4 code (for US addresses).""" + + +class IntlAddressVerificationVerifyResponse(BaseModel): + data: Data + """The result of a verified international address.""" + + message: str + + status: Literal["success", "error"] diff --git a/src/postgrid/types/print_mail/__init__.py b/src/postgrid/types/print_mail/__init__.py new file mode 100644 index 0000000..0527402 --- /dev/null +++ b/src/postgrid/types/print_mail/__init__.py @@ -0,0 +1,90 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .box import Box as Box +from .cheque import Cheque as Cheque +from .letter import Letter as Letter +from .report import Report as Report +from .contact import Contact as Contact +from .campaign import Campaign as Campaign +from .postcard import Postcard as Postcard +from .template import Template as Template +from .file_type import FileType as FileType +from .cheque_size import ChequeSize as ChequeSize +from .letter_size import LetterSize as LetterSize +from .self_mailer import SelfMailer as SelfMailer +from .attached_pdf import AttachedPdf as AttachedPdf +from .bank_account import BankAccount as BankAccount +from .cancellation import Cancellation as Cancellation +from .digital_only import DigitalOnly as DigitalOnly +from .mailing_list import MailingList as MailingList +from .order_status import OrderStatus as OrderStatus +from .plastic_card import PlasticCard as PlasticCard +from .box_cheque_base import BoxChequeBase as BoxChequeBase +from .box_list_params import BoxListParams as BoxListParams +from .deleted_response import DeletedResponse as DeletedResponse +from .order_imb_status import OrderImbStatus as OrderImbStatus +from .sub_organization import SubOrganization as SubOrganization +from .address_placement import AddressPlacement as AddressPlacement +from .box_create_params import BoxCreateParams as BoxCreateParams +from .email_preferences import EmailPreferences as EmailPreferences +from .attached_pdf_param import AttachedPdfParam as AttachedPdfParam +from .cheque_list_params import ChequeListParams as ChequeListParams +from .digital_only_param import DigitalOnlyParam as DigitalOnlyParam +from .letter_list_params import LetterListParams as LetterListParams +from .plastic_card_param import PlasticCardParam as PlasticCardParam +from .report_list_params import ReportListParams as ReportListParams +from .contact_list_params import ContactListParams as ContactListParams +from .mailing_list_update import MailingListUpdate as MailingListUpdate +from .order_mailing_class import OrderMailingClass as OrderMailingClass +from .campaign_list_params import CampaignListParams as CampaignListParams +from .campaign_send_params import CampaignSendParams as CampaignSendParams +from .cheque_create_params import ChequeCreateParams as ChequeCreateParams +from .contact_create_param import ContactCreateParam as ContactCreateParam +from .letter_create_params import LetterCreateParams as LetterCreateParams +from .postcard_list_params import PostcardListParams as PostcardListParams +from .report_create_params import ReportCreateParams as ReportCreateParams +from .report_sample_params import ReportSampleParams as ReportSampleParams +from .report_update_params import ReportUpdateParams as ReportUpdateParams +from .template_list_params import TemplateListParams as TemplateListParams +from .box_cheque_base_param import BoxChequeBaseParam as BoxChequeBaseParam +from .contact_create_params import ContactCreateParams as ContactCreateParams +from .campaign_create_params import CampaignCreateParams as CampaignCreateParams +from .campaign_update_params import CampaignUpdateParams as CampaignUpdateParams +from .postcard_create_params import PostcardCreateParams as PostcardCreateParams +from .template_create_params import TemplateCreateParams as TemplateCreateParams +from .template_update_params import TemplateUpdateParams as TemplateUpdateParams +from .contact_delete_response import ContactDeleteResponse as ContactDeleteResponse +from .self_mailer_list_params import SelfMailerListParams as SelfMailerListParams +from .bank_account_list_params import BankAccountListParams as BankAccountListParams +from .campaign_delete_response import CampaignDeleteResponse as CampaignDeleteResponse +from .mailing_list_jobs_params import MailingListJobsParams as MailingListJobsParams +from .mailing_list_list_params import MailingListListParams as MailingListListParams +from .template_delete_response import TemplateDeleteResponse as TemplateDeleteResponse +from .bank_account_country_code import BankAccountCountryCode as BankAccountCountryCode +from .self_mailer_create_params import SelfMailerCreateParams as SelfMailerCreateParams +from .verification_status_count import VerificationStatusCount as VerificationStatusCount +from .bank_account_create_params import BankAccountCreateParams as BankAccountCreateParams +from .mailing_list_create_params import MailingListCreateParams as MailingListCreateParams +from .mailing_list_update_params import MailingListUpdateParams as MailingListUpdateParams +from .bank_account_delete_response import BankAccountDeleteResponse as BankAccountDeleteResponse +from .cheque_retrieve_url_response import ChequeRetrieveURLResponse as ChequeRetrieveURLResponse +from .letter_retrieve_url_response import LetterRetrieveURLResponse as LetterRetrieveURLResponse +from .mailing_list_delete_response import MailingListDeleteResponse as MailingListDeleteResponse +from .mailing_list_import_response import MailingListImportResponse as MailingListImportResponse +from .sub_organization_list_params import SubOrganizationListParams as SubOrganizationListParams +from .postcard_retrieve_url_response import PostcardRetrieveURLResponse as PostcardRetrieveURLResponse +from .sub_organization_update_params import SubOrganizationUpdateParams as SubOrganizationUpdateParams +from .mailing_list_import_list_params import MailingListImportListParams as MailingListImportListParams +from .sub_organization_update_response import SubOrganizationUpdateResponse as SubOrganizationUpdateResponse +from .mailing_list_import_create_params import MailingListImportCreateParams as MailingListImportCreateParams +from .mailing_list_import_update_params import MailingListImportUpdateParams as MailingListImportUpdateParams +from .self_mailer_retrieve_url_response import SelfMailerRetrieveURLResponse as SelfMailerRetrieveURLResponse +from .mailing_list_import_delete_response import MailingListImportDeleteResponse as MailingListImportDeleteResponse +from .sub_organization_retrieve_users_params import ( + SubOrganizationRetrieveUsersParams as SubOrganizationRetrieveUsersParams, +) +from .sub_organization_retrieve_users_response import ( + SubOrganizationRetrieveUsersResponse as SubOrganizationRetrieveUsersResponse, +) diff --git a/src/postgrid/types/print_mail/address_placement.py b/src/postgrid/types/print_mail/address_placement.py new file mode 100644 index 0000000..0744e98 --- /dev/null +++ b/src/postgrid/types/print_mail/address_placement.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["AddressPlacement"] + +AddressPlacement: TypeAlias = Literal["top_first_page", "insert_blank_page"] diff --git a/src/postgrid/types/print_mail/attached_pdf.py b/src/postgrid/types/print_mail/attached_pdf.py new file mode 100644 index 0000000..4f3090a --- /dev/null +++ b/src/postgrid/types/print_mail/attached_pdf.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["AttachedPdf"] + + +class AttachedPdf(BaseModel): + file: str + """The file (multipart form upload) or URL pointing to a PDF for the attached PDF.""" + + placement: Literal["before_template", "after_template"] + """Enum representing the placement of the attached PDF.""" diff --git a/src/postgrid/types/print_mail/attached_pdf_param.py b/src/postgrid/types/print_mail/attached_pdf_param.py new file mode 100644 index 0000000..ddd40b2 --- /dev/null +++ b/src/postgrid/types/print_mail/attached_pdf_param.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, TypedDict + +__all__ = ["AttachedPdfParam"] + + +class AttachedPdfParam(TypedDict, total=False): + file: Required[str] + """The file (multipart form upload) or URL pointing to a PDF for the attached PDF.""" + + placement: Required[Literal["before_template", "after_template"]] + """Enum representing the placement of the attached PDF.""" diff --git a/src/postgrid/types/print_mail/bank_account.py b/src/postgrid/types/print_mail/bank_account.py new file mode 100644 index 0000000..0e34700 --- /dev/null +++ b/src/postgrid/types/print_mail/bank_account.py @@ -0,0 +1,82 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import builtins +from typing import Dict, Optional +from datetime import datetime +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from ..._models import BaseModel +from .bank_account_country_code import BankAccountCountryCode + +__all__ = ["BankAccount"] + + +class BankAccount(BaseModel): + id: str + """A unique ID prefixed with bank*account*""" + + account_number: str = FieldInfo(alias="accountNumber") + """The account number of the bank account.""" + + bank_country_code: BankAccountCountryCode = FieldInfo(alias="bankCountryCode") + """Countries typically have different bank account formats and standards. + + These are the countries which PostGrid's bank accounts API supports. + """ + + bank_name: str = FieldInfo(alias="bankName") + """The name of the bank.""" + + created_at: datetime = FieldInfo(alias="createdAt") + """The UTC time at which this resource was created.""" + + live: bool + """`true` if this is a live mode resource else `false`.""" + + object: Literal["bank_account"] + """Always `bank_account`.""" + + updated_at: datetime = FieldInfo(alias="updatedAt") + """The UTC time at which this resource was last updated.""" + + bank_primary_line: Optional[str] = FieldInfo(alias="bankPrimaryLine", default=None) + """The primary address line of the bank.""" + + bank_secondary_line: Optional[str] = FieldInfo(alias="bankSecondaryLine", default=None) + """The secondary address line of the bank.""" + + ca_designation_number: Optional[str] = FieldInfo(alias="caDesignationNumber", default=None) + """The designation number of the bank account (for CA).""" + + description: Optional[str] = None + """An optional string describing this resource. + + Will be visible in the API and the dashboard. + """ + + metadata: Optional[Dict[str, builtins.object]] = None + """See the section on Metadata.""" + + route_number: Optional[str] = FieldInfo(alias="routeNumber", default=None) + """The route number of the bank account (for CA).""" + + routing_number: Optional[str] = FieldInfo(alias="routingNumber", default=None) + """The routing number of the bank account (for US).""" + + signature_image: Optional[str] = FieldInfo(alias="signatureImage", default=None) + """A signed link to the signature image uploaded when this bank account was + created. + + This is omitted if `signatureText` is present. + """ + + signature_text: Optional[str] = FieldInfo(alias="signatureText", default=None) + """ + The signature text PostGrid uses to generate a signature for cheques created + using this bank account. This is omitted if `signatureImage` is present. + """ + + transit_number: Optional[str] = FieldInfo(alias="transitNumber", default=None) + """The transit number of the bank account (for CA).""" diff --git a/src/postgrid/types/print_mail/bank_account_country_code.py b/src/postgrid/types/print_mail/bank_account_country_code.py new file mode 100644 index 0000000..395a7b1 --- /dev/null +++ b/src/postgrid/types/print_mail/bank_account_country_code.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["BankAccountCountryCode"] + +BankAccountCountryCode: TypeAlias = Literal["CA", "US"] diff --git a/src/postgrid/types/print_mail/bank_account_create_params.py b/src/postgrid/types/print_mail/bank_account_create_params.py new file mode 100644 index 0000000..100d91a --- /dev/null +++ b/src/postgrid/types/print_mail/bank_account_create_params.py @@ -0,0 +1,162 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union +from typing_extensions import Required, Annotated, TypeAlias, TypedDict + +from ..._types import Base64FileInput +from ..._utils import PropertyInfo +from .bank_account_country_code import BankAccountCountryCode + +__all__ = [ + "BankAccountCreateParams", + "BankAccountCreateSignatureText", + "BankAccountCreateSignatureImageURL", + "BankAccountCreateSignatureImageFile", +] + + +class BankAccountCreateSignatureText(TypedDict, total=False): + account_number: Required[Annotated[str, PropertyInfo(alias="accountNumber")]] + """The account number of the bank account.""" + + bank_country_code: Required[Annotated[BankAccountCountryCode, PropertyInfo(alias="bankCountryCode")]] + """Countries typically have different bank account formats and standards. + + These are the countries which PostGrid's bank accounts API supports. + """ + + bank_name: Required[Annotated[str, PropertyInfo(alias="bankName")]] + """The name of the bank.""" + + signature_text: Required[Annotated[str, PropertyInfo(alias="signatureText")]] + """The signature text of the bank account.""" + + bank_primary_line: Annotated[str, PropertyInfo(alias="bankPrimaryLine")] + """The primary address line of the bank.""" + + bank_secondary_line: Annotated[str, PropertyInfo(alias="bankSecondaryLine")] + """The secondary address line of the bank.""" + + ca_designation_number: Annotated[str, PropertyInfo(alias="caDesignationNumber")] + """The designation number of the bank account (for CA).""" + + description: str + """An optional string describing this resource. + + Will be visible in the API and the dashboard. + """ + + metadata: Dict[str, object] + """See the section on Metadata.""" + + route_number: Annotated[str, PropertyInfo(alias="routeNumber")] + """The route number of the bank account (for CA).""" + + routing_number: Annotated[str, PropertyInfo(alias="routingNumber")] + """The routing number of the bank account (for US).""" + + transit_number: Annotated[str, PropertyInfo(alias="transitNumber")] + """The transit number of the bank account (for CA).""" + + +class BankAccountCreateSignatureImageURL(TypedDict, total=False): + account_number: Required[Annotated[str, PropertyInfo(alias="accountNumber")]] + """The account number of the bank account.""" + + bank_country_code: Required[Annotated[BankAccountCountryCode, PropertyInfo(alias="bankCountryCode")]] + """Countries typically have different bank account formats and standards. + + These are the countries which PostGrid's bank accounts API supports. + """ + + bank_name: Required[Annotated[str, PropertyInfo(alias="bankName")]] + """The name of the bank.""" + + signature_image: Required[Annotated[str, PropertyInfo(alias="signatureImage")]] + """ + Link to signature image which PostGrid will download and apply to cheques + created with this bank account. + """ + + bank_primary_line: Annotated[str, PropertyInfo(alias="bankPrimaryLine")] + """The primary address line of the bank.""" + + bank_secondary_line: Annotated[str, PropertyInfo(alias="bankSecondaryLine")] + """The secondary address line of the bank.""" + + ca_designation_number: Annotated[str, PropertyInfo(alias="caDesignationNumber")] + """The designation number of the bank account (for CA).""" + + description: str + """An optional string describing this resource. + + Will be visible in the API and the dashboard. + """ + + metadata: Dict[str, object] + """See the section on Metadata.""" + + route_number: Annotated[str, PropertyInfo(alias="routeNumber")] + """The route number of the bank account (for CA).""" + + routing_number: Annotated[str, PropertyInfo(alias="routingNumber")] + """The routing number of the bank account (for US).""" + + transit_number: Annotated[str, PropertyInfo(alias="transitNumber")] + """The transit number of the bank account (for CA).""" + + +class BankAccountCreateSignatureImageFile(TypedDict, total=False): + account_number: Required[Annotated[str, PropertyInfo(alias="accountNumber")]] + """The account number of the bank account.""" + + bank_country_code: Required[Annotated[BankAccountCountryCode, PropertyInfo(alias="bankCountryCode")]] + """Countries typically have different bank account formats and standards. + + These are the countries which PostGrid's bank accounts API supports. + """ + + bank_name: Required[Annotated[str, PropertyInfo(alias="bankName")]] + """The name of the bank.""" + + signature_image: Required[ + Annotated[Union[str, Base64FileInput], PropertyInfo(alias="signatureImage", format="base64")] + ] + """ + A PNG or JPEG file which PostGrid will apply to checks created with this bank + account. + """ + + bank_primary_line: Annotated[str, PropertyInfo(alias="bankPrimaryLine")] + """The primary address line of the bank.""" + + bank_secondary_line: Annotated[str, PropertyInfo(alias="bankSecondaryLine")] + """The secondary address line of the bank.""" + + ca_designation_number: Annotated[str, PropertyInfo(alias="caDesignationNumber")] + """The designation number of the bank account (for CA).""" + + description: str + """An optional string describing this resource. + + Will be visible in the API and the dashboard. + """ + + metadata: Dict[str, object] + """See the section on Metadata.""" + + route_number: Annotated[str, PropertyInfo(alias="routeNumber")] + """The route number of the bank account (for CA).""" + + routing_number: Annotated[str, PropertyInfo(alias="routingNumber")] + """The routing number of the bank account (for US).""" + + transit_number: Annotated[str, PropertyInfo(alias="transitNumber")] + """The transit number of the bank account (for CA).""" + + +BankAccountCreateParams: TypeAlias = Union[ + BankAccountCreateSignatureText, BankAccountCreateSignatureImageURL, BankAccountCreateSignatureImageFile +] diff --git a/src/postgrid/types/print_mail/bank_account_delete_response.py b/src/postgrid/types/print_mail/bank_account_delete_response.py new file mode 100644 index 0000000..bada596 --- /dev/null +++ b/src/postgrid/types/print_mail/bank_account_delete_response.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["BankAccountDeleteResponse"] + + +class BankAccountDeleteResponse(BaseModel): + id: str + """A unique ID prefixed with bank*account*""" + + deleted: Literal[True] + + object: Literal["bank_account"] + """Always `bank_account`.""" diff --git a/src/postgrid/types/print_mail/bank_account_list_params.py b/src/postgrid/types/print_mail/bank_account_list_params.py new file mode 100644 index 0000000..92e24ac --- /dev/null +++ b/src/postgrid/types/print_mail/bank_account_list_params.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["BankAccountListParams"] + + +class BankAccountListParams(TypedDict, total=False): + limit: int + + search: str + """You can supply any string to help narrow down the list of resources. + + For example, if you pass `"New York"` (quoted), it will return resources that + have that string present somewhere in their response. Alternatively, you can + supply a structured search query. See the documentation on + `StructuredSearchQuery` for more details. + """ + + skip: int diff --git a/src/postgrid/types/print_mail/box.py b/src/postgrid/types/print_mail/box.py new file mode 100644 index 0000000..1086351 --- /dev/null +++ b/src/postgrid/types/print_mail/box.py @@ -0,0 +1,134 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import builtins +from typing import Dict, List, Optional +from datetime import datetime +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from .contact import Contact +from ..._models import BaseModel +from .cancellation import Cancellation +from .order_status import OrderStatus +from .box_cheque_base import BoxChequeBase +from .order_imb_status import OrderImbStatus +from .order_mailing_class import OrderMailingClass + +__all__ = ["Box", "Cheque"] + + +class Cheque(BoxChequeBase): + from_: Contact = FieldInfo(alias="from") + + to: Contact + + +class Box(BaseModel): + id: str + """A unique ID prefixed with box\\__""" + + cheques: List[Cheque] + """The cheques inside this box (in read mode).""" + + created_at: datetime = FieldInfo(alias="createdAt") + """The UTC time at which this resource was created.""" + + from_: Contact = FieldInfo(alias="from") + """ + The contact of the 'from' field in read mode should be a fully expanded Contact. + """ + + live: bool + """`true` if this is a live mode resource else `false`.""" + + mailing_class: OrderMailingClass = FieldInfo(alias="mailingClass") + """The mailing class of this order. + + This determines the speed and cost of delivery. See `OrderMailingClass` for more + details. + """ + + object: Literal["box"] + """Always "box".""" + + send_date: datetime = FieldInfo(alias="sendDate") + """This order will transition from `ready` to `printing` on the day after this + date. + + For example, if this is a date on Tuesday, the order will transition to + `printing` on Wednesday at midnight eastern time. + """ + + status: OrderStatus + """See `OrderStatus` for more details on the status of this order.""" + + to: Contact + """The recipient of this order. + + This will be provided even if you delete the underlying contact. + """ + + updated_at: datetime = FieldInfo(alias="updatedAt") + """The UTC time at which this resource was last updated.""" + + cancellation: Optional[Cancellation] = None + """The cancellation details of this order. + + Populated if the order has been cancelled. + """ + + description: Optional[str] = None + """An optional string describing this resource. + + Will be visible in the API and the dashboard. + """ + + imb_date: Optional[datetime] = FieldInfo(alias="imbDate", default=None) + """The last date that the IMB status was updated. + + See `imbStatus` for more details. + """ + + imb_status: Optional[OrderImbStatus] = FieldInfo(alias="imbStatus", default=None) + """The Intelligent Mail Barcode (IMB) status of this order. + + Only populated for US-printed and US-destined orders. This is the most detailed + way to track non-express/certified orders. + """ + + imb_zip_code: Optional[str] = FieldInfo(alias="imbZIPCode", default=None) + """ + The most recent ZIP code of the USPS facility that the order has been processed + through. Only populated when an `imbStatus` is present. + """ + + merge_variables: Optional[Dict[str, builtins.object]] = FieldInfo(alias="mergeVariables", default=None) + """ + These will be merged with the variables in the template or HTML you create this + order with. The keys in this object should match the variable names in the + template _exactly_ as they are case-sensitive. Note that these _do not_ apply to + PDFs uploaded with the order. + """ + + metadata: Optional[Dict[str, builtins.object]] = None + """See the section on Metadata.""" + + tracking_number: Optional[str] = FieldInfo(alias="trackingNumber", default=None) + """The tracking number of this order. + + Populated after an express/certified order has been processed for delivery. + """ + + url: Optional[str] = None + """PostGrid renders a PDF preview for all orders. + + This should be inspected to ensure that the order is correct before it is sent + out because it shows what will be printed and mailed to the recipient. Once the + PDF preview is generated, this field will be returned by all `GET` endpoints + which produce this order. + + This URL is a signed link to the PDF preview. It will expire after a short + period of time. If you need to access this URL after it has expired, you can + regenerate it by calling the `GET` endpoint again. + """ diff --git a/src/postgrid/types/print_mail/box_cheque_base.py b/src/postgrid/types/print_mail/box_cheque_base.py new file mode 100644 index 0000000..c21aeb5 --- /dev/null +++ b/src/postgrid/types/print_mail/box_cheque_base.py @@ -0,0 +1,35 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, Optional + +from pydantic import Field as FieldInfo + +from ..._models import BaseModel + +__all__ = ["BoxChequeBase"] + + +class BoxChequeBase(BaseModel): + amount: int + """The amount on the cheque.""" + + bank_account: str = FieldInfo(alias="bankAccount") + """The bank account (ID or reference) from which the cheque amount is drawn.""" + + number: int + """The cheque number.""" + + logo_url: Optional[str] = FieldInfo(alias="logoURL", default=None) + """A URL to a logo for the cheque (optional).""" + + memo: Optional[str] = None + """The memo text on the cheque (optional).""" + + merge_variables: Optional[Dict[str, object]] = FieldInfo(alias="mergeVariables", default=None) + """ + A set of dynamic merge variables for customizing the cheque or accompanying + documents (optional). + """ + + message_template: Optional[str] = FieldInfo(alias="messageTemplate", default=None) + """An optional message template to be printed on or with the cheque.""" diff --git a/src/postgrid/types/print_mail/box_cheque_base_param.py b/src/postgrid/types/print_mail/box_cheque_base_param.py new file mode 100644 index 0000000..6ff0df6 --- /dev/null +++ b/src/postgrid/types/print_mail/box_cheque_base_param.py @@ -0,0 +1,36 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict +from typing_extensions import Required, Annotated, TypedDict + +from ..._utils import PropertyInfo + +__all__ = ["BoxChequeBaseParam"] + + +class BoxChequeBaseParam(TypedDict, total=False): + amount: Required[int] + """The amount on the cheque.""" + + bank_account: Required[Annotated[str, PropertyInfo(alias="bankAccount")]] + """The bank account (ID or reference) from which the cheque amount is drawn.""" + + number: Required[int] + """The cheque number.""" + + logo_url: Annotated[str, PropertyInfo(alias="logoURL")] + """A URL to a logo for the cheque (optional).""" + + memo: str + """The memo text on the cheque (optional).""" + + merge_variables: Annotated[Dict[str, object], PropertyInfo(alias="mergeVariables")] + """ + A set of dynamic merge variables for customizing the cheque or accompanying + documents (optional). + """ + + message_template: Annotated[str, PropertyInfo(alias="messageTemplate")] + """An optional message template to be printed on or with the cheque.""" diff --git a/src/postgrid/types/print_mail/box_create_params.py b/src/postgrid/types/print_mail/box_create_params.py new file mode 100644 index 0000000..76e2f0e --- /dev/null +++ b/src/postgrid/types/print_mail/box_create_params.py @@ -0,0 +1,81 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union, Iterable +from datetime import datetime +from typing_extensions import Required, Annotated, TypeAlias, TypedDict + +from ..._utils import PropertyInfo +from .order_mailing_class import OrderMailingClass +from .box_cheque_base_param import BoxChequeBaseParam +from ..contact_create_with_first_name_param import ContactCreateWithFirstNameParam +from ..contact_create_with_company_name_param import ContactCreateWithCompanyNameParam + +__all__ = ["BoxCreateParams", "Cheque", "ChequeFrom", "ChequeTo", "From", "To"] + + +class BoxCreateParams(TypedDict, total=False): + cheques: Required[Iterable[Cheque]] + """The cheques to be mailed in the box. + + Only 100 cheques can be included in a box at a time. + """ + + from_: Required[Annotated[From, PropertyInfo(alias="from")]] + """The 'from' (sender) of the entire box. + + Accepts inline ContactCreate or a contactID. + """ + + to: Required[To] + """The recipient of this order. + + You can either supply the contact information inline here or provide a contact + ID. PostGrid will automatically deduplicate contacts regardless of whether you + provide the information inline here or call the contact creation endpoint. + """ + + description: str + """An optional string describing this resource. + + Will be visible in the API and the dashboard. + """ + + mailing_class: Annotated[OrderMailingClass, PropertyInfo(alias="mailingClass")] + """The mailing class of this order. + + If not provided, automatically set to `first_class`. + """ + + merge_variables: Annotated[Dict[str, object], PropertyInfo(alias="mergeVariables")] + """ + These will be merged with the variables in the template or HTML you create this + order with. The keys in this object should match the variable names in the + template _exactly_ as they are case-sensitive. Note that these _do not_ apply to + PDFs uploaded with the order. + """ + + metadata: Dict[str, object] + """See the section on Metadata.""" + + send_date: Annotated[Union[str, datetime], PropertyInfo(alias="sendDate", format="iso8601")] + """This order will transition from `ready` to `printing` on the day after this + date. + + You can use this parameter to schedule orders for a future date. + """ + + +ChequeFrom: TypeAlias = Union[ContactCreateWithFirstNameParam, ContactCreateWithCompanyNameParam, str] + +ChequeTo: TypeAlias = Union[ContactCreateWithFirstNameParam, ContactCreateWithCompanyNameParam, str] + + +class Cheque(BoxChequeBaseParam): + to: Required[ChequeTo] + + +From: TypeAlias = Union[ContactCreateWithFirstNameParam, ContactCreateWithCompanyNameParam, str] + +To: TypeAlias = Union[ContactCreateWithFirstNameParam, ContactCreateWithCompanyNameParam, str] diff --git a/src/postgrid/types/print_mail/box_list_params.py b/src/postgrid/types/print_mail/box_list_params.py new file mode 100644 index 0000000..9832ccc --- /dev/null +++ b/src/postgrid/types/print_mail/box_list_params.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["BoxListParams"] + + +class BoxListParams(TypedDict, total=False): + limit: int + + search: str + """You can supply any string to help narrow down the list of resources. + + For example, if you pass `"New York"` (quoted), it will return resources that + have that string present somewhere in their response. Alternatively, you can + supply a structured search query. See the documentation on + `StructuredSearchQuery` for more details. + """ + + skip: int diff --git a/src/postgrid/types/print_mail/campaign.py b/src/postgrid/types/print_mail/campaign.py new file mode 100644 index 0000000..311c1f0 --- /dev/null +++ b/src/postgrid/types/print_mail/campaign.py @@ -0,0 +1,92 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List, Optional +from datetime import datetime +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from ..._models import BaseModel + +__all__ = ["Campaign", "Error"] + + +class Error(BaseModel): + message: str + """A human-readable message describing the error.""" + + type: Literal["processing_error", "internal_error"] + """Type of error encountered during campaign processing.""" + + +class Campaign(BaseModel): + id: str + """A unique ID prefixed with campaign\\__""" + + created_at: datetime = FieldInfo(alias="createdAt") + """The UTC time at which this resource was created.""" + + created_count: int = FieldInfo(alias="createdCount") + """The number of orders successfully created for this campaign.""" + + live: bool + """`true` if this is a live mode resource else `false`.""" + + mailing_list: str = FieldInfo(alias="mailingList") + """The ID of the mailing list associated with this campaign.""" + + status: Literal[ + "drafting", "changes_required", "creating_orders", "draft", "ready", "printing", "processed_for_delivery" + ] + """Status of the campaign lifecycle.""" + + updated_at: datetime = FieldInfo(alias="updatedAt") + """The UTC time at which this resource was last updated.""" + + cheque_profile: Optional[str] = FieldInfo(alias="chequeProfile", default=None) + """The ID of the cheque profile used for this campaign, if applicable.""" + + default_sender_contact: Optional[str] = FieldInfo(alias="defaultSenderContact", default=None) + """ + The ID of the default sender contact to use for orders if not specified per + recipient. + """ + + description: Optional[str] = None + """An optional string describing this resource. + + Will be visible in the API and the dashboard. + """ + + errors: Optional[List[Error]] = None + """A list of processing errors encountered, if any. + + Present when status is 'changes_required'. + """ + + letter_profile: Optional[str] = FieldInfo(alias="letterProfile", default=None) + """The ID of the letter profile used for this campaign, if applicable.""" + + metadata: Optional[Dict[str, object]] = None + """See the section on Metadata.""" + + order_preview_url: Optional[str] = FieldInfo(alias="orderPreviewURL", default=None) + """ + A temporary URL to preview the first rendered order, available once the campaign + status is 'draft' or later. + """ + + postcard_profile: Optional[str] = FieldInfo(alias="postcardProfile", default=None) + """The ID of the postcard profile used for this campaign, if applicable.""" + + report_url: Optional[str] = FieldInfo(alias="reportURL", default=None) + """ + A temporary URL to download the processing report, available once the campaign + is in the `ready` status. + """ + + self_mailer_profile: Optional[str] = FieldInfo(alias="selfMailerProfile", default=None) + """The ID of the self-mailer profile used for this campaign, if applicable.""" + + send_date: Optional[datetime] = FieldInfo(alias="sendDate", default=None) + """The scheduled date and time for the campaign to be sent.""" diff --git a/src/postgrid/types/print_mail/campaign_create_params.py b/src/postgrid/types/print_mail/campaign_create_params.py new file mode 100644 index 0000000..e34bfc3 --- /dev/null +++ b/src/postgrid/types/print_mail/campaign_create_params.py @@ -0,0 +1,48 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union +from datetime import datetime +from typing_extensions import Required, Annotated, TypedDict + +from ..._utils import PropertyInfo + +__all__ = ["CampaignCreateParams"] + + +class CampaignCreateParams(TypedDict, total=False): + mailing_list: Required[Annotated[str, PropertyInfo(alias="mailingList")]] + """The ID of the mailing list associated with this campaign.""" + + cheque_profile: Annotated[str, PropertyInfo(alias="chequeProfile")] + """The ID of the cheque profile used for this campaign, if applicable.""" + + default_sender_contact: Annotated[str, PropertyInfo(alias="defaultSenderContact")] + """ + The ID of the default sender contact to use for orders if not specified per + recipient. + """ + + description: str + """An optional string describing this resource. + + Will be visible in the API and the dashboard. + """ + + letter_profile: Annotated[str, PropertyInfo(alias="letterProfile")] + """The ID of the letter profile used for this campaign, if applicable.""" + + metadata: Dict[str, object] + """See the section on Metadata.""" + + postcard_profile: Annotated[str, PropertyInfo(alias="postcardProfile")] + """The ID of the postcard profile used for this campaign, if applicable.""" + + self_mailer_profile: Annotated[str, PropertyInfo(alias="selfMailerProfile")] + """The ID of the self-mailer profile used for this campaign, if applicable.""" + + send_date: Annotated[Union[str, datetime], PropertyInfo(alias="sendDate", format="iso8601")] + """The scheduled date and time for the campaign to be sent.""" + + idempotency_key: Annotated[str, PropertyInfo(alias="idempotency-key")] diff --git a/src/postgrid/types/print_mail/campaign_delete_response.py b/src/postgrid/types/print_mail/campaign_delete_response.py new file mode 100644 index 0000000..cbd4eea --- /dev/null +++ b/src/postgrid/types/print_mail/campaign_delete_response.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["CampaignDeleteResponse"] + + +class CampaignDeleteResponse(BaseModel): + id: str + """A unique ID prefixed with campaign\\__""" + + deleted: Literal[True] diff --git a/src/postgrid/types/print_mail/campaign_list_params.py b/src/postgrid/types/print_mail/campaign_list_params.py new file mode 100644 index 0000000..34b4e43 --- /dev/null +++ b/src/postgrid/types/print_mail/campaign_list_params.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["CampaignListParams"] + + +class CampaignListParams(TypedDict, total=False): + limit: int + + search: str + """You can supply any string to help narrow down the list of resources. + + For example, if you pass `"New York"` (quoted), it will return resources that + have that string present somewhere in their response. Alternatively, you can + supply a structured search query. See the documentation on + `StructuredSearchQuery` for more details. + """ + + skip: int diff --git a/src/postgrid/types/print_mail/campaign_send_params.py b/src/postgrid/types/print_mail/campaign_send_params.py new file mode 100644 index 0000000..b446700 --- /dev/null +++ b/src/postgrid/types/print_mail/campaign_send_params.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from datetime import datetime +from typing_extensions import Annotated, TypedDict + +from ..._utils import PropertyInfo + +__all__ = ["CampaignSendParams"] + + +class CampaignSendParams(TypedDict, total=False): + send_date: Annotated[Union[Union[str, datetime], str], PropertyInfo(alias="sendDate", format="iso8601")] + """The date and time the campaign should be sent. + + Must be in the future. If omitted, defaults to the earliest possible processing + date. + """ diff --git a/src/postgrid/types/print_mail/campaign_update_params.py b/src/postgrid/types/print_mail/campaign_update_params.py new file mode 100644 index 0000000..829f066 --- /dev/null +++ b/src/postgrid/types/print_mail/campaign_update_params.py @@ -0,0 +1,54 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Optional +from typing_extensions import Annotated, TypedDict + +from ..._utils import PropertyInfo + +__all__ = ["CampaignUpdateParams"] + + +class CampaignUpdateParams(TypedDict, total=False): + cheque_profile: Annotated[Optional[str], PropertyInfo(alias="chequeProfile")] + """The ID of the cheque profile to use. + + Setting this will remove other profile types. Set to `null` to remove. + """ + + default_sender_contact: Annotated[Optional[str], PropertyInfo(alias="defaultSenderContact")] + """The ID of the default sender contact. Set to `null` to remove.""" + + description: Optional[str] + """An optional description for the campaign. + + Set to `null` to remove the existing description. + """ + + letter_profile: Annotated[Optional[str], PropertyInfo(alias="letterProfile")] + """The ID of the letter profile to use. + + Setting this will remove other profile types. Set to `null` to remove. + """ + + mailing_list: Annotated[str, PropertyInfo(alias="mailingList")] + """The ID of the mailing list to associate with this campaign.""" + + metadata: Optional[Dict[str, str]] + """Optional key-value data associated with the campaign. + + Set to `null` to remove existing metadata. + """ + + postcard_profile: Annotated[Optional[str], PropertyInfo(alias="postcardProfile")] + """The ID of the postcard profile to use. + + Setting this will remove other profile types. Set to `null` to remove. + """ + + self_mailer_profile: Annotated[Optional[str], PropertyInfo(alias="selfMailerProfile")] + """The ID of the self-mailer profile to use. + + Setting this will remove other profile types. Set to `null` to remove. + """ diff --git a/src/postgrid/types/print_mail/cancellation.py b/src/postgrid/types/print_mail/cancellation.py new file mode 100644 index 0000000..9e54979 --- /dev/null +++ b/src/postgrid/types/print_mail/cancellation.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from ..._models import BaseModel + +__all__ = ["Cancellation"] + + +class Cancellation(BaseModel): + reason: Literal["user_initiated", "invalid_content", "invalid_order_mailing_class"] + """The reason for the cancellation.""" + + cancelled_by_user: Optional[str] = FieldInfo(alias="cancelledByUser", default=None) + """The user ID who cancelled the order.""" + + note: Optional[str] = None + """An optional note provided by the user who cancelled the order.""" diff --git a/src/postgrid/types/print_mail/cheque.py b/src/postgrid/types/print_mail/cheque.py new file mode 100644 index 0000000..c2ea012 --- /dev/null +++ b/src/postgrid/types/print_mail/cheque.py @@ -0,0 +1,180 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import builtins +from typing import Dict, Union, Optional +from datetime import datetime +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from .contact import Contact +from ..._models import BaseModel +from .cheque_size import ChequeSize +from .cancellation import Cancellation +from .digital_only import DigitalOnly +from .order_status import OrderStatus +from .order_imb_status import OrderImbStatus +from .order_mailing_class import OrderMailingClass + +__all__ = ["Cheque"] + + +class Cheque(BaseModel): + id: str + """A unique ID prefixed with cheque\\__""" + + amount: int + """The amount of the cheque in cents.""" + + bank_account: str = FieldInfo(alias="bankAccount") + """The bank account (ID) associated with the cheque.""" + + created_at: datetime = FieldInfo(alias="createdAt") + """The UTC time at which this resource was created.""" + + currency_code: Literal["USD", "CAD"] = FieldInfo(alias="currencyCode") + """The currency code of the cheque. + + This can be `USD` even if drawing from a Canadian bank account and vice versa. + Defaults to the currency of the bank account country if not otherwise specified. + """ + + from_: Contact = FieldInfo(alias="from") + """The contact information of the sender.""" + + live: bool + """`true` if this is a live mode resource else `false`.""" + + mailing_class: OrderMailingClass = FieldInfo(alias="mailingClass") + """The mailing class of this order. + + This determines the speed and cost of delivery. See `OrderMailingClass` for more + details. + """ + + object: Literal["cheque"] + """Always `cheque`.""" + + send_date: datetime = FieldInfo(alias="sendDate") + """This order will transition from `ready` to `printing` on the day after this + date. + + For example, if this is a date on Tuesday, the order will transition to + `printing` on Wednesday at midnight eastern time. + """ + + size: ChequeSize + """Enum representing the supported cheque sizes.""" + + status: OrderStatus + """See `OrderStatus` for more details on the status of this order.""" + + to: Contact + """The recipient of this order. + + This will be provided even if you delete the underlying contact. + """ + + updated_at: datetime = FieldInfo(alias="updatedAt") + """The UTC time at which this resource was last updated.""" + + cancellation: Optional[Cancellation] = None + """The cancellation details of this order. + + Populated if the order has been cancelled. + """ + + deposit_ready_pdfurl: Optional[str] = FieldInfo(alias="depositReadyPDFURL", default=None) + """ + A link to the deposit-ready PDF for a digital-only cheque, returned if requested + and available. + """ + + description: Optional[str] = None + """An optional string describing this resource. + + Will be visible in the API and the dashboard. + """ + + digital_only: Optional[DigitalOnly] = FieldInfo(alias="digitalOnly", default=None) + """The digitalOnly object contains data for digital-only cheques. + + A watermark must be provided. + """ + + envelope: Union[Literal["standard"], str, None] = None + """The envelope of the cheque. + + If a custom envelope ID is not specified, defaults to `standard`. + """ + + imb_date: Optional[datetime] = FieldInfo(alias="imbDate", default=None) + """The last date that the IMB status was updated. + + See `imbStatus` for more details. + """ + + imb_status: Optional[OrderImbStatus] = FieldInfo(alias="imbStatus", default=None) + """The Intelligent Mail Barcode (IMB) status of this order. + + Only populated for US-printed and US-destined orders. This is the most detailed + way to track non-express/certified orders. + """ + + imb_zip_code: Optional[str] = FieldInfo(alias="imbZIPCode", default=None) + """ + The most recent ZIP code of the USPS facility that the order has been processed + through. Only populated when an `imbStatus` is present. + """ + + logo_url: Optional[str] = FieldInfo(alias="logoURL", default=None) + """An optional logo URL for the cheque. + + This will be placed next to the recipient address at the top left corner of the + cheque. This needs to be a public link to an image file (e.g. a PNG or JPEG + file). + """ + + memo: Optional[str] = None + """The memo of the cheque.""" + + merge_variables: Optional[Dict[str, builtins.object]] = FieldInfo(alias="mergeVariables", default=None) + """ + These will be merged with the variables in the template or HTML you create this + order with. The keys in this object should match the variable names in the + template _exactly_ as they are case-sensitive. Note that these _do not_ apply to + PDFs uploaded with the order. + """ + + message: Optional[str] = None + """The message of the cheque.""" + + metadata: Optional[Dict[str, builtins.object]] = None + """See the section on Metadata.""" + + number: Optional[int] = None + """The number of the cheque. + + If you don't provide this, it will automatically be set to an incrementing + number starting from 1 across your entire account, ensuring that every cheque + has a unique number. + """ + + tracking_number: Optional[str] = FieldInfo(alias="trackingNumber", default=None) + """The tracking number of this order. + + Populated after an express/certified order has been processed for delivery. + """ + + url: Optional[str] = None + """PostGrid renders a PDF preview for all orders. + + This should be inspected to ensure that the order is correct before it is sent + out because it shows what will be printed and mailed to the recipient. Once the + PDF preview is generated, this field will be returned by all `GET` endpoints + which produce this order. + + This URL is a signed link to the PDF preview. It will expire after a short + period of time. If you need to access this URL after it has expired, you can + regenerate it by calling the `GET` endpoint again. + """ diff --git a/src/postgrid/types/print_mail/cheque_create_params.py b/src/postgrid/types/print_mail/cheque_create_params.py new file mode 100644 index 0000000..68d6710 --- /dev/null +++ b/src/postgrid/types/print_mail/cheque_create_params.py @@ -0,0 +1,130 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union +from datetime import datetime +from typing_extensions import Literal, Required, Annotated, TypeAlias, TypedDict + +from ..._utils import PropertyInfo +from .cheque_size import ChequeSize +from .digital_only_param import DigitalOnlyParam +from .order_mailing_class import OrderMailingClass +from ..contact_create_with_first_name_param import ContactCreateWithFirstNameParam +from ..contact_create_with_company_name_param import ContactCreateWithCompanyNameParam + +__all__ = ["ChequeCreateParams", "From", "To", "RedirectTo"] + + +class ChequeCreateParams(TypedDict, total=False): + amount: Required[int] + """The amount of the cheque in cents.""" + + bank_account: Required[Annotated[str, PropertyInfo(alias="bankAccount")]] + """The bank account (ID) associated with the cheque.""" + + from_: Required[Annotated[From, PropertyInfo(alias="from")]] + """The contact information of the sender. + + You can pass contact information inline here just like you can for the `to`. + """ + + to: Required[To] + """The recipient of this order. + + You can either supply the contact information inline here or provide a contact + ID. PostGrid will automatically deduplicate contacts regardless of whether you + provide the information inline here or call the contact creation endpoint. + """ + + currency_code: Annotated[Literal["USD", "CAD"], PropertyInfo(alias="currencyCode")] + """The currency code of the cheque. + + This will be set to the default currency of the bank account (`USD` for US bank + accounts and `CAD` for Canadian bank accounts) if not provided. You can set this + value to `USD` if you want to draw USD from a Canadian bank account or vice + versa. + """ + + description: str + """An optional string describing this resource. + + Will be visible in the API and the dashboard. + """ + + digital_only: Annotated[DigitalOnlyParam, PropertyInfo(alias="digitalOnly")] + """The digitalOnly object contains data for digital-only cheques. + + A watermark must be provided. + """ + + envelope: Union[Literal["standard"], str] + """The envelope of the cheque. + + If a custom envelope ID is not specified, defaults to `standard`. + """ + + logo_url: Annotated[str, PropertyInfo(alias="logoURL")] + """An optional logo URL for the cheque. + + This will be placed next to the recipient address at the top left corner of the + cheque. This needs to be a public link to an image file (e.g. a PNG or JPEG + file). + """ + + mailing_class: Annotated[OrderMailingClass, PropertyInfo(alias="mailingClass")] + """The mailing class of this order. + + If not provided, automatically set to `first_class`. + """ + + memo: str + """The memo of the cheque.""" + + merge_variables: Annotated[Dict[str, object], PropertyInfo(alias="mergeVariables")] + """ + These will be merged with the variables in the template or HTML you create this + order with. The keys in this object should match the variable names in the + template _exactly_ as they are case-sensitive. Note that these _do not_ apply to + PDFs uploaded with the order. + """ + + message: str + """The message of the cheque.""" + + metadata: Dict[str, object] + """See the section on Metadata.""" + + number: int + """The number of the cheque. + + If you don't provide this, it will automatically be set to an incrementing + number starting from 1 across your entire account, ensuring that every cheque + has a unique number. + """ + + redirect_to: Annotated[RedirectTo, PropertyInfo(alias="redirectTo")] + """ + Providing this inserts a blank page at the start of the cheque with the + recipient you provide here. This leaves the cheque that follows intact, which + means you can use this to intercept at cheque at the redirected address and then + mail it forward to the final recipient yourself. One use case for this is + signing cheques at your office before mailing them out yourself. + """ + + send_date: Annotated[Union[str, datetime], PropertyInfo(alias="sendDate", format="iso8601")] + """This order will transition from `ready` to `printing` on the day after this + date. + + You can use this parameter to schedule orders for a future date. + """ + + size: ChequeSize + """Enum representing the supported cheque sizes.""" + + +From: TypeAlias = Union[ContactCreateWithFirstNameParam, ContactCreateWithCompanyNameParam, str] + +To: TypeAlias = Union[ContactCreateWithFirstNameParam, ContactCreateWithCompanyNameParam, str] + +RedirectTo: TypeAlias = Union[ContactCreateWithFirstNameParam, ContactCreateWithCompanyNameParam, str] diff --git a/src/postgrid/types/print_mail/cheque_list_params.py b/src/postgrid/types/print_mail/cheque_list_params.py new file mode 100644 index 0000000..394ef15 --- /dev/null +++ b/src/postgrid/types/print_mail/cheque_list_params.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["ChequeListParams"] + + +class ChequeListParams(TypedDict, total=False): + limit: int + + search: str + """You can supply any string to help narrow down the list of resources. + + For example, if you pass `"New York"` (quoted), it will return resources that + have that string present somewhere in their response. Alternatively, you can + supply a structured search query. See the documentation on + `StructuredSearchQuery` for more details. + """ + + skip: int diff --git a/src/postgrid/types/print_mail/cheque_retrieve_url_response.py b/src/postgrid/types/print_mail/cheque_retrieve_url_response.py new file mode 100644 index 0000000..dfe3a4a --- /dev/null +++ b/src/postgrid/types/print_mail/cheque_retrieve_url_response.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + + +from ..._models import BaseModel + +__all__ = ["ChequeRetrieveURLResponse"] + + +class ChequeRetrieveURLResponse(BaseModel): + id: str + """A unique ID prefixed with cheque\\__""" + + object: str + + url: str + """A signed URL linking to the order preview PDF. + + The link remains valid for 15 minutes from the time of the API call. + """ diff --git a/src/postgrid/types/print_mail/cheque_size.py b/src/postgrid/types/print_mail/cheque_size.py new file mode 100644 index 0000000..22590f5 --- /dev/null +++ b/src/postgrid/types/print_mail/cheque_size.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["ChequeSize"] + +ChequeSize: TypeAlias = Literal["us_letter", "us_legal"] diff --git a/src/postgrid/types/print_mail/contact.py b/src/postgrid/types/print_mail/contact.py new file mode 100644 index 0000000..aa21c16 --- /dev/null +++ b/src/postgrid/types/print_mail/contact.py @@ -0,0 +1,95 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import builtins +from typing import Dict, Optional +from datetime import datetime +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from ..._models import BaseModel + +__all__ = ["Contact"] + + +class Contact(BaseModel): + id: str + """A unique ID prefixed with contact\\__""" + + address_line1: str = FieldInfo(alias="addressLine1") + """The first line of the contact's address.""" + + address_status: Literal["verified", "corrected", "failed"] = FieldInfo(alias="addressStatus") + """One of `verified`, `corrected`, or `failed`.""" + + country_code: str = FieldInfo(alias="countryCode") + """The ISO 3611-1 country code of the contact's address.""" + + created_at: datetime = FieldInfo(alias="createdAt") + """The UTC time at which this resource was created.""" + + live: bool + """`true` if this is a live mode resource else `false`.""" + + object: Literal["contact"] + """Always `contact`.""" + + updated_at: datetime = FieldInfo(alias="updatedAt") + """The UTC time at which this resource was last updated.""" + + address_errors: Optional[str] = FieldInfo(alias="addressErrors", default=None) + """ + A series of human-readable errors/warnings that were raised when running the + provided address through our address verification. + """ + + address_line2: Optional[str] = FieldInfo(alias="addressLine2", default=None) + """Second line of the contact's address, if applicable.""" + + city: Optional[str] = None + """The city of the contact's address.""" + + company_name: Optional[str] = FieldInfo(alias="companyName", default=None) + """Company name of the contact.""" + + description: Optional[str] = None + """An optional string describing this resource. + + Will be visible in the API and the dashboard. + """ + + email: Optional[str] = None + """Email of the contact.""" + + first_name: Optional[str] = FieldInfo(alias="firstName", default=None) + """First name of the contact.""" + + force_verified_status: Optional[bool] = FieldInfo(alias="forceVerifiedStatus", default=None) + """ + If `true`, PostGrid will force this contact to have an `addressStatus` of + `verified` even if our address verification system says otherwise. + """ + + job_title: Optional[str] = FieldInfo(alias="jobTitle", default=None) + """Job title of the contact.""" + + last_name: Optional[str] = FieldInfo(alias="lastName", default=None) + """Last name of the contact.""" + + metadata: Optional[Dict[str, builtins.object]] = None + """See the section on Metadata.""" + + phone_number: Optional[str] = FieldInfo(alias="phoneNumber", default=None) + """Phone number of the contact.""" + + postal_or_zip: Optional[str] = FieldInfo(alias="postalOrZip", default=None) + """The postal or ZIP code of the contact's address.""" + + province_or_state: Optional[str] = FieldInfo(alias="provinceOrState", default=None) + """Province or state of the contact's address.""" + + skip_verification: Optional[bool] = FieldInfo(alias="skipVerification", default=None) + """ + If `true`, PostGrid will skip running this contact's address through our address + verification system. + """ diff --git a/src/postgrid/types/print_mail/contact_create_param.py b/src/postgrid/types/print_mail/contact_create_param.py new file mode 100644 index 0000000..5e7d788 --- /dev/null +++ b/src/postgrid/types/print_mail/contact_create_param.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from typing_extensions import TypeAlias + +from ..contact_create_with_first_name_param import ContactCreateWithFirstNameParam +from ..contact_create_with_company_name_param import ContactCreateWithCompanyNameParam + +__all__ = ["ContactCreateParam"] + +ContactCreateParam: TypeAlias = Union[ContactCreateWithFirstNameParam, ContactCreateWithCompanyNameParam] diff --git a/src/postgrid/types/print_mail/contact_create_params.py b/src/postgrid/types/print_mail/contact_create_params.py new file mode 100644 index 0000000..19a56f1 --- /dev/null +++ b/src/postgrid/types/print_mail/contact_create_params.py @@ -0,0 +1,129 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union +from typing_extensions import Required, Annotated, TypeAlias, TypedDict + +from ..._utils import PropertyInfo + +__all__ = ["ContactCreateParams", "ContactCreateWithFirstName", "ContactCreateWithCompanyName"] + + +class ContactCreateWithFirstName(TypedDict, total=False): + address_line1: Required[Annotated[str, PropertyInfo(alias="addressLine1")]] + """The first line of the contact's address.""" + + country_code: Required[Annotated[str, PropertyInfo(alias="countryCode")]] + """The ISO 3611-1 country code of the contact's address.""" + + first_name: Required[Annotated[str, PropertyInfo(alias="firstName")]] + + address_line2: Annotated[str, PropertyInfo(alias="addressLine2")] + """Second line of the contact's address, if applicable.""" + + city: str + """The city of the contact's address.""" + + company_name: Annotated[str, PropertyInfo(alias="companyName")] + """Company name of the contact.""" + + description: str + """An optional string describing this resource. + + Will be visible in the API and the dashboard. + """ + + email: str + """Email of the contact.""" + + force_verified_status: Annotated[bool, PropertyInfo(alias="forceVerifiedStatus")] + """ + If `true`, PostGrid will force this contact to have an `addressStatus` of + `verified` even if our address verification system says otherwise. + """ + + job_title: Annotated[str, PropertyInfo(alias="jobTitle")] + """Job title of the contact.""" + + last_name: Annotated[str, PropertyInfo(alias="lastName")] + """Last name of the contact.""" + + metadata: Dict[str, object] + """See the section on Metadata.""" + + phone_number: Annotated[str, PropertyInfo(alias="phoneNumber")] + """Phone number of the contact.""" + + postal_or_zip: Annotated[str, PropertyInfo(alias="postalOrZip")] + """The postal or ZIP code of the contact's address.""" + + province_or_state: Annotated[str, PropertyInfo(alias="provinceOrState")] + """Province or state of the contact's address.""" + + skip_verification: Annotated[bool, PropertyInfo(alias="skipVerification")] + """ + If `true`, PostGrid will skip running this contact's address through our address + verification system. + """ + + +class ContactCreateWithCompanyName(TypedDict, total=False): + address_line1: Required[Annotated[str, PropertyInfo(alias="addressLine1")]] + """The first line of the contact's address.""" + + company_name: Required[Annotated[str, PropertyInfo(alias="companyName")]] + + country_code: Required[Annotated[str, PropertyInfo(alias="countryCode")]] + """The ISO 3611-1 country code of the contact's address.""" + + address_line2: Annotated[str, PropertyInfo(alias="addressLine2")] + """Second line of the contact's address, if applicable.""" + + city: str + """The city of the contact's address.""" + + description: str + """An optional string describing this resource. + + Will be visible in the API and the dashboard. + """ + + email: str + """Email of the contact.""" + + first_name: Annotated[str, PropertyInfo(alias="firstName")] + """First name of the contact.""" + + force_verified_status: Annotated[bool, PropertyInfo(alias="forceVerifiedStatus")] + """ + If `true`, PostGrid will force this contact to have an `addressStatus` of + `verified` even if our address verification system says otherwise. + """ + + job_title: Annotated[str, PropertyInfo(alias="jobTitle")] + """Job title of the contact.""" + + last_name: Annotated[str, PropertyInfo(alias="lastName")] + """Last name of the contact.""" + + metadata: Dict[str, object] + """See the section on Metadata.""" + + phone_number: Annotated[str, PropertyInfo(alias="phoneNumber")] + """Phone number of the contact.""" + + postal_or_zip: Annotated[str, PropertyInfo(alias="postalOrZip")] + """The postal or ZIP code of the contact's address.""" + + province_or_state: Annotated[str, PropertyInfo(alias="provinceOrState")] + """Province or state of the contact's address.""" + + skip_verification: Annotated[bool, PropertyInfo(alias="skipVerification")] + """ + If `true`, PostGrid will skip running this contact's address through our address + verification system. + """ + + +ContactCreateParams: TypeAlias = Union[ContactCreateWithFirstName, ContactCreateWithCompanyName] diff --git a/src/postgrid/types/print_mail/contact_delete_response.py b/src/postgrid/types/print_mail/contact_delete_response.py new file mode 100644 index 0000000..87cf5f0 --- /dev/null +++ b/src/postgrid/types/print_mail/contact_delete_response.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["ContactDeleteResponse"] + + +class ContactDeleteResponse(BaseModel): + id: str + """A unique ID prefixed with contact\\__""" + + deleted: Literal[True] + + object: Literal["contact"] + """Always `contact`.""" diff --git a/src/postgrid/types/print_mail/contact_list_params.py b/src/postgrid/types/print_mail/contact_list_params.py new file mode 100644 index 0000000..c1a1947 --- /dev/null +++ b/src/postgrid/types/print_mail/contact_list_params.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["ContactListParams"] + + +class ContactListParams(TypedDict, total=False): + limit: int + + search: str + """You can supply any string to help narrow down the list of resources. + + For example, if you pass `"New York"` (quoted), it will return resources that + have that string present somewhere in their response. Alternatively, you can + supply a structured search query. See the documentation on + `StructuredSearchQuery` for more details. + """ + + skip: int diff --git a/src/postgrid/types/print_mail/deleted_response.py b/src/postgrid/types/print_mail/deleted_response.py new file mode 100644 index 0000000..b222c9e --- /dev/null +++ b/src/postgrid/types/print_mail/deleted_response.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["DeletedResponse"] + + +class DeletedResponse(BaseModel): + id: str + """The ID of the deleted resource.""" + + deleted: Literal[True] + """Indicates the resource was successfully deleted.""" diff --git a/src/postgrid/types/print_mail/digital_only.py b/src/postgrid/types/print_mail/digital_only.py new file mode 100644 index 0000000..49dbcf2 --- /dev/null +++ b/src/postgrid/types/print_mail/digital_only.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + + +from ..._models import BaseModel + +__all__ = ["DigitalOnly"] + + +class DigitalOnly(BaseModel): + watermark: str + """Text to be displayed as a watermark on the digital cheque.""" diff --git a/src/postgrid/types/print_mail/digital_only_param.py b/src/postgrid/types/print_mail/digital_only_param.py new file mode 100644 index 0000000..2637e5a --- /dev/null +++ b/src/postgrid/types/print_mail/digital_only_param.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["DigitalOnlyParam"] + + +class DigitalOnlyParam(TypedDict, total=False): + watermark: Required[str] + """Text to be displayed as a watermark on the digital cheque.""" diff --git a/src/postgrid/types/print_mail/email_preferences.py b/src/postgrid/types/print_mail/email_preferences.py new file mode 100644 index 0000000..c5c90d0 --- /dev/null +++ b/src/postgrid/types/print_mail/email_preferences.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from ..._models import BaseModel + +__all__ = ["EmailPreferences"] + + +class EmailPreferences(BaseModel): + order_preview_send_preference: Optional[Literal["do_not_send", "send_live_only", "send_live_and_test"]] = FieldInfo( + alias="orderPreviewSendPreference", default=None + ) + """The list of preferences for receiving order preview emails.""" diff --git a/src/postgrid/types/print_mail/file_type.py b/src/postgrid/types/print_mail/file_type.py new file mode 100644 index 0000000..6d46c03 --- /dev/null +++ b/src/postgrid/types/print_mail/file_type.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["FileType"] + +FileType: TypeAlias = Literal["csv"] diff --git a/src/postgrid/types/print_mail/letter.py b/src/postgrid/types/print_mail/letter.py new file mode 100644 index 0000000..20d5799 --- /dev/null +++ b/src/postgrid/types/print_mail/letter.py @@ -0,0 +1,177 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import builtins +from typing import Dict, Optional +from datetime import datetime +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from .contact import Contact +from ..._models import BaseModel +from .letter_size import LetterSize +from .attached_pdf import AttachedPdf +from .cancellation import Cancellation +from .order_status import OrderStatus +from .plastic_card import PlasticCard +from .order_imb_status import OrderImbStatus +from .address_placement import AddressPlacement +from .order_mailing_class import OrderMailingClass + +__all__ = ["Letter"] + + +class Letter(BaseModel): + id: str + """A unique ID prefixed with letter\\__""" + + address_placement: AddressPlacement = FieldInfo(alias="addressPlacement") + """Enum representing the placement of the address on the letter.""" + + color: bool + """Indicates if the letter is in color.""" + + created_at: datetime = FieldInfo(alias="createdAt") + """The UTC time at which this resource was created.""" + + double_sided: bool = FieldInfo(alias="doubleSided") + """Indicates if the letter is double-sided.""" + + envelope: str + """The envelope (ID) for the letter or the default `standard` envelope.""" + + from_: Contact = FieldInfo(alias="from") + """The contact information of the sender.""" + + live: bool + """`true` if this is a live mode resource else `false`.""" + + mailing_class: OrderMailingClass = FieldInfo(alias="mailingClass") + """The mailing class of this order. + + This determines the speed and cost of delivery. See `OrderMailingClass` for more + details. + """ + + object: Literal["letter"] + """Always `letter`.""" + + send_date: datetime = FieldInfo(alias="sendDate") + """This order will transition from `ready` to `printing` on the day after this + date. + + For example, if this is a date on Tuesday, the order will transition to + `printing` on Wednesday at midnight eastern time. + """ + + size: LetterSize + """Enum representing the supported letter sizes.""" + + status: OrderStatus + """See `OrderStatus` for more details on the status of this order.""" + + to: Contact + """The recipient of this order. + + This will be provided even if you delete the underlying contact. + """ + + updated_at: datetime = FieldInfo(alias="updatedAt") + """The UTC time at which this resource was last updated.""" + + attached_pdf: Optional[AttachedPdf] = FieldInfo(alias="attachedPDF", default=None) + """Model representing an attached PDF.""" + + cancellation: Optional[Cancellation] = None + """The cancellation details of this order. + + Populated if the order has been cancelled. + """ + + description: Optional[str] = None + """An optional string describing this resource. + + Will be visible in the API and the dashboard. + """ + + html: Optional[str] = None + """The HTML content for the letter. + + You can supply _either_ this or `template` but not both. + """ + + imb_date: Optional[datetime] = FieldInfo(alias="imbDate", default=None) + """The last date that the IMB status was updated. + + See `imbStatus` for more details. + """ + + imb_status: Optional[OrderImbStatus] = FieldInfo(alias="imbStatus", default=None) + """The Intelligent Mail Barcode (IMB) status of this order. + + Only populated for US-printed and US-destined orders. This is the most detailed + way to track non-express/certified orders. + """ + + imb_zip_code: Optional[str] = FieldInfo(alias="imbZIPCode", default=None) + """ + The most recent ZIP code of the USPS facility that the order has been processed + through. Only populated when an `imbStatus` is present. + """ + + merge_variables: Optional[Dict[str, builtins.object]] = FieldInfo(alias="mergeVariables", default=None) + """ + These will be merged with the variables in the template or HTML you create this + order with. The keys in this object should match the variable names in the + template _exactly_ as they are case-sensitive. Note that these _do not_ apply to + PDFs uploaded with the order. + """ + + metadata: Optional[Dict[str, builtins.object]] = None + """See the section on Metadata.""" + + pdf_workflow_run: Optional[str] = FieldInfo(alias="pdfWorkflowRun", default=None) + """The ID of the PDF workflow run that created the letter, if any.""" + + perforated_page: Optional[Literal[1]] = FieldInfo(alias="perforatedPage", default=None) + """If specified, indicates which letter page is perforated. + + Currently, only the first page can be perforated. + """ + + plastic_card: Optional[PlasticCard] = FieldInfo(alias="plasticCard", default=None) + """Model representing a plastic card.""" + + return_envelope: Optional[str] = FieldInfo(alias="returnEnvelope", default=None) + """The return envelope (ID) sent out with the letter, if any.""" + + template: Optional[str] = None + """The template ID used for the letter. + + You can supply _either_ this or `html` but not both. + """ + + tracking_number: Optional[str] = FieldInfo(alias="trackingNumber", default=None) + """The tracking number of this order. + + Populated after an express/certified order has been processed for delivery. + """ + + uploaded_pdf: Optional[str] = FieldInfo(alias="uploadedPDF", default=None) + """ + If a PDF was uploaded for the letter, this will contain the signed link to the + uploaded PDF. + """ + + url: Optional[str] = None + """PostGrid renders a PDF preview for all orders. + + This should be inspected to ensure that the order is correct before it is sent + out because it shows what will be printed and mailed to the recipient. Once the + PDF preview is generated, this field will be returned by all `GET` endpoints + which produce this order. + + This URL is a signed link to the PDF preview. It will expire after a short + period of time. If you need to access this URL after it has expired, you can + regenerate it by calling the `GET` endpoint again. + """ diff --git a/src/postgrid/types/print_mail/letter_create_params.py b/src/postgrid/types/print_mail/letter_create_params.py new file mode 100644 index 0000000..a07d87e --- /dev/null +++ b/src/postgrid/types/print_mail/letter_create_params.py @@ -0,0 +1,216 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union +from datetime import datetime +from typing_extensions import Literal, Required, Annotated, TypeAlias, TypedDict + +from ..._utils import PropertyInfo +from .letter_size import LetterSize +from .address_placement import AddressPlacement +from .attached_pdf_param import AttachedPdfParam +from .plastic_card_param import PlasticCardParam +from .order_mailing_class import OrderMailingClass +from ..contact_create_with_first_name_param import ContactCreateWithFirstNameParam +from ..contact_create_with_company_name_param import ContactCreateWithCompanyNameParam + +__all__ = [ + "LetterCreateParams", + "LetterCreateWithHTML", + "LetterCreateWithHTMLFrom", + "LetterCreateWithHTMLTo", + "LetterCreateWithTemplate", + "LetterCreateWithPdf", + "LetterCreateWithPdfFrom", + "LetterCreateWithPdfTo", +] + + +class LetterCreateWithHTML(TypedDict, total=False): + from_: Required[Annotated[LetterCreateWithHTMLFrom, PropertyInfo(alias="from")]] + """The contact information of the sender. + + You can pass contact information inline here just like you can for the `to`. + """ + + html: Required[str] + """The HTML content for the letter. + + You can supply _either_ this or `template` but not both. + """ + + to: Required[LetterCreateWithHTMLTo] + """The recipient of this order. + + You can either supply the contact information inline here or provide a contact + ID. PostGrid will automatically deduplicate contacts regardless of whether you + provide the information inline here or call the contact creation endpoint. + """ + + address_placement: Annotated[AddressPlacement, PropertyInfo(alias="addressPlacement")] + """Enum representing the placement of the address on the letter.""" + + attached_pdf: Annotated[AttachedPdfParam, PropertyInfo(alias="attachedPDF")] + """Model representing an attached PDF.""" + + color: bool + """Indicates if the letter is in color.""" + + description: str + """An optional string describing this resource. + + Will be visible in the API and the dashboard. + """ + + double_sided: Annotated[bool, PropertyInfo(alias="doubleSided")] + """Indicates if the letter is double-sided.""" + + envelope: str + """The envelope (ID) for the letter. + + You can either specify a custom envelope ID or use the default `standard` + envelope. + """ + + mailing_class: Annotated[OrderMailingClass, PropertyInfo(alias="mailingClass")] + """The mailing class of this order. + + If not provided, automatically set to `first_class`. + """ + + merge_variables: Annotated[Dict[str, object], PropertyInfo(alias="mergeVariables")] + """ + These will be merged with the variables in the template or HTML you create this + order with. The keys in this object should match the variable names in the + template _exactly_ as they are case-sensitive. Note that these _do not_ apply to + PDFs uploaded with the order. + """ + + metadata: Dict[str, object] + """See the section on Metadata.""" + + perforated_page: Annotated[Literal[1], PropertyInfo(alias="perforatedPage")] + """If specified, indicates which letter page is perforated. + + Currently, only the first page can be perforated. + """ + + plastic_card: Annotated[PlasticCardParam, PropertyInfo(alias="plasticCard")] + """Model representing a plastic card.""" + + return_envelope: Annotated[str, PropertyInfo(alias="returnEnvelope")] + """The return envelope (ID) sent out with the letter, if any.""" + + send_date: Annotated[Union[str, datetime], PropertyInfo(alias="sendDate", format="iso8601")] + """This order will transition from `ready` to `printing` on the day after this + date. + + You can use this parameter to schedule orders for a future date. + """ + + size: LetterSize + """Enum representing the supported letter sizes.""" + + +LetterCreateWithHTMLFrom: TypeAlias = Union[ContactCreateWithFirstNameParam, ContactCreateWithCompanyNameParam, str] + +LetterCreateWithHTMLTo: TypeAlias = Union[ContactCreateWithFirstNameParam, ContactCreateWithCompanyNameParam, str] + + +class LetterCreateWithTemplate(TypedDict, total=False): + template: Required[str] + """The template ID for the letter. + + You can supply _either_ this or `html` but not both. + """ + + +class LetterCreateWithPdf(TypedDict, total=False): + from_: Required[Annotated[LetterCreateWithPdfFrom, PropertyInfo(alias="from")]] + """The contact information of the sender. + + You can pass contact information inline here just like you can for the `to`. + """ + + pdf: Required[str] + """A URL pointing to a PDF file for the letter or the PDF file itself.""" + + to: Required[LetterCreateWithPdfTo] + """The recipient of this order. + + You can either supply the contact information inline here or provide a contact + ID. PostGrid will automatically deduplicate contacts regardless of whether you + provide the information inline here or call the contact creation endpoint. + """ + + address_placement: Annotated[AddressPlacement, PropertyInfo(alias="addressPlacement")] + """Enum representing the placement of the address on the letter.""" + + attached_pdf: Annotated[AttachedPdfParam, PropertyInfo(alias="attachedPDF")] + """Model representing an attached PDF.""" + + color: bool + """Indicates if the letter is in color.""" + + description: str + """An optional string describing this resource. + + Will be visible in the API and the dashboard. + """ + + double_sided: Annotated[bool, PropertyInfo(alias="doubleSided")] + """Indicates if the letter is double-sided.""" + + envelope: str + """The envelope (ID) for the letter. + + You can either specify a custom envelope ID or use the default `standard` + envelope. + """ + + mailing_class: Annotated[OrderMailingClass, PropertyInfo(alias="mailingClass")] + """The mailing class of this order. + + If not provided, automatically set to `first_class`. + """ + + merge_variables: Annotated[Dict[str, object], PropertyInfo(alias="mergeVariables")] + """ + These will be merged with the variables in the template or HTML you create this + order with. The keys in this object should match the variable names in the + template _exactly_ as they are case-sensitive. Note that these _do not_ apply to + PDFs uploaded with the order. + """ + + metadata: Dict[str, object] + """See the section on Metadata.""" + + perforated_page: Annotated[Literal[1], PropertyInfo(alias="perforatedPage")] + """If specified, indicates which letter page is perforated. + + Currently, only the first page can be perforated. + """ + + plastic_card: Annotated[PlasticCardParam, PropertyInfo(alias="plasticCard")] + """Model representing a plastic card.""" + + return_envelope: Annotated[str, PropertyInfo(alias="returnEnvelope")] + """The return envelope (ID) sent out with the letter, if any.""" + + send_date: Annotated[Union[str, datetime], PropertyInfo(alias="sendDate", format="iso8601")] + """This order will transition from `ready` to `printing` on the day after this + date. + + You can use this parameter to schedule orders for a future date. + """ + + size: LetterSize + """Enum representing the supported letter sizes.""" + + +LetterCreateWithPdfFrom: TypeAlias = Union[ContactCreateWithFirstNameParam, ContactCreateWithCompanyNameParam, str] + +LetterCreateWithPdfTo: TypeAlias = Union[ContactCreateWithFirstNameParam, ContactCreateWithCompanyNameParam, str] + +LetterCreateParams: TypeAlias = Union[LetterCreateWithHTML, LetterCreateWithTemplate, LetterCreateWithPdf] diff --git a/src/postgrid/types/print_mail/letter_list_params.py b/src/postgrid/types/print_mail/letter_list_params.py new file mode 100644 index 0000000..38fcb03 --- /dev/null +++ b/src/postgrid/types/print_mail/letter_list_params.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["LetterListParams"] + + +class LetterListParams(TypedDict, total=False): + limit: int + + search: str + """You can supply any string to help narrow down the list of resources. + + For example, if you pass `"New York"` (quoted), it will return resources that + have that string present somewhere in their response. Alternatively, you can + supply a structured search query. See the documentation on + `StructuredSearchQuery` for more details. + """ + + skip: int diff --git a/src/postgrid/types/print_mail/letter_retrieve_url_response.py b/src/postgrid/types/print_mail/letter_retrieve_url_response.py new file mode 100644 index 0000000..5fd2cde --- /dev/null +++ b/src/postgrid/types/print_mail/letter_retrieve_url_response.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + + +from ..._models import BaseModel + +__all__ = ["LetterRetrieveURLResponse"] + + +class LetterRetrieveURLResponse(BaseModel): + id: str + """A unique ID prefixed with letter\\__""" + + object: str + + url: str + """A signed URL linking to the order preview PDF. + + The link remains valid for 15 minutes from the time of the API call. + """ diff --git a/src/postgrid/types/print_mail/letter_size.py b/src/postgrid/types/print_mail/letter_size.py new file mode 100644 index 0000000..55e3926 --- /dev/null +++ b/src/postgrid/types/print_mail/letter_size.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["LetterSize"] + +LetterSize: TypeAlias = Literal["us_letter", "a4"] diff --git a/src/postgrid/types/print_mail/mailing_list.py b/src/postgrid/types/print_mail/mailing_list.py new file mode 100644 index 0000000..f64044f --- /dev/null +++ b/src/postgrid/types/print_mail/mailing_list.py @@ -0,0 +1,50 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List, Optional +from datetime import datetime +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from ..._models import BaseModel + +__all__ = ["MailingList", "Error"] + + +class Error(BaseModel): + message: str + """A human-readable message describing the error.""" + + type: Literal[ + "mailing_list_imports_not_found_error", "download_file_error", "operational_error", "internal_service_error" + ] + """Type of error encountered during mailing list processing.""" + + +class MailingList(BaseModel): + id: str + """A unique ID prefixed with mailing*list*""" + + created_at: datetime = FieldInfo(alias="createdAt") + """The UTC time at which this resource was created.""" + + live: bool + """`true` if this is a live mode resource else `false`.""" + + status: Literal["creating_contacts", "removing_contacts", "counting_recipient_country_codes", "completed"] + """Status of the mailing list processing.""" + + updated_at: datetime = FieldInfo(alias="updatedAt") + """The UTC time at which this resource was last updated.""" + + description: Optional[str] = None + """An optional string describing this resource. + + Will be visible in the API and the dashboard. + """ + + errors: Optional[List[Error]] = None + """A list of processing errors encountered, if any.""" + + metadata: Optional[Dict[str, object]] = None + """See the section on Metadata.""" diff --git a/src/postgrid/types/print_mail/mailing_list_create_params.py b/src/postgrid/types/print_mail/mailing_list_create_params.py new file mode 100644 index 0000000..6367583 --- /dev/null +++ b/src/postgrid/types/print_mail/mailing_list_create_params.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict +from typing_extensions import Annotated, TypedDict + +from ..._utils import PropertyInfo + +__all__ = ["MailingListCreateParams"] + + +class MailingListCreateParams(TypedDict, total=False): + description: str + """An optional string describing this resource. + + Will be visible in the API and the dashboard. + """ + + metadata: Dict[str, object] + """See the section on Metadata.""" + + idempotency_key: Annotated[str, PropertyInfo(alias="idempotency-key")] diff --git a/src/postgrid/types/print_mail/mailing_list_delete_response.py b/src/postgrid/types/print_mail/mailing_list_delete_response.py new file mode 100644 index 0000000..a0a9173 --- /dev/null +++ b/src/postgrid/types/print_mail/mailing_list_delete_response.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["MailingListDeleteResponse"] + + +class MailingListDeleteResponse(BaseModel): + id: str + """A unique ID prefixed with mailing*list*""" + + deleted: Literal[True] diff --git a/src/postgrid/types/print_mail/mailing_list_import_create_params.py b/src/postgrid/types/print_mail/mailing_list_import_create_params.py new file mode 100644 index 0000000..1f00711 --- /dev/null +++ b/src/postgrid/types/print_mail/mailing_list_import_create_params.py @@ -0,0 +1,45 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict +from typing_extensions import Required, Annotated, TypedDict + +from ..._utils import PropertyInfo +from .file_type import FileType + +__all__ = ["MailingListImportCreateParams"] + + +class MailingListImportCreateParams(TypedDict, total=False): + file: Required[str] + """The CSV file for this import.""" + + file_type: Required[Annotated[FileType, PropertyInfo(alias="fileType")]] + """Type of file supported for mailing list imports.""" + + receiver_address_mapping: Required[Annotated[Dict[str, str], PropertyInfo(alias="receiverAddressMapping")]] + """Mapping of columns for receiver addresses.""" + + description: str + """An optional string describing this resource. + + Will be visible in the API and the dashboard. + """ + + metadata: Dict[str, object] + """See the section on Metadata.""" + + receiver_merge_variable_mapping: Annotated[Dict[str, str], PropertyInfo(alias="receiverMergeVariableMapping")] + """Optional mapping of columns for receiver merge variables.""" + + sender_address_mapping: Annotated[Dict[str, str], PropertyInfo(alias="senderAddressMapping")] + """ + Optional mapping of columns for sender addresses. If this is present, then all + receivers should have a corresponding sender. + """ + + sender_merge_variable_mapping: Annotated[Dict[str, str], PropertyInfo(alias="senderMergeVariableMapping")] + """Optional mapping of columns for sender merge variables.""" + + idempotency_key: Annotated[str, PropertyInfo(alias="idempotency-key")] diff --git a/src/postgrid/types/print_mail/mailing_list_import_delete_response.py b/src/postgrid/types/print_mail/mailing_list_import_delete_response.py new file mode 100644 index 0000000..b64b4ea --- /dev/null +++ b/src/postgrid/types/print_mail/mailing_list_import_delete_response.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["MailingListImportDeleteResponse"] + + +class MailingListImportDeleteResponse(BaseModel): + id: str + """A unique ID prefixed with mailing*list_import*""" + + deleted: Literal[True] diff --git a/src/postgrid/types/print_mail/mailing_list_import_list_params.py b/src/postgrid/types/print_mail/mailing_list_import_list_params.py new file mode 100644 index 0000000..ca11b65 --- /dev/null +++ b/src/postgrid/types/print_mail/mailing_list_import_list_params.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["MailingListImportListParams"] + + +class MailingListImportListParams(TypedDict, total=False): + limit: int + + search: str + """You can supply any string to help narrow down the list of resources. + + For example, if you pass `"New York"` (quoted), it will return resources that + have that string present somewhere in their response. Alternatively, you can + supply a structured search query. See the documentation on + `StructuredSearchQuery` for more details. + """ + + skip: int diff --git a/src/postgrid/types/print_mail/mailing_list_import_response.py b/src/postgrid/types/print_mail/mailing_list_import_response.py new file mode 100644 index 0000000..d3b889e --- /dev/null +++ b/src/postgrid/types/print_mail/mailing_list_import_response.py @@ -0,0 +1,131 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List, Optional +from datetime import datetime +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from ..._models import BaseModel +from .file_type import FileType +from .verification_status_count import VerificationStatusCount + +__all__ = ["MailingListImportResponse", "Error", "File", "Note"] + + +class Error(BaseModel): + message: str + """A human-readable message describing the error.""" + + type: Literal[ + "no_valid_contacts_error", "multiple_countries_error", "invalid_contact_count_error", "internal_service_error" + ] + """Type of error encountered during import processing.""" + + +class File(BaseModel): + file_type: FileType = FieldInfo(alias="fileType") + """Type of file supported for mailing list imports.""" + + receiver_address_mapping: Dict[str, str] = FieldInfo(alias="receiverAddressMapping") + """ + Mapping of columns for receiver addresses. Contact fields to file columns. + Possible Contact fields: + + - description + - firstName + - lastName + - email + - phoneNumber + - companyName + - addressLine1 + - addressLine2 + - jobTitle + - city + - postalOrZip + - provinceOrState + - countryCode + """ + + url: str + """The signed URL your controller generates.""" + + receiver_merge_variable_mapping: Optional[Dict[str, str]] = FieldInfo( + alias="receiverMergeVariableMapping", default=None + ) + """Optional mapping of columns for receiver merge variables.""" + + sender_address_mapping: Optional[Dict[str, str]] = FieldInfo(alias="senderAddressMapping", default=None) + """Optional mapping of columns for sender addresses.""" + + sender_merge_variable_mapping: Optional[Dict[str, str]] = FieldInfo( + alias="senderMergeVariableMapping", default=None + ) + """Optional mapping of columns for sender merge variables.""" + + +class Note(BaseModel): + message: str + """A human-readable message describing the note.""" + + type: Literal["truncated_test_file", "skipped_invalid_contacts"] + """Type of note attached to the import process.""" + + +class MailingListImportResponse(BaseModel): + id: str + """A unique ID prefixed with mailing*list_import*""" + + created_at: datetime = FieldInfo(alias="createdAt") + """The UTC time at which this resource was created.""" + + errors: List[Error] + """A list of processing errors encountered, if any. + + Present when status is 'changes_required'. + """ + + file: File + """The file object your controller returns: all the mappings plus a signed URL.""" + + invalid_row_count: int = FieldInfo(alias="invalidRowCount") + """Number of invalid rows found in the file.""" + + live: bool + """`true` if this is a live mode resource else `false`.""" + + notes: List[Note] + """Additional notes about the import process.""" + + organization: str + """The organization that owns this import.""" + + receiver_status_count: VerificationStatusCount = FieldInfo(alias="receiverStatusCount") + """Count of contact verification statuses.""" + + status: Literal["validating", "completed", "changes_required"] + """Status of the mailing list import process.""" + + updated_at: datetime = FieldInfo(alias="updatedAt") + """The UTC time at which this resource was last updated.""" + + valid_row_count: int = FieldInfo(alias="validRowCount") + """Number of valid rows processed from the file.""" + + description: Optional[str] = None + """An optional string describing this resource. + + Will be visible in the API and the dashboard. + """ + + metadata: Optional[Dict[str, object]] = None + """See the section on Metadata.""" + + report_url: Optional[str] = FieldInfo(alias="reportURL", default=None) + """ + A temporary URL to download the processing report, available once the import is + completed. + """ + + sender_status_count: Optional[VerificationStatusCount] = FieldInfo(alias="senderStatusCount", default=None) + """Count of contact verification statuses.""" diff --git a/src/postgrid/types/print_mail/mailing_list_import_update_params.py b/src/postgrid/types/print_mail/mailing_list_import_update_params.py new file mode 100644 index 0000000..9d5f974 --- /dev/null +++ b/src/postgrid/types/print_mail/mailing_list_import_update_params.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Optional +from typing_extensions import TypedDict + +__all__ = ["MailingListImportUpdateParams"] + + +class MailingListImportUpdateParams(TypedDict, total=False): + description: Optional[str] + """An optional description for the import. + + Set to `null` to remove the existing description. + """ + + metadata: Optional[Dict[str, str]] + """Optional key-value data associated with the import. + + Set to `null` to remove existing metadata. + """ diff --git a/src/postgrid/types/print_mail/mailing_list_jobs_params.py b/src/postgrid/types/print_mail/mailing_list_jobs_params.py new file mode 100644 index 0000000..392aa57 --- /dev/null +++ b/src/postgrid/types/print_mail/mailing_list_jobs_params.py @@ -0,0 +1,36 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import Annotated, TypedDict + +from ..._utils import PropertyInfo + +__all__ = ["MailingListJobsParams"] + + +class MailingListJobsParams(TypedDict, total=False): + add_contacts: Annotated[List[str], PropertyInfo(alias="addContacts")] + """List of contact IDs to add to the mailing list. + + Cannot be used with other operations. + """ + + add_mailing_list_imports: Annotated[List[str], PropertyInfo(alias="addMailingListImports")] + """List of mailing list import IDs to add to the mailing list. + + Cannot be used with other operations. + """ + + remove_contacts: Annotated[List[str], PropertyInfo(alias="removeContacts")] + """List of contact IDs to remove from the mailing list. + + Cannot be used with other operations. + """ + + remove_mailing_list_imports: Annotated[List[str], PropertyInfo(alias="removeMailingListImports")] + """List of mailing list import IDs to remove from the mailing list. + + Cannot be used with other operations. + """ diff --git a/src/postgrid/types/print_mail/mailing_list_list_params.py b/src/postgrid/types/print_mail/mailing_list_list_params.py new file mode 100644 index 0000000..9c72243 --- /dev/null +++ b/src/postgrid/types/print_mail/mailing_list_list_params.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["MailingListListParams"] + + +class MailingListListParams(TypedDict, total=False): + limit: int + + search: str + """You can supply any string to help narrow down the list of resources. + + For example, if you pass `"New York"` (quoted), it will return resources that + have that string present somewhere in their response. Alternatively, you can + supply a structured search query. See the documentation on + `StructuredSearchQuery` for more details. + """ + + skip: int diff --git a/src/postgrid/types/print_mail/mailing_list_update.py b/src/postgrid/types/print_mail/mailing_list_update.py new file mode 100644 index 0000000..6aaa358 --- /dev/null +++ b/src/postgrid/types/print_mail/mailing_list_update.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, Optional + +from ..._models import BaseModel + +__all__ = ["MailingListUpdate"] + + +class MailingListUpdate(BaseModel): + description: Optional[str] = None + """An optional string describing this resource. + + Will be visible in the API and the dashboard. + """ + + metadata: Optional[Dict[str, object]] = None + """See the section on Metadata.""" diff --git a/src/postgrid/types/print_mail/mailing_list_update_params.py b/src/postgrid/types/print_mail/mailing_list_update_params.py new file mode 100644 index 0000000..45f2e98 --- /dev/null +++ b/src/postgrid/types/print_mail/mailing_list_update_params.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict +from typing_extensions import TypedDict + +__all__ = ["MailingListUpdateParams"] + + +class MailingListUpdateParams(TypedDict, total=False): + description: str + """An optional string describing this resource. + + Will be visible in the API and the dashboard. + """ + + metadata: Dict[str, object] + """See the section on Metadata.""" diff --git a/src/postgrid/types/print_mail/order_imb_status.py b/src/postgrid/types/print_mail/order_imb_status.py new file mode 100644 index 0000000..7bfc340 --- /dev/null +++ b/src/postgrid/types/print_mail/order_imb_status.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["OrderImbStatus"] + +OrderImbStatus: TypeAlias = Literal["entered_mail_stream", "out_for_delivery", "returned_to_sender"] diff --git a/src/postgrid/types/print_mail/order_mailing_class.py b/src/postgrid/types/print_mail/order_mailing_class.py new file mode 100644 index 0000000..3ef97a4 --- /dev/null +++ b/src/postgrid/types/print_mail/order_mailing_class.py @@ -0,0 +1,34 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["OrderMailingClass"] + +OrderMailingClass: TypeAlias = Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", +] diff --git a/src/postgrid/types/print_mail/order_profiles/__init__.py b/src/postgrid/types/print_mail/order_profiles/__init__.py new file mode 100644 index 0000000..afd8d01 --- /dev/null +++ b/src/postgrid/types/print_mail/order_profiles/__init__.py @@ -0,0 +1,32 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .currency_code import CurrencyCode as CurrencyCode +from .postcard_size import PostcardSize as PostcardSize +from .cheque_profile import ChequeProfile as ChequeProfile +from .letter_profile import LetterProfile as LetterProfile +from .postcard_profile import PostcardProfile as PostcardProfile +from .self_mailer_size import SelfMailerSize as SelfMailerSize +from .cheque_list_params import ChequeListParams as ChequeListParams +from .letter_list_params import LetterListParams as LetterListParams +from .self_mailer_profile import SelfMailerProfile as SelfMailerProfile +from .cheque_create_params import ChequeCreateParams as ChequeCreateParams +from .cheque_list_response import ChequeListResponse as ChequeListResponse +from .cheque_update_params import ChequeUpdateParams as ChequeUpdateParams +from .letter_create_params import LetterCreateParams as LetterCreateParams +from .letter_update_params import LetterUpdateParams as LetterUpdateParams +from .postcard_list_params import PostcardListParams as PostcardListParams +from .cheque_delete_response import ChequeDeleteResponse as ChequeDeleteResponse +from .cheque_retrieve_params import ChequeRetrieveParams as ChequeRetrieveParams +from .letter_delete_response import LetterDeleteResponse as LetterDeleteResponse +from .letter_retrieve_params import LetterRetrieveParams as LetterRetrieveParams +from .postcard_create_params import PostcardCreateParams as PostcardCreateParams +from .postcard_update_params import PostcardUpdateParams as PostcardUpdateParams +from .self_mailer_list_params import SelfMailerListParams as SelfMailerListParams +from .postcard_delete_response import PostcardDeleteResponse as PostcardDeleteResponse +from .postcard_retrieve_params import PostcardRetrieveParams as PostcardRetrieveParams +from .self_mailer_create_params import SelfMailerCreateParams as SelfMailerCreateParams +from .self_mailer_update_params import SelfMailerUpdateParams as SelfMailerUpdateParams +from .self_mailer_delete_response import SelfMailerDeleteResponse as SelfMailerDeleteResponse +from .self_mailer_retrieve_params import SelfMailerRetrieveParams as SelfMailerRetrieveParams diff --git a/src/postgrid/types/print_mail/order_profiles/cheque_create_params.py b/src/postgrid/types/print_mail/order_profiles/cheque_create_params.py new file mode 100644 index 0000000..a720e07 --- /dev/null +++ b/src/postgrid/types/print_mail/order_profiles/cheque_create_params.py @@ -0,0 +1,68 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, List, Union, Optional +from typing_extensions import Required, Annotated, TypedDict + +from ...._types import Base64FileInput +from ...._utils import PropertyInfo +from ..cheque_size import ChequeSize +from .currency_code import CurrencyCode +from ..order_mailing_class import OrderMailingClass + +__all__ = ["ChequeCreateParams"] + + +class ChequeCreateParams(TypedDict, total=False): + bank_account: Required[Annotated[str, PropertyInfo(alias="bankAccount")]] + """ID of the bank account to use for the cheque. Required for creation.""" + + size: Required[ChequeSize] + """Enum representing the supported cheque sizes.""" + + expand: List[str] + """Optional list of related resources to expand in the response.""" + + currency_code: Annotated[CurrencyCode, PropertyInfo(alias="currencyCode")] + """Enum representing the supported currency codes.""" + + description: Optional[str] + """An optional description for the profile. Set to `null` to remove during update.""" + + letter_pdf: Annotated[Union[str, Base64FileInput], PropertyInfo(alias="letterPDF", format="base64")] + """PDF file for an optional attached letter. + + Cannot be used with `letterHTML` or `letterTemplate`. Input only. + """ + + letter_template: Annotated[str, PropertyInfo(alias="letterTemplate")] + """ID of a template for an optional attached letter. + + Cannot be used with `letterHTML` or `letterPDF`. + """ + + logo: Optional[str] + """A publicly accessible URL for the logo to print on the cheque. + + Set to `null` to remove during update. + """ + + mailing_class: Annotated[OrderMailingClass, PropertyInfo(alias="mailingClass")] + """Mailing class. + + Generally must be first class (or equivalent for destination country) for + cheques. + """ + + memo: Optional[str] + """Memo line text for the cheque. Set to `null` to remove during update.""" + + merge_variables: Annotated[Optional[Dict[str, object]], PropertyInfo(alias="mergeVariables")] + """Default merge variables for orders created using this profile.""" + + message: Optional[str] + """Message included on the cheque stub. Set to `null` to remove during update.""" + + metadata: Optional[Dict[str, str]] + """Optional key-value metadata.""" diff --git a/src/postgrid/types/print_mail/order_profiles/cheque_delete_response.py b/src/postgrid/types/print_mail/order_profiles/cheque_delete_response.py new file mode 100644 index 0000000..0b7f724 --- /dev/null +++ b/src/postgrid/types/print_mail/order_profiles/cheque_delete_response.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["ChequeDeleteResponse"] + + +class ChequeDeleteResponse(BaseModel): + id: str + """Unique identifier for the order profile.""" + + deleted: Literal[True] + + object: Literal["cheque_profile"] + """Always `cheque_profile`.""" diff --git a/src/postgrid/types/print_mail/order_profiles/cheque_list_params.py b/src/postgrid/types/print_mail/order_profiles/cheque_list_params.py new file mode 100644 index 0000000..394ef15 --- /dev/null +++ b/src/postgrid/types/print_mail/order_profiles/cheque_list_params.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["ChequeListParams"] + + +class ChequeListParams(TypedDict, total=False): + limit: int + + search: str + """You can supply any string to help narrow down the list of resources. + + For example, if you pass `"New York"` (quoted), it will return resources that + have that string present somewhere in their response. Alternatively, you can + supply a structured search query. See the documentation on + `StructuredSearchQuery` for more details. + """ + + skip: int diff --git a/src/postgrid/types/print_mail/order_profiles/cheque_list_response.py b/src/postgrid/types/print_mail/order_profiles/cheque_list_response.py new file mode 100644 index 0000000..538a449 --- /dev/null +++ b/src/postgrid/types/print_mail/order_profiles/cheque_list_response.py @@ -0,0 +1,74 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import builtins +from typing import Dict, Optional +from datetime import datetime +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from ...._models import BaseModel +from ..cheque_size import ChequeSize +from ..order_mailing_class import OrderMailingClass + +__all__ = ["ChequeListResponse"] + + +class ChequeListResponse(BaseModel): + id: str + """Unique identifier for the order profile.""" + + bank_account: str = FieldInfo(alias="bankAccount") + """ID of the bank account to use for the cheque. Required for creation.""" + + created_at: datetime = FieldInfo(alias="createdAt") + """Timestamp when the profile was created.""" + + live: bool + """Indicates if the profile is associated with the live or test environment.""" + + object: Literal["cheque_profile"] + """Always `cheque_profile`.""" + + size: ChequeSize + """Enum representing the supported cheque sizes.""" + + updated_at: datetime = FieldInfo(alias="updatedAt") + """Timestamp when the profile was last updated.""" + + description: Optional[str] = None + """An optional description for the profile. Set to `null` to remove during update.""" + + letter_template: Optional[str] = FieldInfo(alias="letterTemplate", default=None) + """ID of a template for an optional attached letter. + + Cannot be used with `letterHTML` or `letterPDF`. + """ + + letter_uploaded_pdf: Optional[str] = FieldInfo(alias="letterUploadedPDF", default=None) + """A temporary, signed URL to view the attached letter PDF, if any. Output only.""" + + logo: Optional[str] = None + """A publicly accessible URL for the logo to print on the cheque. + + Set to `null` to remove during update. + """ + + mailing_class: Optional[OrderMailingClass] = FieldInfo(alias="mailingClass", default=None) + """Mailing class. + + Generally must be first class (or equivalent for destination country) for + cheques. + """ + + memo: Optional[str] = None + """Memo line text for the cheque. Set to `null` to remove during update.""" + + merge_variables: Optional[Dict[str, builtins.object]] = FieldInfo(alias="mergeVariables", default=None) + """Default merge variables for orders created using this profile.""" + + message: Optional[str] = None + """Message included on the cheque stub. Set to `null` to remove during update.""" + + metadata: Optional[Dict[str, str]] = None + """Optional key-value metadata.""" diff --git a/src/postgrid/types/print_mail/order_profiles/cheque_profile.py b/src/postgrid/types/print_mail/order_profiles/cheque_profile.py new file mode 100644 index 0000000..7b5f889 --- /dev/null +++ b/src/postgrid/types/print_mail/order_profiles/cheque_profile.py @@ -0,0 +1,78 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import builtins +from typing import Dict, Optional +from datetime import datetime +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from ...._models import BaseModel +from ..cheque_size import ChequeSize +from .currency_code import CurrencyCode +from ..order_mailing_class import OrderMailingClass + +__all__ = ["ChequeProfile"] + + +class ChequeProfile(BaseModel): + id: str + """Unique identifier for the order profile.""" + + bank_account: str = FieldInfo(alias="bankAccount") + """ID of the bank account to use for the cheque. Required for creation.""" + + created_at: datetime = FieldInfo(alias="createdAt") + """Timestamp when the profile was created.""" + + currency_code: CurrencyCode = FieldInfo(alias="currencyCode") + """Enum representing the supported currency codes.""" + + live: bool + """Indicates if the profile is associated with the live or test environment.""" + + object: Literal["cheque_profile"] + """Always `cheque_profile`.""" + + size: ChequeSize + """Enum representing the supported cheque sizes.""" + + updated_at: datetime = FieldInfo(alias="updatedAt") + """Timestamp when the profile was last updated.""" + + description: Optional[str] = None + """An optional description for the profile. Set to `null` to remove during update.""" + + letter_template: Optional[str] = FieldInfo(alias="letterTemplate", default=None) + """ID of a template for an optional attached letter. + + Cannot be used with `letterHTML` or `letterPDF`. + """ + + letter_uploaded_pdf: Optional[str] = FieldInfo(alias="letterUploadedPDF", default=None) + """A temporary, signed URL to view the attached letter PDF, if any. Output only.""" + + logo: Optional[str] = None + """A publicly accessible URL for the logo to print on the cheque. + + Set to `null` to remove during update. + """ + + mailing_class: Optional[OrderMailingClass] = FieldInfo(alias="mailingClass", default=None) + """Mailing class. + + Generally must be first class (or equivalent for destination country) for + cheques. + """ + + memo: Optional[str] = None + """Memo line text for the cheque. Set to `null` to remove during update.""" + + merge_variables: Optional[Dict[str, builtins.object]] = FieldInfo(alias="mergeVariables", default=None) + """Default merge variables for orders created using this profile.""" + + message: Optional[str] = None + """Message included on the cheque stub. Set to `null` to remove during update.""" + + metadata: Optional[Dict[str, str]] = None + """Optional key-value metadata.""" diff --git a/src/postgrid/types/print_mail/order_profiles/cheque_retrieve_params.py b/src/postgrid/types/print_mail/order_profiles/cheque_retrieve_params.py new file mode 100644 index 0000000..6cd8f00 --- /dev/null +++ b/src/postgrid/types/print_mail/order_profiles/cheque_retrieve_params.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import TypedDict + +__all__ = ["ChequeRetrieveParams"] + + +class ChequeRetrieveParams(TypedDict, total=False): + expand: List[str] + """Optional list of related resources to expand in the response.""" diff --git a/src/postgrid/types/print_mail/order_profiles/cheque_update_params.py b/src/postgrid/types/print_mail/order_profiles/cheque_update_params.py new file mode 100644 index 0000000..9070c12 --- /dev/null +++ b/src/postgrid/types/print_mail/order_profiles/cheque_update_params.py @@ -0,0 +1,68 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, List, Union, Optional +from typing_extensions import Required, Annotated, TypedDict + +from ...._types import Base64FileInput +from ...._utils import PropertyInfo +from ..cheque_size import ChequeSize +from .currency_code import CurrencyCode +from ..order_mailing_class import OrderMailingClass + +__all__ = ["ChequeUpdateParams"] + + +class ChequeUpdateParams(TypedDict, total=False): + bank_account: Required[Annotated[str, PropertyInfo(alias="bankAccount")]] + """ID of the bank account to use for the cheque. Required for creation.""" + + size: Required[ChequeSize] + """Enum representing the supported cheque sizes.""" + + expand: List[str] + """Optional list of related resources to expand in the response.""" + + currency_code: Annotated[CurrencyCode, PropertyInfo(alias="currencyCode")] + """Enum representing the supported currency codes.""" + + description: Optional[str] + """An optional description for the profile. Set to `null` to remove during update.""" + + letter_pdf: Annotated[Union[str, Base64FileInput], PropertyInfo(alias="letterPDF", format="base64")] + """PDF file for an optional attached letter. + + Cannot be used with `letterHTML` or `letterTemplate`. Input only. + """ + + letter_template: Annotated[str, PropertyInfo(alias="letterTemplate")] + """ID of a template for an optional attached letter. + + Cannot be used with `letterHTML` or `letterPDF`. + """ + + logo: Optional[str] + """A publicly accessible URL for the logo to print on the cheque. + + Set to `null` to remove during update. + """ + + mailing_class: Annotated[OrderMailingClass, PropertyInfo(alias="mailingClass")] + """Mailing class. + + Generally must be first class (or equivalent for destination country) for + cheques. + """ + + memo: Optional[str] + """Memo line text for the cheque. Set to `null` to remove during update.""" + + merge_variables: Annotated[Optional[Dict[str, object]], PropertyInfo(alias="mergeVariables")] + """Default merge variables for orders created using this profile.""" + + message: Optional[str] + """Message included on the cheque stub. Set to `null` to remove during update.""" + + metadata: Optional[Dict[str, str]] + """Optional key-value metadata.""" diff --git a/src/postgrid/types/print_mail/order_profiles/currency_code.py b/src/postgrid/types/print_mail/order_profiles/currency_code.py new file mode 100644 index 0000000..56b2a59 --- /dev/null +++ b/src/postgrid/types/print_mail/order_profiles/currency_code.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["CurrencyCode"] + +CurrencyCode: TypeAlias = Literal["CAD", "USD"] diff --git a/src/postgrid/types/print_mail/order_profiles/letter_create_params.py b/src/postgrid/types/print_mail/order_profiles/letter_create_params.py new file mode 100644 index 0000000..8e49aa3 --- /dev/null +++ b/src/postgrid/types/print_mail/order_profiles/letter_create_params.py @@ -0,0 +1,61 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, List, Optional +from typing_extensions import Literal, Required, Annotated, TypedDict + +from ...._utils import PropertyInfo +from ..letter_size import LetterSize +from ..address_placement import AddressPlacement +from ..attached_pdf_param import AttachedPdfParam +from ..order_mailing_class import OrderMailingClass + +__all__ = ["LetterCreateParams"] + + +class LetterCreateParams(TypedDict, total=False): + size: Required[LetterSize] + """Enum representing the supported letter sizes.""" + + expand: List[str] + """Optional list of related resources to expand in the response.""" + + address_placement: Annotated[AddressPlacement, PropertyInfo(alias="addressPlacement")] + """Enum representing the placement of the address on the letter.""" + + attached_pdf: Annotated[AttachedPdfParam, PropertyInfo(alias="attachedPDF")] + """Model representing an attached PDF.""" + + color: bool + """Specifies whether to print in color (true) or black and white (false).""" + + description: Optional[str] + """An optional description for the profile. Set to `null` to remove during update.""" + + double_sided: Annotated[bool, PropertyInfo(alias="doubleSided")] + """Specifies whether to print on both sides of the paper.""" + + envelope: str + """ID of a custom envelope to use.""" + + mailing_class: Annotated[OrderMailingClass, PropertyInfo(alias="mailingClass")] + """Mailing class.""" + + merge_variables: Annotated[Optional[Dict[str, object]], PropertyInfo(alias="mergeVariables")] + """Default merge variables for orders created using this profile.""" + + metadata: Optional[Dict[str, str]] + """Optional key-value metadata.""" + + pdf: str + """A PDF file containing the letter content. Cannot be used with `template`.""" + + perforated_page: Annotated[Literal[1], PropertyInfo(alias="perforatedPage")] + """Specifies which page number should be perforated (if any).""" + + return_envelope: Annotated[str, PropertyInfo(alias="returnEnvelope")] + """ID of a return envelope to include.""" + + template: str + """ID of a template to use for the letter content. Cannot be used with `pdf`.""" diff --git a/src/postgrid/types/print_mail/order_profiles/letter_delete_response.py b/src/postgrid/types/print_mail/order_profiles/letter_delete_response.py new file mode 100644 index 0000000..d62d8b3 --- /dev/null +++ b/src/postgrid/types/print_mail/order_profiles/letter_delete_response.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["LetterDeleteResponse"] + + +class LetterDeleteResponse(BaseModel): + id: str + """Unique identifier for the order profile.""" + + deleted: Literal[True] + + object: Literal["letter_profile"] + """Always `letter_profile`.""" diff --git a/src/postgrid/types/print_mail/order_profiles/letter_list_params.py b/src/postgrid/types/print_mail/order_profiles/letter_list_params.py new file mode 100644 index 0000000..38fcb03 --- /dev/null +++ b/src/postgrid/types/print_mail/order_profiles/letter_list_params.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["LetterListParams"] + + +class LetterListParams(TypedDict, total=False): + limit: int + + search: str + """You can supply any string to help narrow down the list of resources. + + For example, if you pass `"New York"` (quoted), it will return resources that + have that string present somewhere in their response. Alternatively, you can + supply a structured search query. See the documentation on + `StructuredSearchQuery` for more details. + """ + + skip: int diff --git a/src/postgrid/types/print_mail/order_profiles/letter_profile.py b/src/postgrid/types/print_mail/order_profiles/letter_profile.py new file mode 100644 index 0000000..dc7555e --- /dev/null +++ b/src/postgrid/types/print_mail/order_profiles/letter_profile.py @@ -0,0 +1,72 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import builtins +from typing import Dict, Optional +from datetime import datetime +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from ...._models import BaseModel +from ..letter_size import LetterSize +from ..attached_pdf import AttachedPdf +from ..address_placement import AddressPlacement +from ..order_mailing_class import OrderMailingClass + +__all__ = ["LetterProfile"] + + +class LetterProfile(BaseModel): + id: str + """Unique identifier for the order profile.""" + + created_at: datetime = FieldInfo(alias="createdAt") + """Timestamp when the profile was created.""" + + live: bool + """Indicates if the profile is associated with the live or test environment.""" + + object: Literal["letter_profile"] + """Always `letter_profile`.""" + + size: LetterSize + """Enum representing the supported letter sizes.""" + + updated_at: datetime = FieldInfo(alias="updatedAt") + """Timestamp when the profile was last updated.""" + + address_placement: Optional[AddressPlacement] = FieldInfo(alias="addressPlacement", default=None) + """Enum representing the placement of the address on the letter.""" + + attached_pdf: Optional[AttachedPdf] = FieldInfo(alias="attachedPDF", default=None) + """Model representing an attached PDF.""" + + color: Optional[bool] = None + """Specifies whether to print in color (true) or black and white (false).""" + + description: Optional[str] = None + """An optional description for the profile. Set to `null` to remove during update.""" + + double_sided: Optional[bool] = FieldInfo(alias="doubleSided", default=None) + """Specifies whether to print on both sides of the paper.""" + + envelope: Optional[str] = None + """ID of a custom envelope to use.""" + + mailing_class: Optional[OrderMailingClass] = FieldInfo(alias="mailingClass", default=None) + """Mailing class.""" + + merge_variables: Optional[Dict[str, builtins.object]] = FieldInfo(alias="mergeVariables", default=None) + """Default merge variables for orders created using this profile.""" + + metadata: Optional[Dict[str, str]] = None + """Optional key-value metadata.""" + + perforated_page: Optional[Literal[1]] = FieldInfo(alias="perforatedPage", default=None) + """Specifies which page number should be perforated (if any).""" + + template: Optional[str] = None + """ID of a template to use for the letter content. Cannot be used with `pdf`.""" + + uploaded_pdf: Optional[str] = FieldInfo(alias="uploadedPDF", default=None) + """A temporary, signed URL to view the uploaded PDF, if any.""" diff --git a/src/postgrid/types/print_mail/order_profiles/letter_retrieve_params.py b/src/postgrid/types/print_mail/order_profiles/letter_retrieve_params.py new file mode 100644 index 0000000..f391450 --- /dev/null +++ b/src/postgrid/types/print_mail/order_profiles/letter_retrieve_params.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import TypedDict + +__all__ = ["LetterRetrieveParams"] + + +class LetterRetrieveParams(TypedDict, total=False): + expand: List[str] + """Optional list of related resources to expand in the response.""" diff --git a/src/postgrid/types/print_mail/order_profiles/letter_update_params.py b/src/postgrid/types/print_mail/order_profiles/letter_update_params.py new file mode 100644 index 0000000..68c5cf4 --- /dev/null +++ b/src/postgrid/types/print_mail/order_profiles/letter_update_params.py @@ -0,0 +1,57 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, List, Optional +from typing_extensions import Literal, Annotated, TypedDict + +from ...._utils import PropertyInfo +from ..address_placement import AddressPlacement +from ..attached_pdf_param import AttachedPdfParam +from ..order_mailing_class import OrderMailingClass + +__all__ = ["LetterUpdateParams"] + + +class LetterUpdateParams(TypedDict, total=False): + expand: List[str] + """Optional list of related resources to expand in the response.""" + + address_placement: Annotated[AddressPlacement, PropertyInfo(alias="addressPlacement")] + """Enum representing the placement of the address on the letter.""" + + attached_pdf: Annotated[AttachedPdfParam, PropertyInfo(alias="attachedPDF")] + """Model representing an attached PDF.""" + + color: bool + """Specifies whether to print in color (true) or black and white (false).""" + + description: Optional[str] + """An optional description for the profile. Set to `null` to remove during update.""" + + double_sided: Annotated[bool, PropertyInfo(alias="doubleSided")] + """Specifies whether to print on both sides of the paper.""" + + envelope: str + """ID of a custom envelope to use.""" + + mailing_class: Annotated[OrderMailingClass, PropertyInfo(alias="mailingClass")] + """Mailing class.""" + + merge_variables: Annotated[Optional[Dict[str, object]], PropertyInfo(alias="mergeVariables")] + """Default merge variables for orders created using this profile.""" + + metadata: Optional[Dict[str, str]] + """Optional key-value metadata.""" + + pdf: str + """A PDF file containing the letter content. Cannot be used with `template`.""" + + perforated_page: Annotated[Literal[1], PropertyInfo(alias="perforatedPage")] + """Specifies which page number should be perforated (if any).""" + + return_envelope: Annotated[str, PropertyInfo(alias="returnEnvelope")] + """ID of a return envelope to include.""" + + template: str + """ID of a template to use for the letter content. Cannot be used with `pdf`.""" diff --git a/src/postgrid/types/print_mail/order_profiles/postcard_create_params.py b/src/postgrid/types/print_mail/order_profiles/postcard_create_params.py new file mode 100644 index 0000000..bc5cd8a --- /dev/null +++ b/src/postgrid/types/print_mail/order_profiles/postcard_create_params.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, List, Optional +from typing_extensions import Required, Annotated, TypedDict + +from ...._utils import PropertyInfo +from .postcard_size import PostcardSize +from ..order_mailing_class import OrderMailingClass + +__all__ = ["PostcardCreateParams"] + + +class PostcardCreateParams(TypedDict, total=False): + size: Required[PostcardSize] + """Enum representing the supported postcard sizes.""" + + expand: List[str] + """Optional list of related resources to expand in the response.""" + + back_template: Annotated[str, PropertyInfo(alias="backTemplate")] + """ID of the template for the back side. Required unless `pdf` is provided.""" + + description: Optional[str] + """An optional description for the profile. Set to `null` to remove during update.""" + + front_template: Annotated[str, PropertyInfo(alias="frontTemplate")] + """ID of the template for the front side. Required unless `pdf` is provided.""" + + mailing_class: Annotated[OrderMailingClass, PropertyInfo(alias="mailingClass")] + """ + Mailing class (cannot include extra services like `certified` or `registered` + for postcards, though). + """ + + merge_variables: Annotated[Optional[Dict[str, object]], PropertyInfo(alias="mergeVariables")] + """Default merge variables for orders created using this profile.""" + + metadata: Optional[Dict[str, str]] + """Optional key-value metadata.""" + + pdf: str + """A 2-page PDF file containing the postcard content (front and back). + + Cannot be used with `frontTemplate`/`backTemplate`. + """ diff --git a/src/postgrid/types/print_mail/order_profiles/postcard_delete_response.py b/src/postgrid/types/print_mail/order_profiles/postcard_delete_response.py new file mode 100644 index 0000000..42d9932 --- /dev/null +++ b/src/postgrid/types/print_mail/order_profiles/postcard_delete_response.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["PostcardDeleteResponse"] + + +class PostcardDeleteResponse(BaseModel): + id: str + """Unique identifier for the order profile.""" + + deleted: Literal[True] + + object: Literal["postcard_profile"] + """Always `postcard_profile`.""" diff --git a/src/postgrid/types/print_mail/order_profiles/postcard_list_params.py b/src/postgrid/types/print_mail/order_profiles/postcard_list_params.py new file mode 100644 index 0000000..03b3e16 --- /dev/null +++ b/src/postgrid/types/print_mail/order_profiles/postcard_list_params.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["PostcardListParams"] + + +class PostcardListParams(TypedDict, total=False): + limit: int + + search: str + """You can supply any string to help narrow down the list of resources. + + For example, if you pass `"New York"` (quoted), it will return resources that + have that string present somewhere in their response. Alternatively, you can + supply a structured search query. See the documentation on + `StructuredSearchQuery` for more details. + """ + + skip: int diff --git a/src/postgrid/types/print_mail/order_profiles/postcard_profile.py b/src/postgrid/types/print_mail/order_profiles/postcard_profile.py new file mode 100644 index 0000000..b1fbef4 --- /dev/null +++ b/src/postgrid/types/print_mail/order_profiles/postcard_profile.py @@ -0,0 +1,58 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import builtins +from typing import Dict, Optional +from datetime import datetime +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from ...._models import BaseModel +from .postcard_size import PostcardSize +from ..order_mailing_class import OrderMailingClass + +__all__ = ["PostcardProfile"] + + +class PostcardProfile(BaseModel): + id: str + """Unique identifier for the order profile.""" + + created_at: datetime = FieldInfo(alias="createdAt") + """Timestamp when the profile was created.""" + + live: bool + """Indicates if the profile is associated with the live or test environment.""" + + object: Literal["postcard_profile"] + """Always `postcard_profile`.""" + + size: PostcardSize + """Enum representing the supported postcard sizes.""" + + updated_at: datetime = FieldInfo(alias="updatedAt") + """Timestamp when the profile was last updated.""" + + back_template: Optional[str] = FieldInfo(alias="backTemplate", default=None) + """ID of the template for the back side. Required unless `pdf` is provided.""" + + description: Optional[str] = None + """An optional description for the profile. Set to `null` to remove during update.""" + + front_template: Optional[str] = FieldInfo(alias="frontTemplate", default=None) + """ID of the template for the front side. Required unless `pdf` is provided.""" + + mailing_class: Optional[OrderMailingClass] = FieldInfo(alias="mailingClass", default=None) + """ + Mailing class (cannot include extra services like `certified` or `registered` + for postcards, though). + """ + + merge_variables: Optional[Dict[str, builtins.object]] = FieldInfo(alias="mergeVariables", default=None) + """Default merge variables for orders created using this profile.""" + + metadata: Optional[Dict[str, str]] = None + """Optional key-value metadata.""" + + uploaded_pdf: Optional[str] = FieldInfo(alias="uploadedPDF", default=None) + """A temporary, signed URL to view the uploaded PDF content, if any.""" diff --git a/src/postgrid/types/print_mail/order_profiles/postcard_retrieve_params.py b/src/postgrid/types/print_mail/order_profiles/postcard_retrieve_params.py new file mode 100644 index 0000000..6acb779 --- /dev/null +++ b/src/postgrid/types/print_mail/order_profiles/postcard_retrieve_params.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import TypedDict + +__all__ = ["PostcardRetrieveParams"] + + +class PostcardRetrieveParams(TypedDict, total=False): + expand: List[str] + """Optional list of related resources to expand in the response.""" diff --git a/src/postgrid/types/print_mail/order_profiles/postcard_size.py b/src/postgrid/types/print_mail/order_profiles/postcard_size.py new file mode 100644 index 0000000..e059962 --- /dev/null +++ b/src/postgrid/types/print_mail/order_profiles/postcard_size.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["PostcardSize"] + +PostcardSize: TypeAlias = Literal["6x4", "9x6", "11x6"] diff --git a/src/postgrid/types/print_mail/order_profiles/postcard_update_params.py b/src/postgrid/types/print_mail/order_profiles/postcard_update_params.py new file mode 100644 index 0000000..0ba8187 --- /dev/null +++ b/src/postgrid/types/print_mail/order_profiles/postcard_update_params.py @@ -0,0 +1,43 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, List, Optional +from typing_extensions import Annotated, TypedDict + +from ...._utils import PropertyInfo +from ..order_mailing_class import OrderMailingClass + +__all__ = ["PostcardUpdateParams"] + + +class PostcardUpdateParams(TypedDict, total=False): + expand: List[str] + """Optional list of related resources to expand in the response.""" + + back_template: Annotated[str, PropertyInfo(alias="backTemplate")] + """ID of the template for the back side. Required unless `pdf` is provided.""" + + description: Optional[str] + """An optional description for the profile. Set to `null` to remove during update.""" + + front_template: Annotated[str, PropertyInfo(alias="frontTemplate")] + """ID of the template for the front side. Required unless `pdf` is provided.""" + + mailing_class: Annotated[OrderMailingClass, PropertyInfo(alias="mailingClass")] + """ + Mailing class (cannot include extra services like `certified` or `registered` + for postcards, though). + """ + + merge_variables: Annotated[Optional[Dict[str, object]], PropertyInfo(alias="mergeVariables")] + """Default merge variables for orders created using this profile.""" + + metadata: Optional[Dict[str, str]] + """Optional key-value metadata.""" + + pdf: str + """A 2-page PDF file containing the postcard content (front and back). + + Cannot be used with `frontTemplate`/`backTemplate`. + """ diff --git a/src/postgrid/types/print_mail/order_profiles/self_mailer_create_params.py b/src/postgrid/types/print_mail/order_profiles/self_mailer_create_params.py new file mode 100644 index 0000000..347e49f --- /dev/null +++ b/src/postgrid/types/print_mail/order_profiles/self_mailer_create_params.py @@ -0,0 +1,44 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, List, Optional +from typing_extensions import Required, Annotated, TypedDict + +from ...._utils import PropertyInfo +from .self_mailer_size import SelfMailerSize +from ..order_mailing_class import OrderMailingClass + +__all__ = ["SelfMailerCreateParams"] + + +class SelfMailerCreateParams(TypedDict, total=False): + size: Required[SelfMailerSize] + """Enum representing the supported self-mailer sizes.""" + + expand: List[str] + """Optional list of related resources to expand in the response.""" + + description: Optional[str] + """An optional description for the profile. Set to `null` to remove during update.""" + + inside_template: Annotated[str, PropertyInfo(alias="insideTemplate")] + """ID of the template for the inside. Required unless `pdf` is provided.""" + + mailing_class: Annotated[OrderMailingClass, PropertyInfo(alias="mailingClass")] + """Mailing class (cannot include extra services for self-mailers).""" + + merge_variables: Annotated[Optional[Dict[str, object]], PropertyInfo(alias="mergeVariables")] + """Default merge variables for orders created using this profile.""" + + metadata: Optional[Dict[str, str]] + """Optional key-value metadata.""" + + outside_template: Annotated[str, PropertyInfo(alias="outsideTemplate")] + """ID of the template for the outside. Required unless `pdf` is provided.""" + + pdf: str + """A 2-page PDF file containing the self-mailer content (inside and outside). + + Cannot be used with `insideTemplate`/`outsideTemplate`. + """ diff --git a/src/postgrid/types/print_mail/order_profiles/self_mailer_delete_response.py b/src/postgrid/types/print_mail/order_profiles/self_mailer_delete_response.py new file mode 100644 index 0000000..2f0b52a --- /dev/null +++ b/src/postgrid/types/print_mail/order_profiles/self_mailer_delete_response.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ...._models import BaseModel + +__all__ = ["SelfMailerDeleteResponse"] + + +class SelfMailerDeleteResponse(BaseModel): + id: str + """Unique identifier for the order profile.""" + + deleted: Literal[True] + + object: Literal["self_mailer_profile"] + """Always `self_mailer_profile`.""" diff --git a/src/postgrid/types/print_mail/order_profiles/self_mailer_list_params.py b/src/postgrid/types/print_mail/order_profiles/self_mailer_list_params.py new file mode 100644 index 0000000..8afdca9 --- /dev/null +++ b/src/postgrid/types/print_mail/order_profiles/self_mailer_list_params.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["SelfMailerListParams"] + + +class SelfMailerListParams(TypedDict, total=False): + limit: int + + search: str + """You can supply any string to help narrow down the list of resources. + + For example, if you pass `"New York"` (quoted), it will return resources that + have that string present somewhere in their response. Alternatively, you can + supply a structured search query. See the documentation on + `StructuredSearchQuery` for more details. + """ + + skip: int diff --git a/src/postgrid/types/print_mail/order_profiles/self_mailer_profile.py b/src/postgrid/types/print_mail/order_profiles/self_mailer_profile.py new file mode 100644 index 0000000..08ee044 --- /dev/null +++ b/src/postgrid/types/print_mail/order_profiles/self_mailer_profile.py @@ -0,0 +1,55 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import builtins +from typing import Dict, Optional +from datetime import datetime +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from ...._models import BaseModel +from .self_mailer_size import SelfMailerSize +from ..order_mailing_class import OrderMailingClass + +__all__ = ["SelfMailerProfile"] + + +class SelfMailerProfile(BaseModel): + id: str + """Unique identifier for the order profile.""" + + created_at: datetime = FieldInfo(alias="createdAt") + """Timestamp when the profile was created.""" + + live: bool + """Indicates if the profile is associated with the live or test environment.""" + + object: Literal["self_mailer_profile"] + """Always `self_mailer_profile`.""" + + size: SelfMailerSize + """Enum representing the supported self-mailer sizes.""" + + updated_at: datetime = FieldInfo(alias="updatedAt") + """Timestamp when the profile was last updated.""" + + description: Optional[str] = None + """An optional description for the profile. Set to `null` to remove during update.""" + + inside_template: Optional[str] = FieldInfo(alias="insideTemplate", default=None) + """ID of the template for the inside. Required unless `pdf` is provided.""" + + mailing_class: Optional[OrderMailingClass] = FieldInfo(alias="mailingClass", default=None) + """Mailing class (cannot include extra services for self-mailers).""" + + merge_variables: Optional[Dict[str, builtins.object]] = FieldInfo(alias="mergeVariables", default=None) + """Default merge variables for orders created using this profile.""" + + metadata: Optional[Dict[str, str]] = None + """Optional key-value metadata.""" + + outside_template: Optional[str] = FieldInfo(alias="outsideTemplate", default=None) + """ID of the template for the outside. Required unless `pdf` is provided.""" + + uploaded_pdf: Optional[str] = FieldInfo(alias="uploadedPDF", default=None) + """A temporary, signed URL to view the uploaded PDF, if any.""" diff --git a/src/postgrid/types/print_mail/order_profiles/self_mailer_retrieve_params.py b/src/postgrid/types/print_mail/order_profiles/self_mailer_retrieve_params.py new file mode 100644 index 0000000..2ba7c3f --- /dev/null +++ b/src/postgrid/types/print_mail/order_profiles/self_mailer_retrieve_params.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import TypedDict + +__all__ = ["SelfMailerRetrieveParams"] + + +class SelfMailerRetrieveParams(TypedDict, total=False): + expand: List[str] + """Optional list of related resources to expand in the response.""" diff --git a/src/postgrid/types/print_mail/order_profiles/self_mailer_size.py b/src/postgrid/types/print_mail/order_profiles/self_mailer_size.py new file mode 100644 index 0000000..99ff836 --- /dev/null +++ b/src/postgrid/types/print_mail/order_profiles/self_mailer_size.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["SelfMailerSize"] + +SelfMailerSize: TypeAlias = Literal["8.5x11_bifold", "8.5x11_trifold", "9.5x16_trifold"] diff --git a/src/postgrid/types/print_mail/order_profiles/self_mailer_update_params.py b/src/postgrid/types/print_mail/order_profiles/self_mailer_update_params.py new file mode 100644 index 0000000..56141f6 --- /dev/null +++ b/src/postgrid/types/print_mail/order_profiles/self_mailer_update_params.py @@ -0,0 +1,44 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, List, Optional +from typing_extensions import Required, Annotated, TypedDict + +from ...._utils import PropertyInfo +from .self_mailer_size import SelfMailerSize +from ..order_mailing_class import OrderMailingClass + +__all__ = ["SelfMailerUpdateParams"] + + +class SelfMailerUpdateParams(TypedDict, total=False): + size: Required[SelfMailerSize] + """Enum representing the supported self-mailer sizes.""" + + expand: List[str] + """Optional list of related resources to expand in the response.""" + + description: Optional[str] + """An optional description for the profile. Set to `null` to remove during update.""" + + inside_template: Annotated[str, PropertyInfo(alias="insideTemplate")] + """ID of the template for the inside. Required unless `pdf` is provided.""" + + mailing_class: Annotated[OrderMailingClass, PropertyInfo(alias="mailingClass")] + """Mailing class (cannot include extra services for self-mailers).""" + + merge_variables: Annotated[Optional[Dict[str, object]], PropertyInfo(alias="mergeVariables")] + """Default merge variables for orders created using this profile.""" + + metadata: Optional[Dict[str, str]] + """Optional key-value metadata.""" + + outside_template: Annotated[str, PropertyInfo(alias="outsideTemplate")] + """ID of the template for the outside. Required unless `pdf` is provided.""" + + pdf: str + """A 2-page PDF file containing the self-mailer content (inside and outside). + + Cannot be used with `insideTemplate`/`outsideTemplate`. + """ diff --git a/src/postgrid/types/print_mail/order_status.py b/src/postgrid/types/print_mail/order_status.py new file mode 100644 index 0000000..9fc29d4 --- /dev/null +++ b/src/postgrid/types/print_mail/order_status.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["OrderStatus"] + +OrderStatus: TypeAlias = Literal["ready", "printing", "processed_for_delivery", "completed", "cancelled"] diff --git a/src/postgrid/types/print_mail/plastic_card.py b/src/postgrid/types/print_mail/plastic_card.py new file mode 100644 index 0000000..1ba4f09 --- /dev/null +++ b/src/postgrid/types/print_mail/plastic_card.py @@ -0,0 +1,58 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from ..._models import BaseModel + +__all__ = ["PlasticCard", "DoubleSided", "SingleSided"] + + +class DoubleSided(BaseModel): + back_html: Optional[str] = FieldInfo(alias="backHTML", default=None) + """The HTML content for the back side of the double-sided plastic card.""" + + back_template: Optional[str] = FieldInfo(alias="backTemplate", default=None) + """The template ID for the back side of the double-sided plastic card.""" + + front_html: Optional[str] = FieldInfo(alias="frontHTML", default=None) + """The HTML content for the front side of the double-sided plastic card.""" + + front_template: Optional[str] = FieldInfo(alias="frontTemplate", default=None) + """The template ID for the front side of the double-sided plastic card.""" + + pdf: Optional[str] = None + """ + A URL pointing to a PDF file for the double-sided plastic card or the file + itself. + """ + + +class SingleSided(BaseModel): + html: Optional[str] = None + """The HTML content for the single-sided plastic card. + + Can specify one of this, `template`, or `pdf`. + """ + + pdf: Optional[str] = None + """ + A URL pointing to a PDF file for the single-sided plastic card or the PDF file + itself. + """ + + template: Optional[str] = None + """The template ID for the single-sided plastic card.""" + + +class PlasticCard(BaseModel): + size: Literal["standard"] + """Enum representing the size of the plastic card.""" + + double_sided: Optional[DoubleSided] = FieldInfo(alias="doubleSided", default=None) + """Model representing a double-sided plastic card.""" + + single_sided: Optional[SingleSided] = FieldInfo(alias="singleSided", default=None) + """Model representing a single-sided plastic card.""" diff --git a/src/postgrid/types/print_mail/plastic_card_param.py b/src/postgrid/types/print_mail/plastic_card_param.py new file mode 100644 index 0000000..4ae554e --- /dev/null +++ b/src/postgrid/types/print_mail/plastic_card_param.py @@ -0,0 +1,57 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal, Required, Annotated, TypedDict + +from ..._utils import PropertyInfo + +__all__ = ["PlasticCardParam", "DoubleSided", "SingleSided"] + + +class DoubleSided(TypedDict, total=False): + back_html: Annotated[str, PropertyInfo(alias="backHTML")] + """The HTML content for the back side of the double-sided plastic card.""" + + back_template: Annotated[str, PropertyInfo(alias="backTemplate")] + """The template ID for the back side of the double-sided plastic card.""" + + front_html: Annotated[str, PropertyInfo(alias="frontHTML")] + """The HTML content for the front side of the double-sided plastic card.""" + + front_template: Annotated[str, PropertyInfo(alias="frontTemplate")] + """The template ID for the front side of the double-sided plastic card.""" + + pdf: str + """ + A URL pointing to a PDF file for the double-sided plastic card or the file + itself. + """ + + +class SingleSided(TypedDict, total=False): + html: str + """The HTML content for the single-sided plastic card. + + Can specify one of this, `template`, or `pdf`. + """ + + pdf: str + """ + A URL pointing to a PDF file for the single-sided plastic card or the PDF file + itself. + """ + + template: str + """The template ID for the single-sided plastic card.""" + + +class PlasticCardParam(TypedDict, total=False): + size: Required[Literal["standard"]] + """Enum representing the size of the plastic card.""" + + double_sided: Annotated[DoubleSided, PropertyInfo(alias="doubleSided")] + """Model representing a double-sided plastic card.""" + + single_sided: Annotated[SingleSided, PropertyInfo(alias="singleSided")] + """Model representing a single-sided plastic card.""" diff --git a/src/postgrid/types/print_mail/postcard.py b/src/postgrid/types/print_mail/postcard.py new file mode 100644 index 0000000..5609a2a --- /dev/null +++ b/src/postgrid/types/print_mail/postcard.py @@ -0,0 +1,126 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import builtins +from typing import Dict, Optional +from datetime import datetime +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from .contact import Contact +from ..._models import BaseModel +from .cancellation import Cancellation +from .order_status import OrderStatus +from .order_imb_status import OrderImbStatus +from .order_mailing_class import OrderMailingClass +from .order_profiles.postcard_size import PostcardSize + +__all__ = ["Postcard"] + + +class Postcard(BaseModel): + id: str + """A unique ID prefixed with postcard\\__""" + + created_at: datetime = FieldInfo(alias="createdAt") + """The UTC time at which this resource was created.""" + + live: bool + """`true` if this is a live mode resource else `false`.""" + + mailing_class: OrderMailingClass = FieldInfo(alias="mailingClass") + """The mailing class of this order. + + This determines the speed and cost of delivery. See `OrderMailingClass` for more + details. + """ + + object: Literal["postcard"] + """Always `postcard`.""" + + send_date: datetime = FieldInfo(alias="sendDate") + """This order will transition from `ready` to `printing` on the day after this + date. + + For example, if this is a date on Tuesday, the order will transition to + `printing` on Wednesday at midnight eastern time. + """ + + size: PostcardSize + """Enum representing the supported postcard sizes.""" + + status: OrderStatus + """See `OrderStatus` for more details on the status of this order.""" + + to: Contact + """The recipient of this order. + + This will be provided even if you delete the underlying contact. + """ + + updated_at: datetime = FieldInfo(alias="updatedAt") + """The UTC time at which this resource was last updated.""" + + cancellation: Optional[Cancellation] = None + """The cancellation details of this order. + + Populated if the order has been cancelled. + """ + + description: Optional[str] = None + """An optional string describing this resource. + + Will be visible in the API and the dashboard. + """ + + from_: Optional[Contact] = FieldInfo(alias="from", default=None) + """The contact information of the sender.""" + + imb_date: Optional[datetime] = FieldInfo(alias="imbDate", default=None) + """The last date that the IMB status was updated. + + See `imbStatus` for more details. + """ + + imb_status: Optional[OrderImbStatus] = FieldInfo(alias="imbStatus", default=None) + """The Intelligent Mail Barcode (IMB) status of this order. + + Only populated for US-printed and US-destined orders. This is the most detailed + way to track non-express/certified orders. + """ + + imb_zip_code: Optional[str] = FieldInfo(alias="imbZIPCode", default=None) + """ + The most recent ZIP code of the USPS facility that the order has been processed + through. Only populated when an `imbStatus` is present. + """ + + merge_variables: Optional[Dict[str, builtins.object]] = FieldInfo(alias="mergeVariables", default=None) + """ + These will be merged with the variables in the template or HTML you create this + order with. The keys in this object should match the variable names in the + template _exactly_ as they are case-sensitive. Note that these _do not_ apply to + PDFs uploaded with the order. + """ + + metadata: Optional[Dict[str, builtins.object]] = None + """See the section on Metadata.""" + + tracking_number: Optional[str] = FieldInfo(alias="trackingNumber", default=None) + """The tracking number of this order. + + Populated after an express/certified order has been processed for delivery. + """ + + url: Optional[str] = None + """PostGrid renders a PDF preview for all orders. + + This should be inspected to ensure that the order is correct before it is sent + out because it shows what will be printed and mailed to the recipient. Once the + PDF preview is generated, this field will be returned by all `GET` endpoints + which produce this order. + + This URL is a signed link to the PDF preview. It will expire after a short + period of time. If you need to access this URL after it has expired, you can + regenerate it by calling the `GET` endpoint again. + """ diff --git a/src/postgrid/types/print_mail/postcard_create_params.py b/src/postgrid/types/print_mail/postcard_create_params.py new file mode 100644 index 0000000..ef43122 --- /dev/null +++ b/src/postgrid/types/print_mail/postcard_create_params.py @@ -0,0 +1,239 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union +from datetime import datetime +from typing_extensions import Required, Annotated, TypeAlias, TypedDict + +from ..._types import Base64FileInput +from ..._utils import PropertyInfo +from .order_mailing_class import OrderMailingClass +from .order_profiles.postcard_size import PostcardSize +from ..contact_create_with_first_name_param import ContactCreateWithFirstNameParam +from ..contact_create_with_company_name_param import ContactCreateWithCompanyNameParam + +__all__ = [ + "PostcardCreateParams", + "PostcardCreateWithHTML", + "PostcardCreateWithHTMLTo", + "PostcardCreateWithHTMLFrom", + "PostcardCreateWithTemplate", + "PostcardCreateWithPdfurl", + "PostcardCreateWithPdfurlTo", + "PostcardCreateWithPdfurlFrom", + "PostcardCreateWithPdfFile", + "PostcardCreateWithPdfFileTo", + "PostcardCreateWithPdfFileFrom", +] + + +class PostcardCreateWithHTML(TypedDict, total=False): + back_html: Required[Annotated[str, PropertyInfo(alias="backHTML")]] + """The HTML content for the back of the postcard. + + You can supply _either_ this or `backTemplate` but not both. + """ + + front_html: Required[Annotated[str, PropertyInfo(alias="frontHTML")]] + """The HTML content for the front of the postcard. + + You can supply _either_ this or `frontTemplate` but not both. + """ + + size: Required[PostcardSize] + """Enum representing the supported postcard sizes.""" + + to: Required[PostcardCreateWithHTMLTo] + """The recipient of this order. + + You can either supply the contact information inline here or provide a contact + ID. PostGrid will automatically deduplicate contacts regardless of whether you + provide the information inline here or call the contact creation endpoint. + """ + + description: str + """An optional string describing this resource. + + Will be visible in the API and the dashboard. + """ + + from_: Annotated[PostcardCreateWithHTMLFrom, PropertyInfo(alias="from")] + """The contact information of the sender. + + You can pass contact information inline here just like you can for the `to`. + Unlike other order types, the sender address is optional for postcards. + """ + + mailing_class: Annotated[OrderMailingClass, PropertyInfo(alias="mailingClass")] + """The mailing class of this order. + + If not provided, automatically set to `first_class`. + """ + + merge_variables: Annotated[Dict[str, object], PropertyInfo(alias="mergeVariables")] + """ + These will be merged with the variables in the template or HTML you create this + order with. The keys in this object should match the variable names in the + template _exactly_ as they are case-sensitive. Note that these _do not_ apply to + PDFs uploaded with the order. + """ + + metadata: Dict[str, object] + """See the section on Metadata.""" + + send_date: Annotated[Union[str, datetime], PropertyInfo(alias="sendDate", format="iso8601")] + """This order will transition from `ready` to `printing` on the day after this + date. + + You can use this parameter to schedule orders for a future date. + """ + + +PostcardCreateWithHTMLTo: TypeAlias = Union[ContactCreateWithFirstNameParam, ContactCreateWithCompanyNameParam, str] + +PostcardCreateWithHTMLFrom: TypeAlias = Union[ContactCreateWithFirstNameParam, ContactCreateWithCompanyNameParam, str] + + +class PostcardCreateWithTemplate(TypedDict, total=False): + back_template: Required[Annotated[str, PropertyInfo(alias="backTemplate")]] + """The template ID for the back of the postcard. + + You can supply _either_ this or `backHTML` but not both. + """ + + front_template: Required[Annotated[str, PropertyInfo(alias="frontTemplate")]] + """The template ID for the front of the postcard. + + You can supply _either_ this or `frontHTML` but not both. + """ + + +class PostcardCreateWithPdfurl(TypedDict, total=False): + pdf: Required[str] + """A URL pointing to a 2 page PDF file. + + The first page is the front of the postcard and the second page is the back + (where the address will be stamped on). + """ + + size: Required[PostcardSize] + """Enum representing the supported postcard sizes.""" + + to: Required[PostcardCreateWithPdfurlTo] + """The recipient of this order. + + You can either supply the contact information inline here or provide a contact + ID. PostGrid will automatically deduplicate contacts regardless of whether you + provide the information inline here or call the contact creation endpoint. + """ + + description: str + """An optional string describing this resource. + + Will be visible in the API and the dashboard. + """ + + from_: Annotated[PostcardCreateWithPdfurlFrom, PropertyInfo(alias="from")] + """The contact information of the sender. + + You can pass contact information inline here just like you can for the `to`. + Unlike other order types, the sender address is optional for postcards. + """ + + mailing_class: Annotated[OrderMailingClass, PropertyInfo(alias="mailingClass")] + """The mailing class of this order. + + If not provided, automatically set to `first_class`. + """ + + merge_variables: Annotated[Dict[str, object], PropertyInfo(alias="mergeVariables")] + """ + These will be merged with the variables in the template or HTML you create this + order with. The keys in this object should match the variable names in the + template _exactly_ as they are case-sensitive. Note that these _do not_ apply to + PDFs uploaded with the order. + """ + + metadata: Dict[str, object] + """See the section on Metadata.""" + + send_date: Annotated[Union[str, datetime], PropertyInfo(alias="sendDate", format="iso8601")] + """This order will transition from `ready` to `printing` on the day after this + date. + + You can use this parameter to schedule orders for a future date. + """ + + +PostcardCreateWithPdfurlTo: TypeAlias = Union[ContactCreateWithFirstNameParam, ContactCreateWithCompanyNameParam, str] + +PostcardCreateWithPdfurlFrom: TypeAlias = Union[ContactCreateWithFirstNameParam, ContactCreateWithCompanyNameParam, str] + + +class PostcardCreateWithPdfFile(TypedDict, total=False): + pdf: Required[Annotated[Union[str, Base64FileInput], PropertyInfo(format="base64")]] + """A 2 page PDF file. + + The first page is the front of the postcard and the second page is the back + (where the address will be stamped on). + """ + + size: Required[PostcardSize] + """Enum representing the supported postcard sizes.""" + + to: Required[PostcardCreateWithPdfFileTo] + """The recipient of this order. + + You can either supply the contact information inline here or provide a contact + ID. PostGrid will automatically deduplicate contacts regardless of whether you + provide the information inline here or call the contact creation endpoint. + """ + + description: str + """An optional string describing this resource. + + Will be visible in the API and the dashboard. + """ + + from_: Annotated[PostcardCreateWithPdfFileFrom, PropertyInfo(alias="from")] + """The contact information of the sender. + + You can pass contact information inline here just like you can for the `to`. + Unlike other order types, the sender address is optional for postcards. + """ + + mailing_class: Annotated[OrderMailingClass, PropertyInfo(alias="mailingClass")] + """The mailing class of this order. + + If not provided, automatically set to `first_class`. + """ + + merge_variables: Annotated[Dict[str, object], PropertyInfo(alias="mergeVariables")] + """ + These will be merged with the variables in the template or HTML you create this + order with. The keys in this object should match the variable names in the + template _exactly_ as they are case-sensitive. Note that these _do not_ apply to + PDFs uploaded with the order. + """ + + metadata: Dict[str, object] + """See the section on Metadata.""" + + send_date: Annotated[Union[str, datetime], PropertyInfo(alias="sendDate", format="iso8601")] + """This order will transition from `ready` to `printing` on the day after this + date. + + You can use this parameter to schedule orders for a future date. + """ + + +PostcardCreateWithPdfFileTo: TypeAlias = Union[ContactCreateWithFirstNameParam, ContactCreateWithCompanyNameParam, str] + +PostcardCreateWithPdfFileFrom: TypeAlias = Union[ + ContactCreateWithFirstNameParam, ContactCreateWithCompanyNameParam, str +] + +PostcardCreateParams: TypeAlias = Union[ + PostcardCreateWithHTML, PostcardCreateWithTemplate, PostcardCreateWithPdfurl, PostcardCreateWithPdfFile +] diff --git a/src/postgrid/types/print_mail/postcard_list_params.py b/src/postgrid/types/print_mail/postcard_list_params.py new file mode 100644 index 0000000..03b3e16 --- /dev/null +++ b/src/postgrid/types/print_mail/postcard_list_params.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["PostcardListParams"] + + +class PostcardListParams(TypedDict, total=False): + limit: int + + search: str + """You can supply any string to help narrow down the list of resources. + + For example, if you pass `"New York"` (quoted), it will return resources that + have that string present somewhere in their response. Alternatively, you can + supply a structured search query. See the documentation on + `StructuredSearchQuery` for more details. + """ + + skip: int diff --git a/src/postgrid/types/print_mail/postcard_retrieve_url_response.py b/src/postgrid/types/print_mail/postcard_retrieve_url_response.py new file mode 100644 index 0000000..6082563 --- /dev/null +++ b/src/postgrid/types/print_mail/postcard_retrieve_url_response.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + + +from ..._models import BaseModel + +__all__ = ["PostcardRetrieveURLResponse"] + + +class PostcardRetrieveURLResponse(BaseModel): + id: str + """A unique ID prefixed with postcard\\__""" + + object: str + + url: str + """A signed URL linking to the order preview PDF. + + The link remains valid for 15 minutes from the time of the API call. + """ diff --git a/src/postgrid/types/print_mail/report.py b/src/postgrid/types/print_mail/report.py new file mode 100644 index 0000000..958fe18 --- /dev/null +++ b/src/postgrid/types/print_mail/report.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, Optional +from datetime import datetime +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from ..._models import BaseModel + +__all__ = ["Report"] + + +class Report(BaseModel): + id: str + """Unique identifier for the report.""" + + created_at: datetime = FieldInfo(alias="createdAt") + """Timestamp when the report was created.""" + + live: bool + """Indicates if the report is associated with the live or test environment.""" + + object: Literal["report"] + """Always `report`.""" + + sql_query: str = FieldInfo(alias="sqlQuery") + """The SQL query defining the report.""" + + updated_at: datetime = FieldInfo(alias="updatedAt") + """Timestamp when the report was last updated.""" + + description: Optional[str] = None + """An optional user-friendly description for the report.""" + + metadata: Optional[Dict[str, str]] = None + """Optional key-value metadata associated with the report.""" diff --git a/src/postgrid/types/print_mail/report_create_params.py b/src/postgrid/types/print_mail/report_create_params.py new file mode 100644 index 0000000..b56223b --- /dev/null +++ b/src/postgrid/types/print_mail/report_create_params.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict +from typing_extensions import Required, Annotated, TypedDict + +from ..._utils import PropertyInfo + +__all__ = ["ReportCreateParams"] + + +class ReportCreateParams(TypedDict, total=False): + sql_query: Required[Annotated[str, PropertyInfo(alias="sqlQuery")]] + """The SQL query defining the report.""" + + description: str + """An optional user-friendly description for the report.""" + + metadata: Dict[str, str] + """Optional key-value metadata associated with the report.""" diff --git a/src/postgrid/types/print_mail/report_list_params.py b/src/postgrid/types/print_mail/report_list_params.py new file mode 100644 index 0000000..dd2cb50 --- /dev/null +++ b/src/postgrid/types/print_mail/report_list_params.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["ReportListParams"] + + +class ReportListParams(TypedDict, total=False): + limit: int + + search: str + """You can supply any string to help narrow down the list of resources. + + For example, if you pass `"New York"` (quoted), it will return resources that + have that string present somewhere in their response. Alternatively, you can + supply a structured search query. See the documentation on + `StructuredSearchQuery` for more details. + """ + + skip: int diff --git a/src/postgrid/types/print_mail/report_sample_params.py b/src/postgrid/types/print_mail/report_sample_params.py new file mode 100644 index 0000000..13244bb --- /dev/null +++ b/src/postgrid/types/print_mail/report_sample_params.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import Required, Annotated, TypedDict + +from ..._utils import PropertyInfo + +__all__ = ["ReportSampleParams"] + + +class ReportSampleParams(TypedDict, total=False): + sql_query: Required[Annotated[str, PropertyInfo(alias="sqlQuery")]] + """The SQL query to execute for the sample.""" + + limit: int + """Maximum number of rows to return in the sample.""" + + params: List[str] + """ + Optional parameters to bind to the SQL query (e.g., for placeholders like ? or + $1). + """ diff --git a/src/postgrid/types/print_mail/report_update_params.py b/src/postgrid/types/print_mail/report_update_params.py new file mode 100644 index 0000000..0b6d0aa --- /dev/null +++ b/src/postgrid/types/print_mail/report_update_params.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Optional +from typing_extensions import Annotated, TypedDict + +from ..._utils import PropertyInfo + +__all__ = ["ReportUpdateParams"] + + +class ReportUpdateParams(TypedDict, total=False): + description: Optional[str] + """An optional user-friendly description for the report. Set to null to remove.""" + + metadata: Optional[Dict[str, str]] + """Optional key-value metadata associated with the report. Set to null to remove.""" + + sql_query: Annotated[str, PropertyInfo(alias="sqlQuery")] + """The SQL query defining the report.""" diff --git a/src/postgrid/types/print_mail/reports/__init__.py b/src/postgrid/types/print_mail/reports/__init__.py new file mode 100644 index 0000000..efdb6a0 --- /dev/null +++ b/src/postgrid/types/print_mail/reports/__init__.py @@ -0,0 +1,8 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .report_export import ReportExport as ReportExport +from .report_sample import ReportSample as ReportSample +from .export_create_params import ExportCreateParams as ExportCreateParams +from .sample_create_params import SampleCreateParams as SampleCreateParams diff --git a/src/postgrid/types/print_mail/reports/export_create_params.py b/src/postgrid/types/print_mail/reports/export_create_params.py new file mode 100644 index 0000000..5d11e8f --- /dev/null +++ b/src/postgrid/types/print_mail/reports/export_create_params.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, List +from typing_extensions import TypedDict + +__all__ = ["ExportCreateParams"] + + +class ExportCreateParams(TypedDict, total=False): + description: str + """An optional user-friendly description for the export.""" + + metadata: Dict[str, str] + """Optional key-value metadata associated with the export.""" + + params: List[str] + """Optional parameters to bind to the SQL query of the associated report.""" diff --git a/src/postgrid/types/print_mail/reports/report_export.py b/src/postgrid/types/print_mail/reports/report_export.py new file mode 100644 index 0000000..b36cec3 --- /dev/null +++ b/src/postgrid/types/print_mail/reports/report_export.py @@ -0,0 +1,66 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List, Optional +from datetime import datetime +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from ...._models import BaseModel + +__all__ = ["ReportExport", "Report"] + + +class Report(BaseModel): + id: str + """The ID of the report.""" + + sql_query: str = FieldInfo(alias="sqlQuery") + """The SQL query used for this export (snapshotted at creation time).""" + + +class ReportExport(BaseModel): + id: str + """Unique identifier for the report export.""" + + created_at: datetime = FieldInfo(alias="createdAt") + """Timestamp when the export was created.""" + + live: bool + """Indicates if the export is associated with the live or test environment.""" + + object: Literal["report_export"] + """Always `report_export`.""" + + report: Report + """Details of the report this export was generated from.""" + + updated_at: datetime = FieldInfo(alias="updatedAt") + """Timestamp when the export was last updated (e.g., finished generation).""" + + description: Optional[str] = None + """An optional user-friendly description for the export.""" + + failure_message: Optional[str] = FieldInfo(alias="failureMessage", default=None) + """If export generation failed, this contains the error message.""" + + metadata: Optional[Dict[str, str]] = None + """Optional key-value metadata associated with the export.""" + + output_url: Optional[str] = FieldInfo(alias="outputURL", default=None) + """A signed URL to download the exported data (CSV format). + + Available when generation is complete and successful. + """ + + params: Optional[List[str]] = None + """Optional parameters to bind to the SQL query of the associated report.""" + + row_count: Optional[int] = FieldInfo(alias="rowCount", default=None) + """The number of rows in the exported data.""" + + size_in_bytes: Optional[int] = FieldInfo(alias="sizeInBytes", default=None) + """The size of the generated export file in bytes.""" + + truncated_to_bytes: Optional[int] = FieldInfo(alias="truncatedToBytes", default=None) + """If the output was truncated, indicates the byte limit reached.""" diff --git a/src/postgrid/types/print_mail/reports/report_sample.py b/src/postgrid/types/print_mail/reports/report_sample.py new file mode 100644 index 0000000..b0b617c --- /dev/null +++ b/src/postgrid/types/print_mail/reports/report_sample.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, List, Optional + +from ...._models import BaseModel + +__all__ = ["ReportSample"] + + +class ReportSample(BaseModel): + id: str + """Unique identifier for the sample query result.""" + + records: List[Dict[str, object]] + """The actual data records returned by the sample query.""" + + report: Optional[str] = None + """ + The ID of the report this sample was generated from, or null for ad-hoc samples. + """ diff --git a/src/postgrid/types/print_mail/reports/sample_create_params.py b/src/postgrid/types/print_mail/reports/sample_create_params.py new file mode 100644 index 0000000..14f94ce --- /dev/null +++ b/src/postgrid/types/print_mail/reports/sample_create_params.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List +from typing_extensions import TypedDict + +__all__ = ["SampleCreateParams"] + + +class SampleCreateParams(TypedDict, total=False): + limit: int + """Maximum number of rows to return in the sample.""" + + params: List[str] + """ + Optional parameters to bind to the SQL query (e.g., for placeholders like ? or + $1). + """ diff --git a/src/postgrid/types/print_mail/self_mailer.py b/src/postgrid/types/print_mail/self_mailer.py new file mode 100644 index 0000000..ebf0d08 --- /dev/null +++ b/src/postgrid/types/print_mail/self_mailer.py @@ -0,0 +1,126 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import builtins +from typing import Dict, Optional +from datetime import datetime +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from .contact import Contact +from ..._models import BaseModel +from .cancellation import Cancellation +from .order_status import OrderStatus +from .order_imb_status import OrderImbStatus +from .order_mailing_class import OrderMailingClass +from .order_profiles.self_mailer_size import SelfMailerSize + +__all__ = ["SelfMailer"] + + +class SelfMailer(BaseModel): + id: str + """A unique ID prefixed with self*mailer*""" + + created_at: datetime = FieldInfo(alias="createdAt") + """The UTC time at which this resource was created.""" + + from_: Contact = FieldInfo(alias="from") + """The contact information of the sender.""" + + live: bool + """`true` if this is a live mode resource else `false`.""" + + mailing_class: OrderMailingClass = FieldInfo(alias="mailingClass") + """The mailing class of this order. + + This determines the speed and cost of delivery. See `OrderMailingClass` for more + details. + """ + + object: Literal["self_mailer"] + """Always `self_mailer`.""" + + send_date: datetime = FieldInfo(alias="sendDate") + """This order will transition from `ready` to `printing` on the day after this + date. + + For example, if this is a date on Tuesday, the order will transition to + `printing` on Wednesday at midnight eastern time. + """ + + size: SelfMailerSize + """Enum representing the supported self-mailer sizes.""" + + status: OrderStatus + """See `OrderStatus` for more details on the status of this order.""" + + to: Contact + """The recipient of this order. + + This will be provided even if you delete the underlying contact. + """ + + updated_at: datetime = FieldInfo(alias="updatedAt") + """The UTC time at which this resource was last updated.""" + + cancellation: Optional[Cancellation] = None + """The cancellation details of this order. + + Populated if the order has been cancelled. + """ + + description: Optional[str] = None + """An optional string describing this resource. + + Will be visible in the API and the dashboard. + """ + + imb_date: Optional[datetime] = FieldInfo(alias="imbDate", default=None) + """The last date that the IMB status was updated. + + See `imbStatus` for more details. + """ + + imb_status: Optional[OrderImbStatus] = FieldInfo(alias="imbStatus", default=None) + """The Intelligent Mail Barcode (IMB) status of this order. + + Only populated for US-printed and US-destined orders. This is the most detailed + way to track non-express/certified orders. + """ + + imb_zip_code: Optional[str] = FieldInfo(alias="imbZIPCode", default=None) + """ + The most recent ZIP code of the USPS facility that the order has been processed + through. Only populated when an `imbStatus` is present. + """ + + merge_variables: Optional[Dict[str, builtins.object]] = FieldInfo(alias="mergeVariables", default=None) + """ + These will be merged with the variables in the template or HTML you create this + order with. The keys in this object should match the variable names in the + template _exactly_ as they are case-sensitive. Note that these _do not_ apply to + PDFs uploaded with the order. + """ + + metadata: Optional[Dict[str, builtins.object]] = None + """See the section on Metadata.""" + + tracking_number: Optional[str] = FieldInfo(alias="trackingNumber", default=None) + """The tracking number of this order. + + Populated after an express/certified order has been processed for delivery. + """ + + url: Optional[str] = None + """PostGrid renders a PDF preview for all orders. + + This should be inspected to ensure that the order is correct before it is sent + out because it shows what will be printed and mailed to the recipient. Once the + PDF preview is generated, this field will be returned by all `GET` endpoints + which produce this order. + + This URL is a signed link to the PDF preview. It will expire after a short + period of time. If you need to access this URL after it has expired, you can + regenerate it by calling the `GET` endpoint again. + """ diff --git a/src/postgrid/types/print_mail/self_mailer_create_params.py b/src/postgrid/types/print_mail/self_mailer_create_params.py new file mode 100644 index 0000000..23d2ae2 --- /dev/null +++ b/src/postgrid/types/print_mail/self_mailer_create_params.py @@ -0,0 +1,240 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union +from datetime import datetime +from typing_extensions import Required, Annotated, TypeAlias, TypedDict + +from ..._types import Base64FileInput +from ..._utils import PropertyInfo +from .order_mailing_class import OrderMailingClass +from .order_profiles.self_mailer_size import SelfMailerSize +from ..contact_create_with_first_name_param import ContactCreateWithFirstNameParam +from ..contact_create_with_company_name_param import ContactCreateWithCompanyNameParam + +__all__ = [ + "SelfMailerCreateParams", + "SelfMailerCreateWithHTML", + "SelfMailerCreateWithHTMLFrom", + "SelfMailerCreateWithHTMLTo", + "SelfMailerCreateWithTemplate", + "SelfMailerCreateWithPdfurl", + "SelfMailerCreateWithPdfurlFrom", + "SelfMailerCreateWithPdfurlTo", + "SelfMailerCreateWithPdfFile", + "SelfMailerCreateWithPdfFileFrom", + "SelfMailerCreateWithPdfFileTo", +] + + +class SelfMailerCreateWithHTML(TypedDict, total=False): + from_: Required[Annotated[SelfMailerCreateWithHTMLFrom, PropertyInfo(alias="from")]] + """The contact information of the sender. + + You can pass contact information inline here just like you can for the `to`. + """ + + inside_html: Required[Annotated[str, PropertyInfo(alias="insideHTML")]] + """The HTML content for the inside of the self-mailer. + + You can supply _either_ this or `insideTemplate` but not both. + """ + + outside_html: Required[Annotated[str, PropertyInfo(alias="outsideHTML")]] + """The HTML content for the outside of the self-mailer. + + You can supply _either_ this or `outsideTemplate` but not both. + """ + + size: Required[SelfMailerSize] + """Enum representing the supported self-mailer sizes.""" + + to: Required[SelfMailerCreateWithHTMLTo] + """The recipient of this order. + + You can either supply the contact information inline here or provide a contact + ID. PostGrid will automatically deduplicate contacts regardless of whether you + provide the information inline here or call the contact creation endpoint. + """ + + description: str + """An optional string describing this resource. + + Will be visible in the API and the dashboard. + """ + + mailing_class: Annotated[OrderMailingClass, PropertyInfo(alias="mailingClass")] + """The mailing class of this order. + + If not provided, automatically set to `first_class`. + """ + + merge_variables: Annotated[Dict[str, object], PropertyInfo(alias="mergeVariables")] + """ + These will be merged with the variables in the template or HTML you create this + order with. The keys in this object should match the variable names in the + template _exactly_ as they are case-sensitive. Note that these _do not_ apply to + PDFs uploaded with the order. + """ + + metadata: Dict[str, object] + """See the section on Metadata.""" + + send_date: Annotated[Union[str, datetime], PropertyInfo(alias="sendDate", format="iso8601")] + """This order will transition from `ready` to `printing` on the day after this + date. + + You can use this parameter to schedule orders for a future date. + """ + + +SelfMailerCreateWithHTMLFrom: TypeAlias = Union[ContactCreateWithFirstNameParam, ContactCreateWithCompanyNameParam, str] + +SelfMailerCreateWithHTMLTo: TypeAlias = Union[ContactCreateWithFirstNameParam, ContactCreateWithCompanyNameParam, str] + + +class SelfMailerCreateWithTemplate(TypedDict, total=False): + inside_template: Required[Annotated[str, PropertyInfo(alias="insideTemplate")]] + """The template ID for the inside of the self-mailer. + + You can supply _either_ this or `insideHTML` but not both. + """ + + outside_template: Required[Annotated[str, PropertyInfo(alias="outsideTemplate")]] + """The template ID for the outside of the self-mailer. + + You can supply _either_ this or `outsideHTML` but not both. + """ + + +class SelfMailerCreateWithPdfurl(TypedDict, total=False): + from_: Required[Annotated[SelfMailerCreateWithPdfurlFrom, PropertyInfo(alias="from")]] + """The contact information of the sender. + + You can pass contact information inline here just like you can for the `to`. + """ + + pdf: Required[str] + """A URL pointing to a 2 page PDF file. + + The first page is the inside of the self-mailer and the second page is the + outside (where the address will be stamped on). + """ + + size: Required[SelfMailerSize] + """Enum representing the supported self-mailer sizes.""" + + to: Required[SelfMailerCreateWithPdfurlTo] + """The recipient of this order. + + You can either supply the contact information inline here or provide a contact + ID. PostGrid will automatically deduplicate contacts regardless of whether you + provide the information inline here or call the contact creation endpoint. + """ + + description: str + """An optional string describing this resource. + + Will be visible in the API and the dashboard. + """ + + mailing_class: Annotated[OrderMailingClass, PropertyInfo(alias="mailingClass")] + """The mailing class of this order. + + If not provided, automatically set to `first_class`. + """ + + merge_variables: Annotated[Dict[str, object], PropertyInfo(alias="mergeVariables")] + """ + These will be merged with the variables in the template or HTML you create this + order with. The keys in this object should match the variable names in the + template _exactly_ as they are case-sensitive. Note that these _do not_ apply to + PDFs uploaded with the order. + """ + + metadata: Dict[str, object] + """See the section on Metadata.""" + + send_date: Annotated[Union[str, datetime], PropertyInfo(alias="sendDate", format="iso8601")] + """This order will transition from `ready` to `printing` on the day after this + date. + + You can use this parameter to schedule orders for a future date. + """ + + +SelfMailerCreateWithPdfurlFrom: TypeAlias = Union[ + ContactCreateWithFirstNameParam, ContactCreateWithCompanyNameParam, str +] + +SelfMailerCreateWithPdfurlTo: TypeAlias = Union[ContactCreateWithFirstNameParam, ContactCreateWithCompanyNameParam, str] + + +class SelfMailerCreateWithPdfFile(TypedDict, total=False): + from_: Required[Annotated[SelfMailerCreateWithPdfFileFrom, PropertyInfo(alias="from")]] + """The contact information of the sender. + + You can pass contact information inline here just like you can for the `to`. + """ + + pdf: Required[Annotated[Union[str, Base64FileInput], PropertyInfo(format="base64")]] + """A 2 page PDF file. + + The first page is the inside of the self-mailer and the second page is the + outside (where the address will be stamped on). + """ + + size: Required[SelfMailerSize] + """Enum representing the supported self-mailer sizes.""" + + to: Required[SelfMailerCreateWithPdfFileTo] + """The recipient of this order. + + You can either supply the contact information inline here or provide a contact + ID. PostGrid will automatically deduplicate contacts regardless of whether you + provide the information inline here or call the contact creation endpoint. + """ + + description: str + """An optional string describing this resource. + + Will be visible in the API and the dashboard. + """ + + mailing_class: Annotated[OrderMailingClass, PropertyInfo(alias="mailingClass")] + """The mailing class of this order. + + If not provided, automatically set to `first_class`. + """ + + merge_variables: Annotated[Dict[str, object], PropertyInfo(alias="mergeVariables")] + """ + These will be merged with the variables in the template or HTML you create this + order with. The keys in this object should match the variable names in the + template _exactly_ as they are case-sensitive. Note that these _do not_ apply to + PDFs uploaded with the order. + """ + + metadata: Dict[str, object] + """See the section on Metadata.""" + + send_date: Annotated[Union[str, datetime], PropertyInfo(alias="sendDate", format="iso8601")] + """This order will transition from `ready` to `printing` on the day after this + date. + + You can use this parameter to schedule orders for a future date. + """ + + +SelfMailerCreateWithPdfFileFrom: TypeAlias = Union[ + ContactCreateWithFirstNameParam, ContactCreateWithCompanyNameParam, str +] + +SelfMailerCreateWithPdfFileTo: TypeAlias = Union[ + ContactCreateWithFirstNameParam, ContactCreateWithCompanyNameParam, str +] + +SelfMailerCreateParams: TypeAlias = Union[ + SelfMailerCreateWithHTML, SelfMailerCreateWithTemplate, SelfMailerCreateWithPdfurl, SelfMailerCreateWithPdfFile +] diff --git a/src/postgrid/types/print_mail/self_mailer_list_params.py b/src/postgrid/types/print_mail/self_mailer_list_params.py new file mode 100644 index 0000000..8afdca9 --- /dev/null +++ b/src/postgrid/types/print_mail/self_mailer_list_params.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["SelfMailerListParams"] + + +class SelfMailerListParams(TypedDict, total=False): + limit: int + + search: str + """You can supply any string to help narrow down the list of resources. + + For example, if you pass `"New York"` (quoted), it will return resources that + have that string present somewhere in their response. Alternatively, you can + supply a structured search query. See the documentation on + `StructuredSearchQuery` for more details. + """ + + skip: int diff --git a/src/postgrid/types/print_mail/self_mailer_retrieve_url_response.py b/src/postgrid/types/print_mail/self_mailer_retrieve_url_response.py new file mode 100644 index 0000000..e5db83d --- /dev/null +++ b/src/postgrid/types/print_mail/self_mailer_retrieve_url_response.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + + +from ..._models import BaseModel + +__all__ = ["SelfMailerRetrieveURLResponse"] + + +class SelfMailerRetrieveURLResponse(BaseModel): + id: str + """A unique ID prefixed with self*mailer*""" + + object: str + + url: str + """A signed URL linking to the order preview PDF. + + The link remains valid for 15 minutes from the time of the API call. + """ diff --git a/src/postgrid/types/print_mail/sub_organization.py b/src/postgrid/types/print_mail/sub_organization.py new file mode 100644 index 0000000..ebf3b1a --- /dev/null +++ b/src/postgrid/types/print_mail/sub_organization.py @@ -0,0 +1,42 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from datetime import datetime +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from ..._models import BaseModel + +__all__ = ["SubOrganization"] + + +class SubOrganization(BaseModel): + id: str + """A unique ID prefixed with `sub_org_`.""" + + country_code: str = FieldInfo(alias="countryCode") + """The country code of the sub-organization.""" + + created_at: datetime = FieldInfo(alias="createdAt") + """The UTC time at which this resource was created.""" + + limit: int + """ + The limit of mailings the sub-organization can send before being charged + overages for the month. + """ + + name: str + """The name of the sub-organization.""" + + object: Literal["sub_org"] + """Always `sub_org`.""" + + spend: int + """The current rolling charge for the sub-organization for the month, in cents.""" + + updated_at: datetime = FieldInfo(alias="updatedAt") + """The UTC time at which this resource was last update.""" + + usage: int + """The amount of mail the sub-organization has sent out this month.""" diff --git a/src/postgrid/types/print_mail/sub_organization_list_params.py b/src/postgrid/types/print_mail/sub_organization_list_params.py new file mode 100644 index 0000000..7133ae5 --- /dev/null +++ b/src/postgrid/types/print_mail/sub_organization_list_params.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["SubOrganizationListParams"] + + +class SubOrganizationListParams(TypedDict, total=False): + limit: int + + search: str + """You can supply any string to help narrow down the list of resources. + + For example, if you pass `"New York"` (quoted), it will return resources that + have that string present somewhere in their response. Alternatively, you can + supply a structured search query. See the documentation on + `StructuredSearchQuery` for more details. + """ + + skip: int diff --git a/src/postgrid/types/print_mail/sub_organization_retrieve_users_params.py b/src/postgrid/types/print_mail/sub_organization_retrieve_users_params.py new file mode 100644 index 0000000..2ddca13 --- /dev/null +++ b/src/postgrid/types/print_mail/sub_organization_retrieve_users_params.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["SubOrganizationRetrieveUsersParams"] + + +class SubOrganizationRetrieveUsersParams(TypedDict, total=False): + limit: int + + search: str + """You can supply any string to help narrow down the list of resources. + + For example, if you pass `"New York"` (quoted), it will return resources that + have that string present somewhere in their response. Alternatively, you can + supply a structured search query. See the documentation on + `StructuredSearchQuery` for more details. + """ + + skip: int diff --git a/src/postgrid/types/print_mail/sub_organization_retrieve_users_response.py b/src/postgrid/types/print_mail/sub_organization_retrieve_users_response.py new file mode 100644 index 0000000..e6b8e5b --- /dev/null +++ b/src/postgrid/types/print_mail/sub_organization_retrieve_users_response.py @@ -0,0 +1,54 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime +from typing_extensions import TypeAlias + +from pydantic import Field as FieldInfo + +from ..._models import BaseModel +from .email_preferences import EmailPreferences + +__all__ = ["SubOrganizationRetrieveUsersResponse", "SubOrganizationRetrieveUsersResponseItem"] + + +class SubOrganizationRetrieveUsersResponseItem(BaseModel): + id: str + """A unique ID prefixed with `user_`.""" + + email: str + """The email of the user.""" + + name: str + """The name of the user.""" + + organization: str + """A unique ID prefixed with `user_`.""" + + pending_invite: bool = FieldInfo(alias="pendingInvite") + """Indicates if the user has a pending invite.""" + + roles: List[str] + """The roles given to the user. Roles can be used to restrict access for users.""" + + verified_email: bool = FieldInfo(alias="verifiedEmail") + """Indicates if the user has a verified email or not.""" + + email_preferences: Optional[EmailPreferences] = FieldInfo(alias="emailPreferences", default=None) + """A set of preferences for how a user should receive emails.""" + + last_login_time: Optional[datetime] = FieldInfo(alias="lastLoginTime", default=None) + """The date and time at which the user last logged in.""" + + phone_number: Optional[str] = FieldInfo(alias="phoneNumber", default=None) + """The phone number of the user.""" + + previous_emails: Optional[List[str]] = FieldInfo(alias="previousEmails", default=None) + """A list of emails the user has previously had. + + If a user has changed their email before, this list will be populated with all + of the emails they once had. + """ + + +SubOrganizationRetrieveUsersResponse: TypeAlias = List[SubOrganizationRetrieveUsersResponseItem] diff --git a/src/postgrid/types/print_mail/sub_organization_update_params.py b/src/postgrid/types/print_mail/sub_organization_update_params.py new file mode 100644 index 0000000..16a60a1 --- /dev/null +++ b/src/postgrid/types/print_mail/sub_organization_update_params.py @@ -0,0 +1,29 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, Annotated, TypedDict + +from ..._utils import PropertyInfo + +__all__ = ["SubOrganizationUpdateParams"] + + +class SubOrganizationUpdateParams(TypedDict, total=False): + country_code: Required[Annotated[str, PropertyInfo(alias="countryCode")]] + """The country code of the sub-organization.""" + + email: Required[str] + """The email of the user to be created alongside the sub-organization.""" + + name: Required[str] + """The name of the user to be created alongside the sub-organization.""" + + organization_name: Required[Annotated[str, PropertyInfo(alias="organizationName")]] + """The name of the sub-organization.""" + + password: Required[str] + """The password of the user to be created alongside the sub-organization.""" + + phone_number: Annotated[str, PropertyInfo(alias="phoneNumber")] + """The phone number of the user to be created alongside the sub-organization.""" diff --git a/src/postgrid/types/print_mail/sub_organization_update_response.py b/src/postgrid/types/print_mail/sub_organization_update_response.py new file mode 100644 index 0000000..0fb225a --- /dev/null +++ b/src/postgrid/types/print_mail/sub_organization_update_response.py @@ -0,0 +1,73 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime + +from pydantic import Field as FieldInfo + +from ..._models import BaseModel +from .sub_organization import SubOrganization +from .email_preferences import EmailPreferences + +__all__ = ["SubOrganizationUpdateResponse", "User", "UserAPIKey"] + + +class UserAPIKey(BaseModel): + value: str + """The value of the API key.""" + + active_until: Optional[datetime] = FieldInfo(alias="activeUntil", default=None) + """An optional date which the API key is active until. + + After this date, the API key will no longer be valid. + """ + + +class User(BaseModel): + id: str + """A unique ID prefixed with `user_`.""" + + api_keys: List[UserAPIKey] = FieldInfo(alias="apiKeys") + """The user's API keys. Contains live and test mode API keys.""" + + email: str + """The email of the user.""" + + name: str + """The name of the user.""" + + organization: str + """A unique ID prefixed with `user_`.""" + + pending_invite: bool = FieldInfo(alias="pendingInvite") + """Indicates if the user has a pending invite.""" + + roles: List[str] + """The roles given to the user. Roles can be used to restrict access for users.""" + + verified_email: bool = FieldInfo(alias="verifiedEmail") + """Indicates if the user has a verified email or not.""" + + email_preferences: Optional[EmailPreferences] = FieldInfo(alias="emailPreferences", default=None) + """A set of preferences for how a user should receive emails.""" + + last_login_time: Optional[datetime] = FieldInfo(alias="lastLoginTime", default=None) + """The date and time at which the user last logged in.""" + + phone_number: Optional[str] = FieldInfo(alias="phoneNumber", default=None) + """The phone number of the user.""" + + previous_emails: Optional[List[str]] = FieldInfo(alias="previousEmails", default=None) + """A list of emails the user has previously had. + + If a user has changed their email before, this list will be populated with all + of the emails they once had. + """ + + +class SubOrganizationUpdateResponse(BaseModel): + sub_organization: SubOrganization = FieldInfo(alias="subOrganization") + """The Sub-Organization object.""" + + user: User + """The user object.""" diff --git a/src/postgrid/types/print_mail/template.py b/src/postgrid/types/print_mail/template.py new file mode 100644 index 0000000..a8382b4 --- /dev/null +++ b/src/postgrid/types/print_mail/template.py @@ -0,0 +1,41 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import builtins +from typing import Dict, Optional +from datetime import datetime +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from ..._models import BaseModel + +__all__ = ["Template"] + + +class Template(BaseModel): + id: str + """A unique ID prefixed with template\\__""" + + created_at: datetime = FieldInfo(alias="createdAt") + """The UTC time at which this resource was created.""" + + live: bool + """`true` if this is a live mode resource else `false`.""" + + object: Literal["template"] + """Always `template`.""" + + updated_at: datetime = FieldInfo(alias="updatedAt") + """The UTC time at which this resource was last updated.""" + + description: Optional[str] = None + """An optional string describing this resource. + + Will be visible in the API and the dashboard. + """ + + html: Optional[str] = None + """The HTML content of this template.""" + + metadata: Optional[Dict[str, builtins.object]] = None + """See the section on Metadata.""" diff --git a/src/postgrid/types/print_mail/template_create_params.py b/src/postgrid/types/print_mail/template_create_params.py new file mode 100644 index 0000000..e9b2c2e --- /dev/null +++ b/src/postgrid/types/print_mail/template_create_params.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict +from typing_extensions import TypedDict + +__all__ = ["TemplateCreateParams"] + + +class TemplateCreateParams(TypedDict, total=False): + description: str + """An optional string describing this resource. + + Will be visible in the API and the dashboard. + """ + + html: str + """The HTML content of this template.""" + + metadata: Dict[str, object] + """See the section on Metadata.""" diff --git a/src/postgrid/types/print_mail/template_delete_response.py b/src/postgrid/types/print_mail/template_delete_response.py new file mode 100644 index 0000000..affc742 --- /dev/null +++ b/src/postgrid/types/print_mail/template_delete_response.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["TemplateDeleteResponse"] + + +class TemplateDeleteResponse(BaseModel): + id: str + """A unique ID prefixed with template\\__""" + + deleted: Literal[True] + + object: Literal["template"] + """Always `template`.""" diff --git a/src/postgrid/types/print_mail/template_list_params.py b/src/postgrid/types/print_mail/template_list_params.py new file mode 100644 index 0000000..ea22b8f --- /dev/null +++ b/src/postgrid/types/print_mail/template_list_params.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["TemplateListParams"] + + +class TemplateListParams(TypedDict, total=False): + limit: int + + search: str + """You can supply any string to help narrow down the list of resources. + + For example, if you pass `"New York"` (quoted), it will return resources that + have that string present somewhere in their response. Alternatively, you can + supply a structured search query. See the documentation on + `StructuredSearchQuery` for more details. + """ + + skip: int diff --git a/src/postgrid/types/print_mail/template_update_params.py b/src/postgrid/types/print_mail/template_update_params.py new file mode 100644 index 0000000..2254d92 --- /dev/null +++ b/src/postgrid/types/print_mail/template_update_params.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict +from typing_extensions import TypedDict + +__all__ = ["TemplateUpdateParams"] + + +class TemplateUpdateParams(TypedDict, total=False): + description: str + """An optional string describing this resource. + + Will be visible in the API and the dashboard. + """ + + html: str + """The HTML content of this template.""" + + metadata: Dict[str, object] + """See the section on Metadata.""" diff --git a/src/postgrid/types/print_mail/verification_status_count.py b/src/postgrid/types/print_mail/verification_status_count.py new file mode 100644 index 0000000..e97d994 --- /dev/null +++ b/src/postgrid/types/print_mail/verification_status_count.py @@ -0,0 +1,19 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + + +from pydantic import Field as FieldInfo + +from ..._models import BaseModel + +__all__ = ["VerificationStatusCount"] + + +class VerificationStatusCount(BaseModel): + corrected_count: int = FieldInfo(alias="correctedCount") + """Number of contacts that were corrected during verification.""" + + failed_count: int = FieldInfo(alias="failedCount") + """Number of contacts that failed verification.""" + + verified_count: int = FieldInfo(alias="verifiedCount") + """Number of contacts that were verified without changes.""" diff --git a/src/postgrid/types/status.py b/src/postgrid/types/status.py new file mode 100644 index 0000000..d7f18aa --- /dev/null +++ b/src/postgrid/types/status.py @@ -0,0 +1,7 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing_extensions import Literal, TypeAlias + +__all__ = ["Status"] + +Status: TypeAlias = Literal["verified", "corrected", "failed"] diff --git a/tests/__init__.py b/tests/__init__.py index e69de29..fd8019a 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/__init__.py b/tests/api_resources/__init__.py new file mode 100644 index 0000000..fd8019a --- /dev/null +++ b/tests/api_resources/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/print_mail/__init__.py b/tests/api_resources/print_mail/__init__.py new file mode 100644 index 0000000..fd8019a --- /dev/null +++ b/tests/api_resources/print_mail/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/print_mail/order_profiles/__init__.py b/tests/api_resources/print_mail/order_profiles/__init__.py new file mode 100644 index 0000000..fd8019a --- /dev/null +++ b/tests/api_resources/print_mail/order_profiles/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/print_mail/order_profiles/test_cheques.py b/tests/api_resources/print_mail/order_profiles/test_cheques.py new file mode 100644 index 0000000..33f20e8 --- /dev/null +++ b/tests/api_resources/print_mail/order_profiles/test_cheques.py @@ -0,0 +1,545 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from postgrid import PostGrid, AsyncPostGrid +from tests.utils import assert_matches_type +from postgrid.pagination import SyncSkipLimit, AsyncSkipLimit +from postgrid.types.print_mail.order_profiles import ( + ChequeProfile, + ChequeListResponse, + ChequeDeleteResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestCheques: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip() + @parametrize + def test_method_create(self, client: PostGrid) -> None: + cheque = client.print_mail.order_profiles.cheques.create( + bank_account="bankAccount", + size="us_letter", + ) + assert_matches_type(ChequeProfile, cheque, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_create_with_all_params(self, client: PostGrid) -> None: + cheque = client.print_mail.order_profiles.cheques.create( + bank_account="bankAccount", + size="us_letter", + expand=["string"], + currency_code="CAD", + description="description", + letter_pdf="U3RhaW5sZXNzIHJvY2tz", + letter_template="letterTemplate", + logo="https://example.com", + mailing_class="first_class", + memo="memo", + merge_variables={"foo": "bar"}, + message="message", + metadata={"foo": "string"}, + ) + assert_matches_type(ChequeProfile, cheque, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_create(self, client: PostGrid) -> None: + response = client.print_mail.order_profiles.cheques.with_raw_response.create( + bank_account="bankAccount", + size="us_letter", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + cheque = response.parse() + assert_matches_type(ChequeProfile, cheque, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_create(self, client: PostGrid) -> None: + with client.print_mail.order_profiles.cheques.with_streaming_response.create( + bank_account="bankAccount", + size="us_letter", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + cheque = response.parse() + assert_matches_type(ChequeProfile, cheque, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_method_retrieve(self, client: PostGrid) -> None: + cheque = client.print_mail.order_profiles.cheques.retrieve( + id="id", + ) + assert_matches_type(ChequeProfile, cheque, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_retrieve_with_all_params(self, client: PostGrid) -> None: + cheque = client.print_mail.order_profiles.cheques.retrieve( + id="id", + expand=["string"], + ) + assert_matches_type(ChequeProfile, cheque, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_retrieve(self, client: PostGrid) -> None: + response = client.print_mail.order_profiles.cheques.with_raw_response.retrieve( + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + cheque = response.parse() + assert_matches_type(ChequeProfile, cheque, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_retrieve(self, client: PostGrid) -> None: + with client.print_mail.order_profiles.cheques.with_streaming_response.retrieve( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + cheque = response.parse() + assert_matches_type(ChequeProfile, cheque, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_path_params_retrieve(self, client: PostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.print_mail.order_profiles.cheques.with_raw_response.retrieve( + id="", + ) + + @pytest.mark.skip() + @parametrize + def test_method_update(self, client: PostGrid) -> None: + cheque = client.print_mail.order_profiles.cheques.update( + id="id", + bank_account="bankAccount", + size="us_letter", + ) + assert_matches_type(ChequeProfile, cheque, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_update_with_all_params(self, client: PostGrid) -> None: + cheque = client.print_mail.order_profiles.cheques.update( + id="id", + bank_account="bankAccount", + size="us_letter", + expand=["string"], + currency_code="CAD", + description="description", + letter_pdf="U3RhaW5sZXNzIHJvY2tz", + letter_template="letterTemplate", + logo="https://example.com", + mailing_class="first_class", + memo="memo", + merge_variables={"foo": "bar"}, + message="message", + metadata={"foo": "string"}, + ) + assert_matches_type(ChequeProfile, cheque, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_update(self, client: PostGrid) -> None: + response = client.print_mail.order_profiles.cheques.with_raw_response.update( + id="id", + bank_account="bankAccount", + size="us_letter", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + cheque = response.parse() + assert_matches_type(ChequeProfile, cheque, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_update(self, client: PostGrid) -> None: + with client.print_mail.order_profiles.cheques.with_streaming_response.update( + id="id", + bank_account="bankAccount", + size="us_letter", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + cheque = response.parse() + assert_matches_type(ChequeProfile, cheque, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_path_params_update(self, client: PostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.print_mail.order_profiles.cheques.with_raw_response.update( + id="", + bank_account="bankAccount", + size="us_letter", + ) + + @pytest.mark.skip() + @parametrize + def test_method_list(self, client: PostGrid) -> None: + cheque = client.print_mail.order_profiles.cheques.list() + assert_matches_type(SyncSkipLimit[ChequeListResponse], cheque, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_list_with_all_params(self, client: PostGrid) -> None: + cheque = client.print_mail.order_profiles.cheques.list( + limit=0, + search="search", + skip=0, + ) + assert_matches_type(SyncSkipLimit[ChequeListResponse], cheque, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_list(self, client: PostGrid) -> None: + response = client.print_mail.order_profiles.cheques.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + cheque = response.parse() + assert_matches_type(SyncSkipLimit[ChequeListResponse], cheque, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_list(self, client: PostGrid) -> None: + with client.print_mail.order_profiles.cheques.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + cheque = response.parse() + assert_matches_type(SyncSkipLimit[ChequeListResponse], cheque, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_method_delete(self, client: PostGrid) -> None: + cheque = client.print_mail.order_profiles.cheques.delete( + "id", + ) + assert_matches_type(ChequeDeleteResponse, cheque, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_delete(self, client: PostGrid) -> None: + response = client.print_mail.order_profiles.cheques.with_raw_response.delete( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + cheque = response.parse() + assert_matches_type(ChequeDeleteResponse, cheque, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_delete(self, client: PostGrid) -> None: + with client.print_mail.order_profiles.cheques.with_streaming_response.delete( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + cheque = response.parse() + assert_matches_type(ChequeDeleteResponse, cheque, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_path_params_delete(self, client: PostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.print_mail.order_profiles.cheques.with_raw_response.delete( + "", + ) + + +class TestAsyncCheques: + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip() + @parametrize + async def test_method_create(self, async_client: AsyncPostGrid) -> None: + cheque = await async_client.print_mail.order_profiles.cheques.create( + bank_account="bankAccount", + size="us_letter", + ) + assert_matches_type(ChequeProfile, cheque, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncPostGrid) -> None: + cheque = await async_client.print_mail.order_profiles.cheques.create( + bank_account="bankAccount", + size="us_letter", + expand=["string"], + currency_code="CAD", + description="description", + letter_pdf="U3RhaW5sZXNzIHJvY2tz", + letter_template="letterTemplate", + logo="https://example.com", + mailing_class="first_class", + memo="memo", + merge_variables={"foo": "bar"}, + message="message", + metadata={"foo": "string"}, + ) + assert_matches_type(ChequeProfile, cheque, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_create(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.order_profiles.cheques.with_raw_response.create( + bank_account="bankAccount", + size="us_letter", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + cheque = await response.parse() + assert_matches_type(ChequeProfile, cheque, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_create(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.order_profiles.cheques.with_streaming_response.create( + bank_account="bankAccount", + size="us_letter", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + cheque = await response.parse() + assert_matches_type(ChequeProfile, cheque, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_method_retrieve(self, async_client: AsyncPostGrid) -> None: + cheque = await async_client.print_mail.order_profiles.cheques.retrieve( + id="id", + ) + assert_matches_type(ChequeProfile, cheque, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_retrieve_with_all_params(self, async_client: AsyncPostGrid) -> None: + cheque = await async_client.print_mail.order_profiles.cheques.retrieve( + id="id", + expand=["string"], + ) + assert_matches_type(ChequeProfile, cheque, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.order_profiles.cheques.with_raw_response.retrieve( + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + cheque = await response.parse() + assert_matches_type(ChequeProfile, cheque, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.order_profiles.cheques.with_streaming_response.retrieve( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + cheque = await response.parse() + assert_matches_type(ChequeProfile, cheque, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncPostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.print_mail.order_profiles.cheques.with_raw_response.retrieve( + id="", + ) + + @pytest.mark.skip() + @parametrize + async def test_method_update(self, async_client: AsyncPostGrid) -> None: + cheque = await async_client.print_mail.order_profiles.cheques.update( + id="id", + bank_account="bankAccount", + size="us_letter", + ) + assert_matches_type(ChequeProfile, cheque, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_update_with_all_params(self, async_client: AsyncPostGrid) -> None: + cheque = await async_client.print_mail.order_profiles.cheques.update( + id="id", + bank_account="bankAccount", + size="us_letter", + expand=["string"], + currency_code="CAD", + description="description", + letter_pdf="U3RhaW5sZXNzIHJvY2tz", + letter_template="letterTemplate", + logo="https://example.com", + mailing_class="first_class", + memo="memo", + merge_variables={"foo": "bar"}, + message="message", + metadata={"foo": "string"}, + ) + assert_matches_type(ChequeProfile, cheque, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_update(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.order_profiles.cheques.with_raw_response.update( + id="id", + bank_account="bankAccount", + size="us_letter", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + cheque = await response.parse() + assert_matches_type(ChequeProfile, cheque, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_update(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.order_profiles.cheques.with_streaming_response.update( + id="id", + bank_account="bankAccount", + size="us_letter", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + cheque = await response.parse() + assert_matches_type(ChequeProfile, cheque, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_path_params_update(self, async_client: AsyncPostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.print_mail.order_profiles.cheques.with_raw_response.update( + id="", + bank_account="bankAccount", + size="us_letter", + ) + + @pytest.mark.skip() + @parametrize + async def test_method_list(self, async_client: AsyncPostGrid) -> None: + cheque = await async_client.print_mail.order_profiles.cheques.list() + assert_matches_type(AsyncSkipLimit[ChequeListResponse], cheque, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncPostGrid) -> None: + cheque = await async_client.print_mail.order_profiles.cheques.list( + limit=0, + search="search", + skip=0, + ) + assert_matches_type(AsyncSkipLimit[ChequeListResponse], cheque, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_list(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.order_profiles.cheques.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + cheque = await response.parse() + assert_matches_type(AsyncSkipLimit[ChequeListResponse], cheque, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_list(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.order_profiles.cheques.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + cheque = await response.parse() + assert_matches_type(AsyncSkipLimit[ChequeListResponse], cheque, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_method_delete(self, async_client: AsyncPostGrid) -> None: + cheque = await async_client.print_mail.order_profiles.cheques.delete( + "id", + ) + assert_matches_type(ChequeDeleteResponse, cheque, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_delete(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.order_profiles.cheques.with_raw_response.delete( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + cheque = await response.parse() + assert_matches_type(ChequeDeleteResponse, cheque, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.order_profiles.cheques.with_streaming_response.delete( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + cheque = await response.parse() + assert_matches_type(ChequeDeleteResponse, cheque, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_path_params_delete(self, async_client: AsyncPostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.print_mail.order_profiles.cheques.with_raw_response.delete( + "", + ) diff --git a/tests/api_resources/print_mail/order_profiles/test_letters.py b/tests/api_resources/print_mail/order_profiles/test_letters.py new file mode 100644 index 0000000..19b9bed --- /dev/null +++ b/tests/api_resources/print_mail/order_profiles/test_letters.py @@ -0,0 +1,540 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from postgrid import PostGrid, AsyncPostGrid +from tests.utils import assert_matches_type +from postgrid.pagination import SyncSkipLimit, AsyncSkipLimit +from postgrid.types.print_mail.order_profiles import ( + LetterProfile, + LetterDeleteResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestLetters: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip() + @parametrize + def test_method_create(self, client: PostGrid) -> None: + letter = client.print_mail.order_profiles.letters.create( + size="us_letter", + ) + assert_matches_type(LetterProfile, letter, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_create_with_all_params(self, client: PostGrid) -> None: + letter = client.print_mail.order_profiles.letters.create( + size="us_letter", + expand=["string"], + address_placement="top_first_page", + attached_pdf={ + "file": "https://example.com", + "placement": "before_template", + }, + color=True, + description="Monthly Newsletter Profile", + double_sided=True, + envelope="envelope", + mailing_class="first_class", + merge_variables={"salutation": "bar"}, + metadata={"campaign": "Q1 Newsletter"}, + pdf="https://example.com", + perforated_page=1, + return_envelope="returnEnvelope", + template="template_abc", + ) + assert_matches_type(LetterProfile, letter, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_create(self, client: PostGrid) -> None: + response = client.print_mail.order_profiles.letters.with_raw_response.create( + size="us_letter", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + letter = response.parse() + assert_matches_type(LetterProfile, letter, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_create(self, client: PostGrid) -> None: + with client.print_mail.order_profiles.letters.with_streaming_response.create( + size="us_letter", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + letter = response.parse() + assert_matches_type(LetterProfile, letter, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_method_retrieve(self, client: PostGrid) -> None: + letter = client.print_mail.order_profiles.letters.retrieve( + id="id", + ) + assert_matches_type(LetterProfile, letter, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_retrieve_with_all_params(self, client: PostGrid) -> None: + letter = client.print_mail.order_profiles.letters.retrieve( + id="id", + expand=["string"], + ) + assert_matches_type(LetterProfile, letter, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_retrieve(self, client: PostGrid) -> None: + response = client.print_mail.order_profiles.letters.with_raw_response.retrieve( + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + letter = response.parse() + assert_matches_type(LetterProfile, letter, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_retrieve(self, client: PostGrid) -> None: + with client.print_mail.order_profiles.letters.with_streaming_response.retrieve( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + letter = response.parse() + assert_matches_type(LetterProfile, letter, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_path_params_retrieve(self, client: PostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.print_mail.order_profiles.letters.with_raw_response.retrieve( + id="", + ) + + @pytest.mark.skip() + @parametrize + def test_method_update(self, client: PostGrid) -> None: + letter = client.print_mail.order_profiles.letters.update( + id="id", + ) + assert_matches_type(LetterProfile, letter, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_update_with_all_params(self, client: PostGrid) -> None: + letter = client.print_mail.order_profiles.letters.update( + id="id", + expand=["string"], + address_placement="top_first_page", + attached_pdf={ + "file": "https://example.com", + "placement": "before_template", + }, + color=False, + description="Updated Newsletter Profile", + double_sided=True, + envelope="envelope", + mailing_class="first_class", + merge_variables={"foo": "bar"}, + metadata={"foo": "string"}, + pdf="https://example.com", + perforated_page=1, + return_envelope="returnEnvelope", + template="template", + ) + assert_matches_type(LetterProfile, letter, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_update(self, client: PostGrid) -> None: + response = client.print_mail.order_profiles.letters.with_raw_response.update( + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + letter = response.parse() + assert_matches_type(LetterProfile, letter, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_update(self, client: PostGrid) -> None: + with client.print_mail.order_profiles.letters.with_streaming_response.update( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + letter = response.parse() + assert_matches_type(LetterProfile, letter, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_path_params_update(self, client: PostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.print_mail.order_profiles.letters.with_raw_response.update( + id="", + ) + + @pytest.mark.skip() + @parametrize + def test_method_list(self, client: PostGrid) -> None: + letter = client.print_mail.order_profiles.letters.list() + assert_matches_type(SyncSkipLimit[LetterProfile], letter, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_list_with_all_params(self, client: PostGrid) -> None: + letter = client.print_mail.order_profiles.letters.list( + limit=0, + search="search", + skip=0, + ) + assert_matches_type(SyncSkipLimit[LetterProfile], letter, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_list(self, client: PostGrid) -> None: + response = client.print_mail.order_profiles.letters.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + letter = response.parse() + assert_matches_type(SyncSkipLimit[LetterProfile], letter, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_list(self, client: PostGrid) -> None: + with client.print_mail.order_profiles.letters.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + letter = response.parse() + assert_matches_type(SyncSkipLimit[LetterProfile], letter, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_method_delete(self, client: PostGrid) -> None: + letter = client.print_mail.order_profiles.letters.delete( + "id", + ) + assert_matches_type(LetterDeleteResponse, letter, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_delete(self, client: PostGrid) -> None: + response = client.print_mail.order_profiles.letters.with_raw_response.delete( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + letter = response.parse() + assert_matches_type(LetterDeleteResponse, letter, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_delete(self, client: PostGrid) -> None: + with client.print_mail.order_profiles.letters.with_streaming_response.delete( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + letter = response.parse() + assert_matches_type(LetterDeleteResponse, letter, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_path_params_delete(self, client: PostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.print_mail.order_profiles.letters.with_raw_response.delete( + "", + ) + + +class TestAsyncLetters: + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip() + @parametrize + async def test_method_create(self, async_client: AsyncPostGrid) -> None: + letter = await async_client.print_mail.order_profiles.letters.create( + size="us_letter", + ) + assert_matches_type(LetterProfile, letter, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncPostGrid) -> None: + letter = await async_client.print_mail.order_profiles.letters.create( + size="us_letter", + expand=["string"], + address_placement="top_first_page", + attached_pdf={ + "file": "https://example.com", + "placement": "before_template", + }, + color=True, + description="Monthly Newsletter Profile", + double_sided=True, + envelope="envelope", + mailing_class="first_class", + merge_variables={"salutation": "bar"}, + metadata={"campaign": "Q1 Newsletter"}, + pdf="https://example.com", + perforated_page=1, + return_envelope="returnEnvelope", + template="template_abc", + ) + assert_matches_type(LetterProfile, letter, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_create(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.order_profiles.letters.with_raw_response.create( + size="us_letter", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + letter = await response.parse() + assert_matches_type(LetterProfile, letter, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_create(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.order_profiles.letters.with_streaming_response.create( + size="us_letter", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + letter = await response.parse() + assert_matches_type(LetterProfile, letter, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_method_retrieve(self, async_client: AsyncPostGrid) -> None: + letter = await async_client.print_mail.order_profiles.letters.retrieve( + id="id", + ) + assert_matches_type(LetterProfile, letter, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_retrieve_with_all_params(self, async_client: AsyncPostGrid) -> None: + letter = await async_client.print_mail.order_profiles.letters.retrieve( + id="id", + expand=["string"], + ) + assert_matches_type(LetterProfile, letter, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.order_profiles.letters.with_raw_response.retrieve( + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + letter = await response.parse() + assert_matches_type(LetterProfile, letter, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.order_profiles.letters.with_streaming_response.retrieve( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + letter = await response.parse() + assert_matches_type(LetterProfile, letter, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncPostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.print_mail.order_profiles.letters.with_raw_response.retrieve( + id="", + ) + + @pytest.mark.skip() + @parametrize + async def test_method_update(self, async_client: AsyncPostGrid) -> None: + letter = await async_client.print_mail.order_profiles.letters.update( + id="id", + ) + assert_matches_type(LetterProfile, letter, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_update_with_all_params(self, async_client: AsyncPostGrid) -> None: + letter = await async_client.print_mail.order_profiles.letters.update( + id="id", + expand=["string"], + address_placement="top_first_page", + attached_pdf={ + "file": "https://example.com", + "placement": "before_template", + }, + color=False, + description="Updated Newsletter Profile", + double_sided=True, + envelope="envelope", + mailing_class="first_class", + merge_variables={"foo": "bar"}, + metadata={"foo": "string"}, + pdf="https://example.com", + perforated_page=1, + return_envelope="returnEnvelope", + template="template", + ) + assert_matches_type(LetterProfile, letter, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_update(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.order_profiles.letters.with_raw_response.update( + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + letter = await response.parse() + assert_matches_type(LetterProfile, letter, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_update(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.order_profiles.letters.with_streaming_response.update( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + letter = await response.parse() + assert_matches_type(LetterProfile, letter, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_path_params_update(self, async_client: AsyncPostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.print_mail.order_profiles.letters.with_raw_response.update( + id="", + ) + + @pytest.mark.skip() + @parametrize + async def test_method_list(self, async_client: AsyncPostGrid) -> None: + letter = await async_client.print_mail.order_profiles.letters.list() + assert_matches_type(AsyncSkipLimit[LetterProfile], letter, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncPostGrid) -> None: + letter = await async_client.print_mail.order_profiles.letters.list( + limit=0, + search="search", + skip=0, + ) + assert_matches_type(AsyncSkipLimit[LetterProfile], letter, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_list(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.order_profiles.letters.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + letter = await response.parse() + assert_matches_type(AsyncSkipLimit[LetterProfile], letter, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_list(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.order_profiles.letters.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + letter = await response.parse() + assert_matches_type(AsyncSkipLimit[LetterProfile], letter, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_method_delete(self, async_client: AsyncPostGrid) -> None: + letter = await async_client.print_mail.order_profiles.letters.delete( + "id", + ) + assert_matches_type(LetterDeleteResponse, letter, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_delete(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.order_profiles.letters.with_raw_response.delete( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + letter = await response.parse() + assert_matches_type(LetterDeleteResponse, letter, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.order_profiles.letters.with_streaming_response.delete( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + letter = await response.parse() + assert_matches_type(LetterDeleteResponse, letter, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_path_params_delete(self, async_client: AsyncPostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.print_mail.order_profiles.letters.with_raw_response.delete( + "", + ) diff --git a/tests/api_resources/print_mail/order_profiles/test_postcards.py b/tests/api_resources/print_mail/order_profiles/test_postcards.py new file mode 100644 index 0000000..30ca7dc --- /dev/null +++ b/tests/api_resources/print_mail/order_profiles/test_postcards.py @@ -0,0 +1,504 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from postgrid import PostGrid, AsyncPostGrid +from tests.utils import assert_matches_type +from postgrid.pagination import SyncSkipLimit, AsyncSkipLimit +from postgrid.types.print_mail.order_profiles import ( + PostcardProfile, + PostcardDeleteResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestPostcards: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip() + @parametrize + def test_method_create(self, client: PostGrid) -> None: + postcard = client.print_mail.order_profiles.postcards.create( + size="6x4", + ) + assert_matches_type(PostcardProfile, postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_create_with_all_params(self, client: PostGrid) -> None: + postcard = client.print_mail.order_profiles.postcards.create( + size="6x4", + expand=["string"], + back_template="backTemplate", + description="description", + front_template="frontTemplate", + mailing_class="first_class", + merge_variables={"foo": "bar"}, + metadata={"foo": "string"}, + pdf="https://example.com", + ) + assert_matches_type(PostcardProfile, postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_create(self, client: PostGrid) -> None: + response = client.print_mail.order_profiles.postcards.with_raw_response.create( + size="6x4", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + postcard = response.parse() + assert_matches_type(PostcardProfile, postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_create(self, client: PostGrid) -> None: + with client.print_mail.order_profiles.postcards.with_streaming_response.create( + size="6x4", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + postcard = response.parse() + assert_matches_type(PostcardProfile, postcard, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_method_retrieve(self, client: PostGrid) -> None: + postcard = client.print_mail.order_profiles.postcards.retrieve( + id="id", + ) + assert_matches_type(PostcardProfile, postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_retrieve_with_all_params(self, client: PostGrid) -> None: + postcard = client.print_mail.order_profiles.postcards.retrieve( + id="id", + expand=["string"], + ) + assert_matches_type(PostcardProfile, postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_retrieve(self, client: PostGrid) -> None: + response = client.print_mail.order_profiles.postcards.with_raw_response.retrieve( + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + postcard = response.parse() + assert_matches_type(PostcardProfile, postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_retrieve(self, client: PostGrid) -> None: + with client.print_mail.order_profiles.postcards.with_streaming_response.retrieve( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + postcard = response.parse() + assert_matches_type(PostcardProfile, postcard, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_path_params_retrieve(self, client: PostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.print_mail.order_profiles.postcards.with_raw_response.retrieve( + id="", + ) + + @pytest.mark.skip() + @parametrize + def test_method_update(self, client: PostGrid) -> None: + postcard = client.print_mail.order_profiles.postcards.update( + id="id", + ) + assert_matches_type(PostcardProfile, postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_update_with_all_params(self, client: PostGrid) -> None: + postcard = client.print_mail.order_profiles.postcards.update( + id="id", + expand=["string"], + back_template="backTemplate", + description="description", + front_template="frontTemplate", + mailing_class="first_class", + merge_variables={"foo": "bar"}, + metadata={"foo": "string"}, + pdf="https://example.com", + ) + assert_matches_type(PostcardProfile, postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_update(self, client: PostGrid) -> None: + response = client.print_mail.order_profiles.postcards.with_raw_response.update( + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + postcard = response.parse() + assert_matches_type(PostcardProfile, postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_update(self, client: PostGrid) -> None: + with client.print_mail.order_profiles.postcards.with_streaming_response.update( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + postcard = response.parse() + assert_matches_type(PostcardProfile, postcard, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_path_params_update(self, client: PostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.print_mail.order_profiles.postcards.with_raw_response.update( + id="", + ) + + @pytest.mark.skip() + @parametrize + def test_method_list(self, client: PostGrid) -> None: + postcard = client.print_mail.order_profiles.postcards.list() + assert_matches_type(SyncSkipLimit[PostcardProfile], postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_list_with_all_params(self, client: PostGrid) -> None: + postcard = client.print_mail.order_profiles.postcards.list( + limit=0, + search="search", + skip=0, + ) + assert_matches_type(SyncSkipLimit[PostcardProfile], postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_list(self, client: PostGrid) -> None: + response = client.print_mail.order_profiles.postcards.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + postcard = response.parse() + assert_matches_type(SyncSkipLimit[PostcardProfile], postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_list(self, client: PostGrid) -> None: + with client.print_mail.order_profiles.postcards.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + postcard = response.parse() + assert_matches_type(SyncSkipLimit[PostcardProfile], postcard, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_method_delete(self, client: PostGrid) -> None: + postcard = client.print_mail.order_profiles.postcards.delete( + "id", + ) + assert_matches_type(PostcardDeleteResponse, postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_delete(self, client: PostGrid) -> None: + response = client.print_mail.order_profiles.postcards.with_raw_response.delete( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + postcard = response.parse() + assert_matches_type(PostcardDeleteResponse, postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_delete(self, client: PostGrid) -> None: + with client.print_mail.order_profiles.postcards.with_streaming_response.delete( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + postcard = response.parse() + assert_matches_type(PostcardDeleteResponse, postcard, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_path_params_delete(self, client: PostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.print_mail.order_profiles.postcards.with_raw_response.delete( + "", + ) + + +class TestAsyncPostcards: + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip() + @parametrize + async def test_method_create(self, async_client: AsyncPostGrid) -> None: + postcard = await async_client.print_mail.order_profiles.postcards.create( + size="6x4", + ) + assert_matches_type(PostcardProfile, postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncPostGrid) -> None: + postcard = await async_client.print_mail.order_profiles.postcards.create( + size="6x4", + expand=["string"], + back_template="backTemplate", + description="description", + front_template="frontTemplate", + mailing_class="first_class", + merge_variables={"foo": "bar"}, + metadata={"foo": "string"}, + pdf="https://example.com", + ) + assert_matches_type(PostcardProfile, postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_create(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.order_profiles.postcards.with_raw_response.create( + size="6x4", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + postcard = await response.parse() + assert_matches_type(PostcardProfile, postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_create(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.order_profiles.postcards.with_streaming_response.create( + size="6x4", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + postcard = await response.parse() + assert_matches_type(PostcardProfile, postcard, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_method_retrieve(self, async_client: AsyncPostGrid) -> None: + postcard = await async_client.print_mail.order_profiles.postcards.retrieve( + id="id", + ) + assert_matches_type(PostcardProfile, postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_retrieve_with_all_params(self, async_client: AsyncPostGrid) -> None: + postcard = await async_client.print_mail.order_profiles.postcards.retrieve( + id="id", + expand=["string"], + ) + assert_matches_type(PostcardProfile, postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.order_profiles.postcards.with_raw_response.retrieve( + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + postcard = await response.parse() + assert_matches_type(PostcardProfile, postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.order_profiles.postcards.with_streaming_response.retrieve( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + postcard = await response.parse() + assert_matches_type(PostcardProfile, postcard, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncPostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.print_mail.order_profiles.postcards.with_raw_response.retrieve( + id="", + ) + + @pytest.mark.skip() + @parametrize + async def test_method_update(self, async_client: AsyncPostGrid) -> None: + postcard = await async_client.print_mail.order_profiles.postcards.update( + id="id", + ) + assert_matches_type(PostcardProfile, postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_update_with_all_params(self, async_client: AsyncPostGrid) -> None: + postcard = await async_client.print_mail.order_profiles.postcards.update( + id="id", + expand=["string"], + back_template="backTemplate", + description="description", + front_template="frontTemplate", + mailing_class="first_class", + merge_variables={"foo": "bar"}, + metadata={"foo": "string"}, + pdf="https://example.com", + ) + assert_matches_type(PostcardProfile, postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_update(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.order_profiles.postcards.with_raw_response.update( + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + postcard = await response.parse() + assert_matches_type(PostcardProfile, postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_update(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.order_profiles.postcards.with_streaming_response.update( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + postcard = await response.parse() + assert_matches_type(PostcardProfile, postcard, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_path_params_update(self, async_client: AsyncPostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.print_mail.order_profiles.postcards.with_raw_response.update( + id="", + ) + + @pytest.mark.skip() + @parametrize + async def test_method_list(self, async_client: AsyncPostGrid) -> None: + postcard = await async_client.print_mail.order_profiles.postcards.list() + assert_matches_type(AsyncSkipLimit[PostcardProfile], postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncPostGrid) -> None: + postcard = await async_client.print_mail.order_profiles.postcards.list( + limit=0, + search="search", + skip=0, + ) + assert_matches_type(AsyncSkipLimit[PostcardProfile], postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_list(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.order_profiles.postcards.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + postcard = await response.parse() + assert_matches_type(AsyncSkipLimit[PostcardProfile], postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_list(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.order_profiles.postcards.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + postcard = await response.parse() + assert_matches_type(AsyncSkipLimit[PostcardProfile], postcard, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_method_delete(self, async_client: AsyncPostGrid) -> None: + postcard = await async_client.print_mail.order_profiles.postcards.delete( + "id", + ) + assert_matches_type(PostcardDeleteResponse, postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_delete(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.order_profiles.postcards.with_raw_response.delete( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + postcard = await response.parse() + assert_matches_type(PostcardDeleteResponse, postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.order_profiles.postcards.with_streaming_response.delete( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + postcard = await response.parse() + assert_matches_type(PostcardDeleteResponse, postcard, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_path_params_delete(self, async_client: AsyncPostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.print_mail.order_profiles.postcards.with_raw_response.delete( + "", + ) diff --git a/tests/api_resources/print_mail/order_profiles/test_self_mailers.py b/tests/api_resources/print_mail/order_profiles/test_self_mailers.py new file mode 100644 index 0000000..dcf58ec --- /dev/null +++ b/tests/api_resources/print_mail/order_profiles/test_self_mailers.py @@ -0,0 +1,514 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from postgrid import PostGrid, AsyncPostGrid +from tests.utils import assert_matches_type +from postgrid.pagination import SyncSkipLimit, AsyncSkipLimit +from postgrid.types.print_mail.order_profiles import ( + SelfMailerProfile, + SelfMailerDeleteResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestSelfMailers: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip() + @parametrize + def test_method_create(self, client: PostGrid) -> None: + self_mailer = client.print_mail.order_profiles.self_mailers.create( + size="8.5x11_bifold", + ) + assert_matches_type(SelfMailerProfile, self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_create_with_all_params(self, client: PostGrid) -> None: + self_mailer = client.print_mail.order_profiles.self_mailers.create( + size="8.5x11_bifold", + expand=["string"], + description="description", + inside_template="insideTemplate", + mailing_class="first_class", + merge_variables={"foo": "bar"}, + metadata={"foo": "string"}, + outside_template="outsideTemplate", + pdf="https://example.com", + ) + assert_matches_type(SelfMailerProfile, self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_create(self, client: PostGrid) -> None: + response = client.print_mail.order_profiles.self_mailers.with_raw_response.create( + size="8.5x11_bifold", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + self_mailer = response.parse() + assert_matches_type(SelfMailerProfile, self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_create(self, client: PostGrid) -> None: + with client.print_mail.order_profiles.self_mailers.with_streaming_response.create( + size="8.5x11_bifold", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + self_mailer = response.parse() + assert_matches_type(SelfMailerProfile, self_mailer, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_method_retrieve(self, client: PostGrid) -> None: + self_mailer = client.print_mail.order_profiles.self_mailers.retrieve( + id="id", + ) + assert_matches_type(SelfMailerProfile, self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_retrieve_with_all_params(self, client: PostGrid) -> None: + self_mailer = client.print_mail.order_profiles.self_mailers.retrieve( + id="id", + expand=["string"], + ) + assert_matches_type(SelfMailerProfile, self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_retrieve(self, client: PostGrid) -> None: + response = client.print_mail.order_profiles.self_mailers.with_raw_response.retrieve( + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + self_mailer = response.parse() + assert_matches_type(SelfMailerProfile, self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_retrieve(self, client: PostGrid) -> None: + with client.print_mail.order_profiles.self_mailers.with_streaming_response.retrieve( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + self_mailer = response.parse() + assert_matches_type(SelfMailerProfile, self_mailer, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_path_params_retrieve(self, client: PostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.print_mail.order_profiles.self_mailers.with_raw_response.retrieve( + id="", + ) + + @pytest.mark.skip() + @parametrize + def test_method_update(self, client: PostGrid) -> None: + self_mailer = client.print_mail.order_profiles.self_mailers.update( + id="id", + size="8.5x11_bifold", + ) + assert_matches_type(SelfMailerProfile, self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_update_with_all_params(self, client: PostGrid) -> None: + self_mailer = client.print_mail.order_profiles.self_mailers.update( + id="id", + size="8.5x11_bifold", + expand=["string"], + description="description", + inside_template="insideTemplate", + mailing_class="first_class", + merge_variables={"foo": "bar"}, + metadata={"foo": "string"}, + outside_template="outsideTemplate", + pdf="https://example.com", + ) + assert_matches_type(SelfMailerProfile, self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_update(self, client: PostGrid) -> None: + response = client.print_mail.order_profiles.self_mailers.with_raw_response.update( + id="id", + size="8.5x11_bifold", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + self_mailer = response.parse() + assert_matches_type(SelfMailerProfile, self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_update(self, client: PostGrid) -> None: + with client.print_mail.order_profiles.self_mailers.with_streaming_response.update( + id="id", + size="8.5x11_bifold", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + self_mailer = response.parse() + assert_matches_type(SelfMailerProfile, self_mailer, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_path_params_update(self, client: PostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.print_mail.order_profiles.self_mailers.with_raw_response.update( + id="", + size="8.5x11_bifold", + ) + + @pytest.mark.skip() + @parametrize + def test_method_list(self, client: PostGrid) -> None: + self_mailer = client.print_mail.order_profiles.self_mailers.list() + assert_matches_type(SyncSkipLimit[SelfMailerProfile], self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_list_with_all_params(self, client: PostGrid) -> None: + self_mailer = client.print_mail.order_profiles.self_mailers.list( + limit=0, + search="search", + skip=0, + ) + assert_matches_type(SyncSkipLimit[SelfMailerProfile], self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_list(self, client: PostGrid) -> None: + response = client.print_mail.order_profiles.self_mailers.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + self_mailer = response.parse() + assert_matches_type(SyncSkipLimit[SelfMailerProfile], self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_list(self, client: PostGrid) -> None: + with client.print_mail.order_profiles.self_mailers.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + self_mailer = response.parse() + assert_matches_type(SyncSkipLimit[SelfMailerProfile], self_mailer, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_method_delete(self, client: PostGrid) -> None: + self_mailer = client.print_mail.order_profiles.self_mailers.delete( + "id", + ) + assert_matches_type(SelfMailerDeleteResponse, self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_delete(self, client: PostGrid) -> None: + response = client.print_mail.order_profiles.self_mailers.with_raw_response.delete( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + self_mailer = response.parse() + assert_matches_type(SelfMailerDeleteResponse, self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_delete(self, client: PostGrid) -> None: + with client.print_mail.order_profiles.self_mailers.with_streaming_response.delete( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + self_mailer = response.parse() + assert_matches_type(SelfMailerDeleteResponse, self_mailer, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_path_params_delete(self, client: PostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.print_mail.order_profiles.self_mailers.with_raw_response.delete( + "", + ) + + +class TestAsyncSelfMailers: + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip() + @parametrize + async def test_method_create(self, async_client: AsyncPostGrid) -> None: + self_mailer = await async_client.print_mail.order_profiles.self_mailers.create( + size="8.5x11_bifold", + ) + assert_matches_type(SelfMailerProfile, self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncPostGrid) -> None: + self_mailer = await async_client.print_mail.order_profiles.self_mailers.create( + size="8.5x11_bifold", + expand=["string"], + description="description", + inside_template="insideTemplate", + mailing_class="first_class", + merge_variables={"foo": "bar"}, + metadata={"foo": "string"}, + outside_template="outsideTemplate", + pdf="https://example.com", + ) + assert_matches_type(SelfMailerProfile, self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_create(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.order_profiles.self_mailers.with_raw_response.create( + size="8.5x11_bifold", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + self_mailer = await response.parse() + assert_matches_type(SelfMailerProfile, self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_create(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.order_profiles.self_mailers.with_streaming_response.create( + size="8.5x11_bifold", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + self_mailer = await response.parse() + assert_matches_type(SelfMailerProfile, self_mailer, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_method_retrieve(self, async_client: AsyncPostGrid) -> None: + self_mailer = await async_client.print_mail.order_profiles.self_mailers.retrieve( + id="id", + ) + assert_matches_type(SelfMailerProfile, self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_retrieve_with_all_params(self, async_client: AsyncPostGrid) -> None: + self_mailer = await async_client.print_mail.order_profiles.self_mailers.retrieve( + id="id", + expand=["string"], + ) + assert_matches_type(SelfMailerProfile, self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.order_profiles.self_mailers.with_raw_response.retrieve( + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + self_mailer = await response.parse() + assert_matches_type(SelfMailerProfile, self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.order_profiles.self_mailers.with_streaming_response.retrieve( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + self_mailer = await response.parse() + assert_matches_type(SelfMailerProfile, self_mailer, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncPostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.print_mail.order_profiles.self_mailers.with_raw_response.retrieve( + id="", + ) + + @pytest.mark.skip() + @parametrize + async def test_method_update(self, async_client: AsyncPostGrid) -> None: + self_mailer = await async_client.print_mail.order_profiles.self_mailers.update( + id="id", + size="8.5x11_bifold", + ) + assert_matches_type(SelfMailerProfile, self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_update_with_all_params(self, async_client: AsyncPostGrid) -> None: + self_mailer = await async_client.print_mail.order_profiles.self_mailers.update( + id="id", + size="8.5x11_bifold", + expand=["string"], + description="description", + inside_template="insideTemplate", + mailing_class="first_class", + merge_variables={"foo": "bar"}, + metadata={"foo": "string"}, + outside_template="outsideTemplate", + pdf="https://example.com", + ) + assert_matches_type(SelfMailerProfile, self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_update(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.order_profiles.self_mailers.with_raw_response.update( + id="id", + size="8.5x11_bifold", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + self_mailer = await response.parse() + assert_matches_type(SelfMailerProfile, self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_update(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.order_profiles.self_mailers.with_streaming_response.update( + id="id", + size="8.5x11_bifold", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + self_mailer = await response.parse() + assert_matches_type(SelfMailerProfile, self_mailer, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_path_params_update(self, async_client: AsyncPostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.print_mail.order_profiles.self_mailers.with_raw_response.update( + id="", + size="8.5x11_bifold", + ) + + @pytest.mark.skip() + @parametrize + async def test_method_list(self, async_client: AsyncPostGrid) -> None: + self_mailer = await async_client.print_mail.order_profiles.self_mailers.list() + assert_matches_type(AsyncSkipLimit[SelfMailerProfile], self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncPostGrid) -> None: + self_mailer = await async_client.print_mail.order_profiles.self_mailers.list( + limit=0, + search="search", + skip=0, + ) + assert_matches_type(AsyncSkipLimit[SelfMailerProfile], self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_list(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.order_profiles.self_mailers.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + self_mailer = await response.parse() + assert_matches_type(AsyncSkipLimit[SelfMailerProfile], self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_list(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.order_profiles.self_mailers.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + self_mailer = await response.parse() + assert_matches_type(AsyncSkipLimit[SelfMailerProfile], self_mailer, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_method_delete(self, async_client: AsyncPostGrid) -> None: + self_mailer = await async_client.print_mail.order_profiles.self_mailers.delete( + "id", + ) + assert_matches_type(SelfMailerDeleteResponse, self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_delete(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.order_profiles.self_mailers.with_raw_response.delete( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + self_mailer = await response.parse() + assert_matches_type(SelfMailerDeleteResponse, self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.order_profiles.self_mailers.with_streaming_response.delete( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + self_mailer = await response.parse() + assert_matches_type(SelfMailerDeleteResponse, self_mailer, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_path_params_delete(self, async_client: AsyncPostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.print_mail.order_profiles.self_mailers.with_raw_response.delete( + "", + ) diff --git a/tests/api_resources/print_mail/reports/__init__.py b/tests/api_resources/print_mail/reports/__init__.py new file mode 100644 index 0000000..fd8019a --- /dev/null +++ b/tests/api_resources/print_mail/reports/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/print_mail/reports/test_exports.py b/tests/api_resources/print_mail/reports/test_exports.py new file mode 100644 index 0000000..8ec95d4 --- /dev/null +++ b/tests/api_resources/print_mail/reports/test_exports.py @@ -0,0 +1,337 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from postgrid import PostGrid, AsyncPostGrid +from tests.utils import assert_matches_type +from postgrid.types.print_mail import DeletedResponse +from postgrid.types.print_mail.reports import ReportExport + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestExports: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip() + @parametrize + def test_method_create(self, client: PostGrid) -> None: + export = client.print_mail.reports.exports.create( + report_id="reportID", + ) + assert_matches_type(ReportExport, export, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_create_with_all_params(self, client: PostGrid) -> None: + export = client.print_mail.reports.exports.create( + report_id="reportID", + description="October Orders Export", + metadata={"foo": "string"}, + params=["2023-10-01T00:00:00Z"], + ) + assert_matches_type(ReportExport, export, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_create(self, client: PostGrid) -> None: + response = client.print_mail.reports.exports.with_raw_response.create( + report_id="reportID", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + export = response.parse() + assert_matches_type(ReportExport, export, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_create(self, client: PostGrid) -> None: + with client.print_mail.reports.exports.with_streaming_response.create( + report_id="reportID", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + export = response.parse() + assert_matches_type(ReportExport, export, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_path_params_create(self, client: PostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `report_id` but received ''"): + client.print_mail.reports.exports.with_raw_response.create( + report_id="", + ) + + @pytest.mark.skip() + @parametrize + def test_method_retrieve(self, client: PostGrid) -> None: + export = client.print_mail.reports.exports.retrieve( + export_id="exportID", + report_id="reportID", + ) + assert_matches_type(ReportExport, export, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_retrieve(self, client: PostGrid) -> None: + response = client.print_mail.reports.exports.with_raw_response.retrieve( + export_id="exportID", + report_id="reportID", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + export = response.parse() + assert_matches_type(ReportExport, export, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_retrieve(self, client: PostGrid) -> None: + with client.print_mail.reports.exports.with_streaming_response.retrieve( + export_id="exportID", + report_id="reportID", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + export = response.parse() + assert_matches_type(ReportExport, export, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_path_params_retrieve(self, client: PostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `report_id` but received ''"): + client.print_mail.reports.exports.with_raw_response.retrieve( + export_id="exportID", + report_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `export_id` but received ''"): + client.print_mail.reports.exports.with_raw_response.retrieve( + export_id="", + report_id="reportID", + ) + + @pytest.mark.skip() + @parametrize + def test_method_delete(self, client: PostGrid) -> None: + export = client.print_mail.reports.exports.delete( + export_id="exportID", + report_id="reportID", + ) + assert_matches_type(DeletedResponse, export, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_delete(self, client: PostGrid) -> None: + response = client.print_mail.reports.exports.with_raw_response.delete( + export_id="exportID", + report_id="reportID", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + export = response.parse() + assert_matches_type(DeletedResponse, export, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_delete(self, client: PostGrid) -> None: + with client.print_mail.reports.exports.with_streaming_response.delete( + export_id="exportID", + report_id="reportID", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + export = response.parse() + assert_matches_type(DeletedResponse, export, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_path_params_delete(self, client: PostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `report_id` but received ''"): + client.print_mail.reports.exports.with_raw_response.delete( + export_id="exportID", + report_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `export_id` but received ''"): + client.print_mail.reports.exports.with_raw_response.delete( + export_id="", + report_id="reportID", + ) + + +class TestAsyncExports: + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip() + @parametrize + async def test_method_create(self, async_client: AsyncPostGrid) -> None: + export = await async_client.print_mail.reports.exports.create( + report_id="reportID", + ) + assert_matches_type(ReportExport, export, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncPostGrid) -> None: + export = await async_client.print_mail.reports.exports.create( + report_id="reportID", + description="October Orders Export", + metadata={"foo": "string"}, + params=["2023-10-01T00:00:00Z"], + ) + assert_matches_type(ReportExport, export, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_create(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.reports.exports.with_raw_response.create( + report_id="reportID", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + export = await response.parse() + assert_matches_type(ReportExport, export, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_create(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.reports.exports.with_streaming_response.create( + report_id="reportID", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + export = await response.parse() + assert_matches_type(ReportExport, export, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_path_params_create(self, async_client: AsyncPostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `report_id` but received ''"): + await async_client.print_mail.reports.exports.with_raw_response.create( + report_id="", + ) + + @pytest.mark.skip() + @parametrize + async def test_method_retrieve(self, async_client: AsyncPostGrid) -> None: + export = await async_client.print_mail.reports.exports.retrieve( + export_id="exportID", + report_id="reportID", + ) + assert_matches_type(ReportExport, export, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.reports.exports.with_raw_response.retrieve( + export_id="exportID", + report_id="reportID", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + export = await response.parse() + assert_matches_type(ReportExport, export, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.reports.exports.with_streaming_response.retrieve( + export_id="exportID", + report_id="reportID", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + export = await response.parse() + assert_matches_type(ReportExport, export, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncPostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `report_id` but received ''"): + await async_client.print_mail.reports.exports.with_raw_response.retrieve( + export_id="exportID", + report_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `export_id` but received ''"): + await async_client.print_mail.reports.exports.with_raw_response.retrieve( + export_id="", + report_id="reportID", + ) + + @pytest.mark.skip() + @parametrize + async def test_method_delete(self, async_client: AsyncPostGrid) -> None: + export = await async_client.print_mail.reports.exports.delete( + export_id="exportID", + report_id="reportID", + ) + assert_matches_type(DeletedResponse, export, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_delete(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.reports.exports.with_raw_response.delete( + export_id="exportID", + report_id="reportID", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + export = await response.parse() + assert_matches_type(DeletedResponse, export, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.reports.exports.with_streaming_response.delete( + export_id="exportID", + report_id="reportID", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + export = await response.parse() + assert_matches_type(DeletedResponse, export, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_path_params_delete(self, async_client: AsyncPostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `report_id` but received ''"): + await async_client.print_mail.reports.exports.with_raw_response.delete( + export_id="exportID", + report_id="", + ) + + with pytest.raises(ValueError, match=r"Expected a non-empty value for `export_id` but received ''"): + await async_client.print_mail.reports.exports.with_raw_response.delete( + export_id="", + report_id="reportID", + ) diff --git a/tests/api_resources/print_mail/reports/test_samples.py b/tests/api_resources/print_mail/reports/test_samples.py new file mode 100644 index 0000000..4d9cb1c --- /dev/null +++ b/tests/api_resources/print_mail/reports/test_samples.py @@ -0,0 +1,126 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from postgrid import PostGrid, AsyncPostGrid +from tests.utils import assert_matches_type +from postgrid.types.print_mail.reports import ReportSample + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestSamples: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip() + @parametrize + def test_method_create(self, client: PostGrid) -> None: + sample = client.print_mail.reports.samples.create( + id="id", + ) + assert_matches_type(ReportSample, sample, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_create_with_all_params(self, client: PostGrid) -> None: + sample = client.print_mail.reports.samples.create( + id="id", + limit=10, + params=["2023-10-01T00:00:00Z"], + ) + assert_matches_type(ReportSample, sample, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_create(self, client: PostGrid) -> None: + response = client.print_mail.reports.samples.with_raw_response.create( + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + sample = response.parse() + assert_matches_type(ReportSample, sample, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_create(self, client: PostGrid) -> None: + with client.print_mail.reports.samples.with_streaming_response.create( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + sample = response.parse() + assert_matches_type(ReportSample, sample, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_path_params_create(self, client: PostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.print_mail.reports.samples.with_raw_response.create( + id="", + ) + + +class TestAsyncSamples: + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip() + @parametrize + async def test_method_create(self, async_client: AsyncPostGrid) -> None: + sample = await async_client.print_mail.reports.samples.create( + id="id", + ) + assert_matches_type(ReportSample, sample, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncPostGrid) -> None: + sample = await async_client.print_mail.reports.samples.create( + id="id", + limit=10, + params=["2023-10-01T00:00:00Z"], + ) + assert_matches_type(ReportSample, sample, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_create(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.reports.samples.with_raw_response.create( + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + sample = await response.parse() + assert_matches_type(ReportSample, sample, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_create(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.reports.samples.with_streaming_response.create( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + sample = await response.parse() + assert_matches_type(ReportSample, sample, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_path_params_create(self, async_client: AsyncPostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.print_mail.reports.samples.with_raw_response.create( + id="", + ) diff --git a/tests/api_resources/print_mail/test_bank_accounts.py b/tests/api_resources/print_mail/test_bank_accounts.py new file mode 100644 index 0000000..1d2319a --- /dev/null +++ b/tests/api_resources/print_mail/test_bank_accounts.py @@ -0,0 +1,642 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from postgrid import PostGrid, AsyncPostGrid +from tests.utils import assert_matches_type +from postgrid.pagination import SyncSkipLimit, AsyncSkipLimit +from postgrid.types.print_mail import ( + BankAccount, + BankAccountDeleteResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestBankAccounts: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip() + @parametrize + def test_method_create_overload_1(self, client: PostGrid) -> None: + bank_account = client.print_mail.bank_accounts.create( + account_number="accountNumber", + bank_country_code="CA", + bank_name="bankName", + signature_text="signatureText", + ) + assert_matches_type(BankAccount, bank_account, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_create_with_all_params_overload_1(self, client: PostGrid) -> None: + bank_account = client.print_mail.bank_accounts.create( + account_number="accountNumber", + bank_country_code="CA", + bank_name="bankName", + signature_text="signatureText", + bank_primary_line="bankPrimaryLine", + bank_secondary_line="bankSecondaryLine", + ca_designation_number="caDesignationNumber", + description="description", + metadata={"foo": "bar"}, + route_number="routeNumber", + routing_number="routingNumber", + transit_number="transitNumber", + ) + assert_matches_type(BankAccount, bank_account, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_create_overload_1(self, client: PostGrid) -> None: + response = client.print_mail.bank_accounts.with_raw_response.create( + account_number="accountNumber", + bank_country_code="CA", + bank_name="bankName", + signature_text="signatureText", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + bank_account = response.parse() + assert_matches_type(BankAccount, bank_account, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_create_overload_1(self, client: PostGrid) -> None: + with client.print_mail.bank_accounts.with_streaming_response.create( + account_number="accountNumber", + bank_country_code="CA", + bank_name="bankName", + signature_text="signatureText", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + bank_account = response.parse() + assert_matches_type(BankAccount, bank_account, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_method_create_overload_2(self, client: PostGrid) -> None: + bank_account = client.print_mail.bank_accounts.create( + account_number="accountNumber", + bank_country_code="CA", + bank_name="bankName", + signature_image="https://example.com", + ) + assert_matches_type(BankAccount, bank_account, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_create_with_all_params_overload_2(self, client: PostGrid) -> None: + bank_account = client.print_mail.bank_accounts.create( + account_number="accountNumber", + bank_country_code="CA", + bank_name="bankName", + signature_image="https://example.com", + bank_primary_line="bankPrimaryLine", + bank_secondary_line="bankSecondaryLine", + ca_designation_number="caDesignationNumber", + description="description", + metadata={"foo": "bar"}, + route_number="routeNumber", + routing_number="routingNumber", + transit_number="transitNumber", + ) + assert_matches_type(BankAccount, bank_account, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_create_overload_2(self, client: PostGrid) -> None: + response = client.print_mail.bank_accounts.with_raw_response.create( + account_number="accountNumber", + bank_country_code="CA", + bank_name="bankName", + signature_image="https://example.com", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + bank_account = response.parse() + assert_matches_type(BankAccount, bank_account, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_create_overload_2(self, client: PostGrid) -> None: + with client.print_mail.bank_accounts.with_streaming_response.create( + account_number="accountNumber", + bank_country_code="CA", + bank_name="bankName", + signature_image="https://example.com", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + bank_account = response.parse() + assert_matches_type(BankAccount, bank_account, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_method_create_overload_3(self, client: PostGrid) -> None: + bank_account = client.print_mail.bank_accounts.create( + account_number="accountNumber", + bank_country_code="CA", + bank_name="bankName", + signature_image="U3RhaW5sZXNzIHJvY2tz", + ) + assert_matches_type(BankAccount, bank_account, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_create_with_all_params_overload_3(self, client: PostGrid) -> None: + bank_account = client.print_mail.bank_accounts.create( + account_number="accountNumber", + bank_country_code="CA", + bank_name="bankName", + signature_image="U3RhaW5sZXNzIHJvY2tz", + bank_primary_line="bankPrimaryLine", + bank_secondary_line="bankSecondaryLine", + ca_designation_number="caDesignationNumber", + description="description", + metadata={"foo": "bar"}, + route_number="routeNumber", + routing_number="routingNumber", + transit_number="transitNumber", + ) + assert_matches_type(BankAccount, bank_account, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_create_overload_3(self, client: PostGrid) -> None: + response = client.print_mail.bank_accounts.with_raw_response.create( + account_number="accountNumber", + bank_country_code="CA", + bank_name="bankName", + signature_image="U3RhaW5sZXNzIHJvY2tz", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + bank_account = response.parse() + assert_matches_type(BankAccount, bank_account, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_create_overload_3(self, client: PostGrid) -> None: + with client.print_mail.bank_accounts.with_streaming_response.create( + account_number="accountNumber", + bank_country_code="CA", + bank_name="bankName", + signature_image="U3RhaW5sZXNzIHJvY2tz", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + bank_account = response.parse() + assert_matches_type(BankAccount, bank_account, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_method_retrieve(self, client: PostGrid) -> None: + bank_account = client.print_mail.bank_accounts.retrieve( + "id", + ) + assert_matches_type(BankAccount, bank_account, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_retrieve(self, client: PostGrid) -> None: + response = client.print_mail.bank_accounts.with_raw_response.retrieve( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + bank_account = response.parse() + assert_matches_type(BankAccount, bank_account, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_retrieve(self, client: PostGrid) -> None: + with client.print_mail.bank_accounts.with_streaming_response.retrieve( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + bank_account = response.parse() + assert_matches_type(BankAccount, bank_account, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_path_params_retrieve(self, client: PostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.print_mail.bank_accounts.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip() + @parametrize + def test_method_list(self, client: PostGrid) -> None: + bank_account = client.print_mail.bank_accounts.list() + assert_matches_type(SyncSkipLimit[BankAccount], bank_account, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_list_with_all_params(self, client: PostGrid) -> None: + bank_account = client.print_mail.bank_accounts.list( + limit=0, + search="search", + skip=0, + ) + assert_matches_type(SyncSkipLimit[BankAccount], bank_account, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_list(self, client: PostGrid) -> None: + response = client.print_mail.bank_accounts.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + bank_account = response.parse() + assert_matches_type(SyncSkipLimit[BankAccount], bank_account, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_list(self, client: PostGrid) -> None: + with client.print_mail.bank_accounts.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + bank_account = response.parse() + assert_matches_type(SyncSkipLimit[BankAccount], bank_account, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_method_delete(self, client: PostGrid) -> None: + bank_account = client.print_mail.bank_accounts.delete( + "id", + ) + assert_matches_type(BankAccountDeleteResponse, bank_account, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_delete(self, client: PostGrid) -> None: + response = client.print_mail.bank_accounts.with_raw_response.delete( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + bank_account = response.parse() + assert_matches_type(BankAccountDeleteResponse, bank_account, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_delete(self, client: PostGrid) -> None: + with client.print_mail.bank_accounts.with_streaming_response.delete( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + bank_account = response.parse() + assert_matches_type(BankAccountDeleteResponse, bank_account, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_path_params_delete(self, client: PostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.print_mail.bank_accounts.with_raw_response.delete( + "", + ) + + +class TestAsyncBankAccounts: + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip() + @parametrize + async def test_method_create_overload_1(self, async_client: AsyncPostGrid) -> None: + bank_account = await async_client.print_mail.bank_accounts.create( + account_number="accountNumber", + bank_country_code="CA", + bank_name="bankName", + signature_text="signatureText", + ) + assert_matches_type(BankAccount, bank_account, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_create_with_all_params_overload_1(self, async_client: AsyncPostGrid) -> None: + bank_account = await async_client.print_mail.bank_accounts.create( + account_number="accountNumber", + bank_country_code="CA", + bank_name="bankName", + signature_text="signatureText", + bank_primary_line="bankPrimaryLine", + bank_secondary_line="bankSecondaryLine", + ca_designation_number="caDesignationNumber", + description="description", + metadata={"foo": "bar"}, + route_number="routeNumber", + routing_number="routingNumber", + transit_number="transitNumber", + ) + assert_matches_type(BankAccount, bank_account, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_create_overload_1(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.bank_accounts.with_raw_response.create( + account_number="accountNumber", + bank_country_code="CA", + bank_name="bankName", + signature_text="signatureText", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + bank_account = await response.parse() + assert_matches_type(BankAccount, bank_account, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_create_overload_1(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.bank_accounts.with_streaming_response.create( + account_number="accountNumber", + bank_country_code="CA", + bank_name="bankName", + signature_text="signatureText", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + bank_account = await response.parse() + assert_matches_type(BankAccount, bank_account, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_method_create_overload_2(self, async_client: AsyncPostGrid) -> None: + bank_account = await async_client.print_mail.bank_accounts.create( + account_number="accountNumber", + bank_country_code="CA", + bank_name="bankName", + signature_image="https://example.com", + ) + assert_matches_type(BankAccount, bank_account, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_create_with_all_params_overload_2(self, async_client: AsyncPostGrid) -> None: + bank_account = await async_client.print_mail.bank_accounts.create( + account_number="accountNumber", + bank_country_code="CA", + bank_name="bankName", + signature_image="https://example.com", + bank_primary_line="bankPrimaryLine", + bank_secondary_line="bankSecondaryLine", + ca_designation_number="caDesignationNumber", + description="description", + metadata={"foo": "bar"}, + route_number="routeNumber", + routing_number="routingNumber", + transit_number="transitNumber", + ) + assert_matches_type(BankAccount, bank_account, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_create_overload_2(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.bank_accounts.with_raw_response.create( + account_number="accountNumber", + bank_country_code="CA", + bank_name="bankName", + signature_image="https://example.com", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + bank_account = await response.parse() + assert_matches_type(BankAccount, bank_account, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_create_overload_2(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.bank_accounts.with_streaming_response.create( + account_number="accountNumber", + bank_country_code="CA", + bank_name="bankName", + signature_image="https://example.com", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + bank_account = await response.parse() + assert_matches_type(BankAccount, bank_account, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_method_create_overload_3(self, async_client: AsyncPostGrid) -> None: + bank_account = await async_client.print_mail.bank_accounts.create( + account_number="accountNumber", + bank_country_code="CA", + bank_name="bankName", + signature_image="U3RhaW5sZXNzIHJvY2tz", + ) + assert_matches_type(BankAccount, bank_account, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_create_with_all_params_overload_3(self, async_client: AsyncPostGrid) -> None: + bank_account = await async_client.print_mail.bank_accounts.create( + account_number="accountNumber", + bank_country_code="CA", + bank_name="bankName", + signature_image="U3RhaW5sZXNzIHJvY2tz", + bank_primary_line="bankPrimaryLine", + bank_secondary_line="bankSecondaryLine", + ca_designation_number="caDesignationNumber", + description="description", + metadata={"foo": "bar"}, + route_number="routeNumber", + routing_number="routingNumber", + transit_number="transitNumber", + ) + assert_matches_type(BankAccount, bank_account, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_create_overload_3(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.bank_accounts.with_raw_response.create( + account_number="accountNumber", + bank_country_code="CA", + bank_name="bankName", + signature_image="U3RhaW5sZXNzIHJvY2tz", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + bank_account = await response.parse() + assert_matches_type(BankAccount, bank_account, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_create_overload_3(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.bank_accounts.with_streaming_response.create( + account_number="accountNumber", + bank_country_code="CA", + bank_name="bankName", + signature_image="U3RhaW5sZXNzIHJvY2tz", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + bank_account = await response.parse() + assert_matches_type(BankAccount, bank_account, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_method_retrieve(self, async_client: AsyncPostGrid) -> None: + bank_account = await async_client.print_mail.bank_accounts.retrieve( + "id", + ) + assert_matches_type(BankAccount, bank_account, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.bank_accounts.with_raw_response.retrieve( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + bank_account = await response.parse() + assert_matches_type(BankAccount, bank_account, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.bank_accounts.with_streaming_response.retrieve( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + bank_account = await response.parse() + assert_matches_type(BankAccount, bank_account, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncPostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.print_mail.bank_accounts.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip() + @parametrize + async def test_method_list(self, async_client: AsyncPostGrid) -> None: + bank_account = await async_client.print_mail.bank_accounts.list() + assert_matches_type(AsyncSkipLimit[BankAccount], bank_account, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncPostGrid) -> None: + bank_account = await async_client.print_mail.bank_accounts.list( + limit=0, + search="search", + skip=0, + ) + assert_matches_type(AsyncSkipLimit[BankAccount], bank_account, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_list(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.bank_accounts.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + bank_account = await response.parse() + assert_matches_type(AsyncSkipLimit[BankAccount], bank_account, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_list(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.bank_accounts.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + bank_account = await response.parse() + assert_matches_type(AsyncSkipLimit[BankAccount], bank_account, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_method_delete(self, async_client: AsyncPostGrid) -> None: + bank_account = await async_client.print_mail.bank_accounts.delete( + "id", + ) + assert_matches_type(BankAccountDeleteResponse, bank_account, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_delete(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.bank_accounts.with_raw_response.delete( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + bank_account = await response.parse() + assert_matches_type(BankAccountDeleteResponse, bank_account, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.bank_accounts.with_streaming_response.delete( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + bank_account = await response.parse() + assert_matches_type(BankAccountDeleteResponse, bank_account, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_path_params_delete(self, async_client: AsyncPostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.print_mail.bank_accounts.with_raw_response.delete( + "", + ) diff --git a/tests/api_resources/print_mail/test_boxes.py b/tests/api_resources/print_mail/test_boxes.py new file mode 100644 index 0000000..1d558a6 --- /dev/null +++ b/tests/api_resources/print_mail/test_boxes.py @@ -0,0 +1,450 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from postgrid import PostGrid, AsyncPostGrid +from tests.utils import assert_matches_type +from postgrid._utils import parse_datetime +from postgrid.pagination import SyncSkipLimit, AsyncSkipLimit +from postgrid.types.print_mail import Box + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestBoxes: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip() + @parametrize + def test_method_create(self, client: PostGrid) -> None: + box = client.print_mail.boxes.create( + cheques=[ + { + "amount": 5000, + "bankAccount": "bank_abc", + "number": 1042, + "from": "contact_456", + "to": "contact_123", + } + ], + from_="contact_456", + to="contact_123", + ) + assert_matches_type(Box, box, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_create_with_all_params(self, client: PostGrid) -> None: + box = client.print_mail.boxes.create( + cheques=[ + { + "amount": 5000, + "bankAccount": "bank_abc", + "number": 1042, + "logoURL": "https://example.com", + "memo": "memo", + "mergeVariables": {"foo": "bar"}, + "messageTemplate": "messageTemplate", + "from": "contact_456", + "to": "contact_123", + } + ], + from_="contact_456", + to="contact_123", + description="description", + mailing_class="first_class", + merge_variables={"foo": "bar"}, + metadata={"foo": "bar"}, + send_date=parse_datetime("2019-12-27T18:11:19.117Z"), + ) + assert_matches_type(Box, box, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_create(self, client: PostGrid) -> None: + response = client.print_mail.boxes.with_raw_response.create( + cheques=[ + { + "amount": 5000, + "bankAccount": "bank_abc", + "number": 1042, + "from": "contact_456", + "to": "contact_123", + } + ], + from_="contact_456", + to="contact_123", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + box = response.parse() + assert_matches_type(Box, box, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_create(self, client: PostGrid) -> None: + with client.print_mail.boxes.with_streaming_response.create( + cheques=[ + { + "amount": 5000, + "bankAccount": "bank_abc", + "number": 1042, + "from": "contact_456", + "to": "contact_123", + } + ], + from_="contact_456", + to="contact_123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + box = response.parse() + assert_matches_type(Box, box, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_method_retrieve(self, client: PostGrid) -> None: + box = client.print_mail.boxes.retrieve( + "id", + ) + assert_matches_type(Box, box, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_retrieve(self, client: PostGrid) -> None: + response = client.print_mail.boxes.with_raw_response.retrieve( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + box = response.parse() + assert_matches_type(Box, box, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_retrieve(self, client: PostGrid) -> None: + with client.print_mail.boxes.with_streaming_response.retrieve( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + box = response.parse() + assert_matches_type(Box, box, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_path_params_retrieve(self, client: PostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.print_mail.boxes.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip() + @parametrize + def test_method_list(self, client: PostGrid) -> None: + box = client.print_mail.boxes.list() + assert_matches_type(SyncSkipLimit[Box], box, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_list_with_all_params(self, client: PostGrid) -> None: + box = client.print_mail.boxes.list( + limit=0, + search="search", + skip=0, + ) + assert_matches_type(SyncSkipLimit[Box], box, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_list(self, client: PostGrid) -> None: + response = client.print_mail.boxes.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + box = response.parse() + assert_matches_type(SyncSkipLimit[Box], box, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_list(self, client: PostGrid) -> None: + with client.print_mail.boxes.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + box = response.parse() + assert_matches_type(SyncSkipLimit[Box], box, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_method_delete(self, client: PostGrid) -> None: + box = client.print_mail.boxes.delete( + "id", + ) + assert_matches_type(Box, box, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_delete(self, client: PostGrid) -> None: + response = client.print_mail.boxes.with_raw_response.delete( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + box = response.parse() + assert_matches_type(Box, box, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_delete(self, client: PostGrid) -> None: + with client.print_mail.boxes.with_streaming_response.delete( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + box = response.parse() + assert_matches_type(Box, box, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_path_params_delete(self, client: PostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.print_mail.boxes.with_raw_response.delete( + "", + ) + + +class TestAsyncBoxes: + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip() + @parametrize + async def test_method_create(self, async_client: AsyncPostGrid) -> None: + box = await async_client.print_mail.boxes.create( + cheques=[ + { + "amount": 5000, + "bankAccount": "bank_abc", + "number": 1042, + "from": "contact_456", + "to": "contact_123", + } + ], + from_="contact_456", + to="contact_123", + ) + assert_matches_type(Box, box, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncPostGrid) -> None: + box = await async_client.print_mail.boxes.create( + cheques=[ + { + "amount": 5000, + "bankAccount": "bank_abc", + "number": 1042, + "logoURL": "https://example.com", + "memo": "memo", + "mergeVariables": {"foo": "bar"}, + "messageTemplate": "messageTemplate", + "from": "contact_456", + "to": "contact_123", + } + ], + from_="contact_456", + to="contact_123", + description="description", + mailing_class="first_class", + merge_variables={"foo": "bar"}, + metadata={"foo": "bar"}, + send_date=parse_datetime("2019-12-27T18:11:19.117Z"), + ) + assert_matches_type(Box, box, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_create(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.boxes.with_raw_response.create( + cheques=[ + { + "amount": 5000, + "bankAccount": "bank_abc", + "number": 1042, + "from": "contact_456", + "to": "contact_123", + } + ], + from_="contact_456", + to="contact_123", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + box = await response.parse() + assert_matches_type(Box, box, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_create(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.boxes.with_streaming_response.create( + cheques=[ + { + "amount": 5000, + "bankAccount": "bank_abc", + "number": 1042, + "from": "contact_456", + "to": "contact_123", + } + ], + from_="contact_456", + to="contact_123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + box = await response.parse() + assert_matches_type(Box, box, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_method_retrieve(self, async_client: AsyncPostGrid) -> None: + box = await async_client.print_mail.boxes.retrieve( + "id", + ) + assert_matches_type(Box, box, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.boxes.with_raw_response.retrieve( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + box = await response.parse() + assert_matches_type(Box, box, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.boxes.with_streaming_response.retrieve( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + box = await response.parse() + assert_matches_type(Box, box, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncPostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.print_mail.boxes.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip() + @parametrize + async def test_method_list(self, async_client: AsyncPostGrid) -> None: + box = await async_client.print_mail.boxes.list() + assert_matches_type(AsyncSkipLimit[Box], box, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncPostGrid) -> None: + box = await async_client.print_mail.boxes.list( + limit=0, + search="search", + skip=0, + ) + assert_matches_type(AsyncSkipLimit[Box], box, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_list(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.boxes.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + box = await response.parse() + assert_matches_type(AsyncSkipLimit[Box], box, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_list(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.boxes.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + box = await response.parse() + assert_matches_type(AsyncSkipLimit[Box], box, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_method_delete(self, async_client: AsyncPostGrid) -> None: + box = await async_client.print_mail.boxes.delete( + "id", + ) + assert_matches_type(Box, box, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_delete(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.boxes.with_raw_response.delete( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + box = await response.parse() + assert_matches_type(Box, box, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.boxes.with_streaming_response.delete( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + box = await response.parse() + assert_matches_type(Box, box, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_path_params_delete(self, async_client: AsyncPostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.print_mail.boxes.with_raw_response.delete( + "", + ) diff --git a/tests/api_resources/print_mail/test_campaigns.py b/tests/api_resources/print_mail/test_campaigns.py new file mode 100644 index 0000000..2aa6325 --- /dev/null +++ b/tests/api_resources/print_mail/test_campaigns.py @@ -0,0 +1,591 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from postgrid import PostGrid, AsyncPostGrid +from tests.utils import assert_matches_type +from postgrid._utils import parse_datetime +from postgrid.pagination import SyncSkipLimit, AsyncSkipLimit +from postgrid.types.print_mail import ( + Campaign, + CampaignDeleteResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestCampaigns: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip() + @parametrize + def test_method_create(self, client: PostGrid) -> None: + campaign = client.print_mail.campaigns.create( + mailing_list="mailingList", + ) + assert_matches_type(Campaign, campaign, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_create_with_all_params(self, client: PostGrid) -> None: + campaign = client.print_mail.campaigns.create( + mailing_list="mailingList", + cheque_profile="chequeProfile", + default_sender_contact="defaultSenderContact", + description="description", + letter_profile="letterProfile", + metadata={"foo": "bar"}, + postcard_profile="postcardProfile", + self_mailer_profile="selfMailerProfile", + send_date=parse_datetime("2019-12-27T18:11:19.117Z"), + idempotency_key="idempotency-key", + ) + assert_matches_type(Campaign, campaign, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_create(self, client: PostGrid) -> None: + response = client.print_mail.campaigns.with_raw_response.create( + mailing_list="mailingList", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + campaign = response.parse() + assert_matches_type(Campaign, campaign, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_create(self, client: PostGrid) -> None: + with client.print_mail.campaigns.with_streaming_response.create( + mailing_list="mailingList", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + campaign = response.parse() + assert_matches_type(Campaign, campaign, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_method_retrieve(self, client: PostGrid) -> None: + campaign = client.print_mail.campaigns.retrieve( + "id", + ) + assert_matches_type(Campaign, campaign, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_retrieve(self, client: PostGrid) -> None: + response = client.print_mail.campaigns.with_raw_response.retrieve( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + campaign = response.parse() + assert_matches_type(Campaign, campaign, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_retrieve(self, client: PostGrid) -> None: + with client.print_mail.campaigns.with_streaming_response.retrieve( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + campaign = response.parse() + assert_matches_type(Campaign, campaign, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_path_params_retrieve(self, client: PostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.print_mail.campaigns.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip() + @parametrize + def test_method_update(self, client: PostGrid) -> None: + campaign = client.print_mail.campaigns.update( + id="id", + ) + assert_matches_type(Campaign, campaign, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_update_with_all_params(self, client: PostGrid) -> None: + campaign = client.print_mail.campaigns.update( + id="id", + cheque_profile="chequeProfile", + default_sender_contact="defaultSenderContact", + description="description", + letter_profile="letterProfile", + mailing_list="mailingList", + metadata={"foo": "string"}, + postcard_profile="postcardProfile", + self_mailer_profile="selfMailerProfile", + ) + assert_matches_type(Campaign, campaign, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_update(self, client: PostGrid) -> None: + response = client.print_mail.campaigns.with_raw_response.update( + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + campaign = response.parse() + assert_matches_type(Campaign, campaign, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_update(self, client: PostGrid) -> None: + with client.print_mail.campaigns.with_streaming_response.update( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + campaign = response.parse() + assert_matches_type(Campaign, campaign, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_path_params_update(self, client: PostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.print_mail.campaigns.with_raw_response.update( + id="", + ) + + @pytest.mark.skip() + @parametrize + def test_method_list(self, client: PostGrid) -> None: + campaign = client.print_mail.campaigns.list() + assert_matches_type(SyncSkipLimit[Campaign], campaign, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_list_with_all_params(self, client: PostGrid) -> None: + campaign = client.print_mail.campaigns.list( + limit=0, + search="search", + skip=0, + ) + assert_matches_type(SyncSkipLimit[Campaign], campaign, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_list(self, client: PostGrid) -> None: + response = client.print_mail.campaigns.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + campaign = response.parse() + assert_matches_type(SyncSkipLimit[Campaign], campaign, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_list(self, client: PostGrid) -> None: + with client.print_mail.campaigns.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + campaign = response.parse() + assert_matches_type(SyncSkipLimit[Campaign], campaign, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_method_delete(self, client: PostGrid) -> None: + campaign = client.print_mail.campaigns.delete( + "id", + ) + assert_matches_type(CampaignDeleteResponse, campaign, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_delete(self, client: PostGrid) -> None: + response = client.print_mail.campaigns.with_raw_response.delete( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + campaign = response.parse() + assert_matches_type(CampaignDeleteResponse, campaign, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_delete(self, client: PostGrid) -> None: + with client.print_mail.campaigns.with_streaming_response.delete( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + campaign = response.parse() + assert_matches_type(CampaignDeleteResponse, campaign, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_path_params_delete(self, client: PostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.print_mail.campaigns.with_raw_response.delete( + "", + ) + + @pytest.mark.skip() + @parametrize + def test_method_send(self, client: PostGrid) -> None: + campaign = client.print_mail.campaigns.send( + id="id", + ) + assert_matches_type(Campaign, campaign, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_send_with_all_params(self, client: PostGrid) -> None: + campaign = client.print_mail.campaigns.send( + id="id", + send_date=parse_datetime("2019-12-27T18:11:19.117Z"), + ) + assert_matches_type(Campaign, campaign, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_send(self, client: PostGrid) -> None: + response = client.print_mail.campaigns.with_raw_response.send( + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + campaign = response.parse() + assert_matches_type(Campaign, campaign, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_send(self, client: PostGrid) -> None: + with client.print_mail.campaigns.with_streaming_response.send( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + campaign = response.parse() + assert_matches_type(Campaign, campaign, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_path_params_send(self, client: PostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.print_mail.campaigns.with_raw_response.send( + id="", + ) + + +class TestAsyncCampaigns: + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip() + @parametrize + async def test_method_create(self, async_client: AsyncPostGrid) -> None: + campaign = await async_client.print_mail.campaigns.create( + mailing_list="mailingList", + ) + assert_matches_type(Campaign, campaign, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncPostGrid) -> None: + campaign = await async_client.print_mail.campaigns.create( + mailing_list="mailingList", + cheque_profile="chequeProfile", + default_sender_contact="defaultSenderContact", + description="description", + letter_profile="letterProfile", + metadata={"foo": "bar"}, + postcard_profile="postcardProfile", + self_mailer_profile="selfMailerProfile", + send_date=parse_datetime("2019-12-27T18:11:19.117Z"), + idempotency_key="idempotency-key", + ) + assert_matches_type(Campaign, campaign, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_create(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.campaigns.with_raw_response.create( + mailing_list="mailingList", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + campaign = await response.parse() + assert_matches_type(Campaign, campaign, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_create(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.campaigns.with_streaming_response.create( + mailing_list="mailingList", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + campaign = await response.parse() + assert_matches_type(Campaign, campaign, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_method_retrieve(self, async_client: AsyncPostGrid) -> None: + campaign = await async_client.print_mail.campaigns.retrieve( + "id", + ) + assert_matches_type(Campaign, campaign, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.campaigns.with_raw_response.retrieve( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + campaign = await response.parse() + assert_matches_type(Campaign, campaign, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.campaigns.with_streaming_response.retrieve( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + campaign = await response.parse() + assert_matches_type(Campaign, campaign, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncPostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.print_mail.campaigns.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip() + @parametrize + async def test_method_update(self, async_client: AsyncPostGrid) -> None: + campaign = await async_client.print_mail.campaigns.update( + id="id", + ) + assert_matches_type(Campaign, campaign, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_update_with_all_params(self, async_client: AsyncPostGrid) -> None: + campaign = await async_client.print_mail.campaigns.update( + id="id", + cheque_profile="chequeProfile", + default_sender_contact="defaultSenderContact", + description="description", + letter_profile="letterProfile", + mailing_list="mailingList", + metadata={"foo": "string"}, + postcard_profile="postcardProfile", + self_mailer_profile="selfMailerProfile", + ) + assert_matches_type(Campaign, campaign, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_update(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.campaigns.with_raw_response.update( + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + campaign = await response.parse() + assert_matches_type(Campaign, campaign, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_update(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.campaigns.with_streaming_response.update( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + campaign = await response.parse() + assert_matches_type(Campaign, campaign, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_path_params_update(self, async_client: AsyncPostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.print_mail.campaigns.with_raw_response.update( + id="", + ) + + @pytest.mark.skip() + @parametrize + async def test_method_list(self, async_client: AsyncPostGrid) -> None: + campaign = await async_client.print_mail.campaigns.list() + assert_matches_type(AsyncSkipLimit[Campaign], campaign, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncPostGrid) -> None: + campaign = await async_client.print_mail.campaigns.list( + limit=0, + search="search", + skip=0, + ) + assert_matches_type(AsyncSkipLimit[Campaign], campaign, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_list(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.campaigns.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + campaign = await response.parse() + assert_matches_type(AsyncSkipLimit[Campaign], campaign, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_list(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.campaigns.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + campaign = await response.parse() + assert_matches_type(AsyncSkipLimit[Campaign], campaign, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_method_delete(self, async_client: AsyncPostGrid) -> None: + campaign = await async_client.print_mail.campaigns.delete( + "id", + ) + assert_matches_type(CampaignDeleteResponse, campaign, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_delete(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.campaigns.with_raw_response.delete( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + campaign = await response.parse() + assert_matches_type(CampaignDeleteResponse, campaign, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.campaigns.with_streaming_response.delete( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + campaign = await response.parse() + assert_matches_type(CampaignDeleteResponse, campaign, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_path_params_delete(self, async_client: AsyncPostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.print_mail.campaigns.with_raw_response.delete( + "", + ) + + @pytest.mark.skip() + @parametrize + async def test_method_send(self, async_client: AsyncPostGrid) -> None: + campaign = await async_client.print_mail.campaigns.send( + id="id", + ) + assert_matches_type(Campaign, campaign, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_send_with_all_params(self, async_client: AsyncPostGrid) -> None: + campaign = await async_client.print_mail.campaigns.send( + id="id", + send_date=parse_datetime("2019-12-27T18:11:19.117Z"), + ) + assert_matches_type(Campaign, campaign, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_send(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.campaigns.with_raw_response.send( + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + campaign = await response.parse() + assert_matches_type(Campaign, campaign, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_send(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.campaigns.with_streaming_response.send( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + campaign = await response.parse() + assert_matches_type(Campaign, campaign, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_path_params_send(self, async_client: AsyncPostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.print_mail.campaigns.with_raw_response.send( + id="", + ) diff --git a/tests/api_resources/print_mail/test_cheques.py b/tests/api_resources/print_mail/test_cheques.py new file mode 100644 index 0000000..cecd773 --- /dev/null +++ b/tests/api_resources/print_mail/test_cheques.py @@ -0,0 +1,609 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from postgrid import PostGrid, AsyncPostGrid +from tests.utils import assert_matches_type +from postgrid._utils import parse_datetime +from postgrid.pagination import SyncSkipLimit, AsyncSkipLimit +from postgrid.types.print_mail import ( + Cheque, + ChequeRetrieveURLResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestCheques: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip() + @parametrize + def test_method_create(self, client: PostGrid) -> None: + cheque = client.print_mail.cheques.create( + amount=1000, + bank_account="bank_123", + from_="contact_123", + to="contact_123", + ) + assert_matches_type(Cheque, cheque, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_create_with_all_params(self, client: PostGrid) -> None: + cheque = client.print_mail.cheques.create( + amount=1000, + bank_account="bank_123", + from_="contact_123", + to="contact_123", + currency_code="USD", + description="description", + digital_only={"watermark": "watermark"}, + envelope="standard", + logo_url="https://example.com", + mailing_class="first_class", + memo="memo", + merge_variables={"foo": "bar"}, + message="message", + metadata={"foo": "bar"}, + number=123456, + redirect_to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + "address_line2": "addressLine2", + "city": "city", + "company_name": "companyName", + "description": "description", + "email": "email", + "force_verified_status": True, + "job_title": "jobTitle", + "last_name": "lastName", + "metadata": {"foo": "bar"}, + "phone_number": "phoneNumber", + "postal_or_zip": "postalOrZip", + "province_or_state": "provinceOrState", + "skip_verification": True, + }, + send_date=parse_datetime("2019-12-27T18:11:19.117Z"), + size="us_letter", + ) + assert_matches_type(Cheque, cheque, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_create(self, client: PostGrid) -> None: + response = client.print_mail.cheques.with_raw_response.create( + amount=1000, + bank_account="bank_123", + from_="contact_123", + to="contact_123", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + cheque = response.parse() + assert_matches_type(Cheque, cheque, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_create(self, client: PostGrid) -> None: + with client.print_mail.cheques.with_streaming_response.create( + amount=1000, + bank_account="bank_123", + from_="contact_123", + to="contact_123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + cheque = response.parse() + assert_matches_type(Cheque, cheque, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_method_retrieve(self, client: PostGrid) -> None: + cheque = client.print_mail.cheques.retrieve( + "id", + ) + assert_matches_type(Cheque, cheque, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_retrieve(self, client: PostGrid) -> None: + response = client.print_mail.cheques.with_raw_response.retrieve( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + cheque = response.parse() + assert_matches_type(Cheque, cheque, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_retrieve(self, client: PostGrid) -> None: + with client.print_mail.cheques.with_streaming_response.retrieve( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + cheque = response.parse() + assert_matches_type(Cheque, cheque, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_path_params_retrieve(self, client: PostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.print_mail.cheques.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip() + @parametrize + def test_method_list(self, client: PostGrid) -> None: + cheque = client.print_mail.cheques.list() + assert_matches_type(SyncSkipLimit[Cheque], cheque, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_list_with_all_params(self, client: PostGrid) -> None: + cheque = client.print_mail.cheques.list( + limit=0, + search="search", + skip=0, + ) + assert_matches_type(SyncSkipLimit[Cheque], cheque, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_list(self, client: PostGrid) -> None: + response = client.print_mail.cheques.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + cheque = response.parse() + assert_matches_type(SyncSkipLimit[Cheque], cheque, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_list(self, client: PostGrid) -> None: + with client.print_mail.cheques.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + cheque = response.parse() + assert_matches_type(SyncSkipLimit[Cheque], cheque, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_method_delete(self, client: PostGrid) -> None: + cheque = client.print_mail.cheques.delete( + "id", + ) + assert_matches_type(Cheque, cheque, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_delete(self, client: PostGrid) -> None: + response = client.print_mail.cheques.with_raw_response.delete( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + cheque = response.parse() + assert_matches_type(Cheque, cheque, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_delete(self, client: PostGrid) -> None: + with client.print_mail.cheques.with_streaming_response.delete( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + cheque = response.parse() + assert_matches_type(Cheque, cheque, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_path_params_delete(self, client: PostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.print_mail.cheques.with_raw_response.delete( + "", + ) + + @pytest.mark.skip() + @parametrize + def test_method_retrieve_url(self, client: PostGrid) -> None: + cheque = client.print_mail.cheques.retrieve_url( + "id", + ) + assert_matches_type(ChequeRetrieveURLResponse, cheque, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_retrieve_url(self, client: PostGrid) -> None: + response = client.print_mail.cheques.with_raw_response.retrieve_url( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + cheque = response.parse() + assert_matches_type(ChequeRetrieveURLResponse, cheque, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_retrieve_url(self, client: PostGrid) -> None: + with client.print_mail.cheques.with_streaming_response.retrieve_url( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + cheque = response.parse() + assert_matches_type(ChequeRetrieveURLResponse, cheque, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_path_params_retrieve_url(self, client: PostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.print_mail.cheques.with_raw_response.retrieve_url( + "", + ) + + @pytest.mark.skip() + @parametrize + def test_method_retrieve_with_deposit_ready_pdf(self, client: PostGrid) -> None: + cheque = client.print_mail.cheques.retrieve_with_deposit_ready_pdf( + "id", + ) + assert_matches_type(Cheque, cheque, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_retrieve_with_deposit_ready_pdf(self, client: PostGrid) -> None: + response = client.print_mail.cheques.with_raw_response.retrieve_with_deposit_ready_pdf( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + cheque = response.parse() + assert_matches_type(Cheque, cheque, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_retrieve_with_deposit_ready_pdf(self, client: PostGrid) -> None: + with client.print_mail.cheques.with_streaming_response.retrieve_with_deposit_ready_pdf( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + cheque = response.parse() + assert_matches_type(Cheque, cheque, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_path_params_retrieve_with_deposit_ready_pdf(self, client: PostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.print_mail.cheques.with_raw_response.retrieve_with_deposit_ready_pdf( + "", + ) + + +class TestAsyncCheques: + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip() + @parametrize + async def test_method_create(self, async_client: AsyncPostGrid) -> None: + cheque = await async_client.print_mail.cheques.create( + amount=1000, + bank_account="bank_123", + from_="contact_123", + to="contact_123", + ) + assert_matches_type(Cheque, cheque, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncPostGrid) -> None: + cheque = await async_client.print_mail.cheques.create( + amount=1000, + bank_account="bank_123", + from_="contact_123", + to="contact_123", + currency_code="USD", + description="description", + digital_only={"watermark": "watermark"}, + envelope="standard", + logo_url="https://example.com", + mailing_class="first_class", + memo="memo", + merge_variables={"foo": "bar"}, + message="message", + metadata={"foo": "bar"}, + number=123456, + redirect_to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + "address_line2": "addressLine2", + "city": "city", + "company_name": "companyName", + "description": "description", + "email": "email", + "force_verified_status": True, + "job_title": "jobTitle", + "last_name": "lastName", + "metadata": {"foo": "bar"}, + "phone_number": "phoneNumber", + "postal_or_zip": "postalOrZip", + "province_or_state": "provinceOrState", + "skip_verification": True, + }, + send_date=parse_datetime("2019-12-27T18:11:19.117Z"), + size="us_letter", + ) + assert_matches_type(Cheque, cheque, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_create(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.cheques.with_raw_response.create( + amount=1000, + bank_account="bank_123", + from_="contact_123", + to="contact_123", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + cheque = await response.parse() + assert_matches_type(Cheque, cheque, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_create(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.cheques.with_streaming_response.create( + amount=1000, + bank_account="bank_123", + from_="contact_123", + to="contact_123", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + cheque = await response.parse() + assert_matches_type(Cheque, cheque, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_method_retrieve(self, async_client: AsyncPostGrid) -> None: + cheque = await async_client.print_mail.cheques.retrieve( + "id", + ) + assert_matches_type(Cheque, cheque, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.cheques.with_raw_response.retrieve( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + cheque = await response.parse() + assert_matches_type(Cheque, cheque, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.cheques.with_streaming_response.retrieve( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + cheque = await response.parse() + assert_matches_type(Cheque, cheque, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncPostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.print_mail.cheques.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip() + @parametrize + async def test_method_list(self, async_client: AsyncPostGrid) -> None: + cheque = await async_client.print_mail.cheques.list() + assert_matches_type(AsyncSkipLimit[Cheque], cheque, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncPostGrid) -> None: + cheque = await async_client.print_mail.cheques.list( + limit=0, + search="search", + skip=0, + ) + assert_matches_type(AsyncSkipLimit[Cheque], cheque, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_list(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.cheques.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + cheque = await response.parse() + assert_matches_type(AsyncSkipLimit[Cheque], cheque, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_list(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.cheques.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + cheque = await response.parse() + assert_matches_type(AsyncSkipLimit[Cheque], cheque, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_method_delete(self, async_client: AsyncPostGrid) -> None: + cheque = await async_client.print_mail.cheques.delete( + "id", + ) + assert_matches_type(Cheque, cheque, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_delete(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.cheques.with_raw_response.delete( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + cheque = await response.parse() + assert_matches_type(Cheque, cheque, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.cheques.with_streaming_response.delete( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + cheque = await response.parse() + assert_matches_type(Cheque, cheque, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_path_params_delete(self, async_client: AsyncPostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.print_mail.cheques.with_raw_response.delete( + "", + ) + + @pytest.mark.skip() + @parametrize + async def test_method_retrieve_url(self, async_client: AsyncPostGrid) -> None: + cheque = await async_client.print_mail.cheques.retrieve_url( + "id", + ) + assert_matches_type(ChequeRetrieveURLResponse, cheque, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_retrieve_url(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.cheques.with_raw_response.retrieve_url( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + cheque = await response.parse() + assert_matches_type(ChequeRetrieveURLResponse, cheque, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_retrieve_url(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.cheques.with_streaming_response.retrieve_url( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + cheque = await response.parse() + assert_matches_type(ChequeRetrieveURLResponse, cheque, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_path_params_retrieve_url(self, async_client: AsyncPostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.print_mail.cheques.with_raw_response.retrieve_url( + "", + ) + + @pytest.mark.skip() + @parametrize + async def test_method_retrieve_with_deposit_ready_pdf(self, async_client: AsyncPostGrid) -> None: + cheque = await async_client.print_mail.cheques.retrieve_with_deposit_ready_pdf( + "id", + ) + assert_matches_type(Cheque, cheque, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_retrieve_with_deposit_ready_pdf(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.cheques.with_raw_response.retrieve_with_deposit_ready_pdf( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + cheque = await response.parse() + assert_matches_type(Cheque, cheque, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_retrieve_with_deposit_ready_pdf(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.cheques.with_streaming_response.retrieve_with_deposit_ready_pdf( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + cheque = await response.parse() + assert_matches_type(Cheque, cheque, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_path_params_retrieve_with_deposit_ready_pdf(self, async_client: AsyncPostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.print_mail.cheques.with_raw_response.retrieve_with_deposit_ready_pdf( + "", + ) diff --git a/tests/api_resources/print_mail/test_contacts.py b/tests/api_resources/print_mail/test_contacts.py new file mode 100644 index 0000000..54ef305 --- /dev/null +++ b/tests/api_resources/print_mail/test_contacts.py @@ -0,0 +1,519 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from postgrid import PostGrid, AsyncPostGrid +from tests.utils import assert_matches_type +from postgrid.pagination import SyncSkipLimit, AsyncSkipLimit +from postgrid.types.print_mail import Contact, ContactDeleteResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestContacts: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip() + @parametrize + def test_method_create_overload_1(self, client: PostGrid) -> None: + contact = client.print_mail.contacts.create( + address_line1="addressLine1", + country_code="countryCode", + first_name="firstName", + ) + assert_matches_type(Contact, contact, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_create_with_all_params_overload_1(self, client: PostGrid) -> None: + contact = client.print_mail.contacts.create( + address_line1="addressLine1", + country_code="countryCode", + first_name="firstName", + address_line2="addressLine2", + city="city", + company_name="companyName", + description="description", + email="email", + force_verified_status=True, + job_title="jobTitle", + last_name="lastName", + metadata={"foo": "bar"}, + phone_number="phoneNumber", + postal_or_zip="postalOrZip", + province_or_state="provinceOrState", + skip_verification=True, + ) + assert_matches_type(Contact, contact, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_create_overload_1(self, client: PostGrid) -> None: + response = client.print_mail.contacts.with_raw_response.create( + address_line1="addressLine1", + country_code="countryCode", + first_name="firstName", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + contact = response.parse() + assert_matches_type(Contact, contact, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_create_overload_1(self, client: PostGrid) -> None: + with client.print_mail.contacts.with_streaming_response.create( + address_line1="addressLine1", + country_code="countryCode", + first_name="firstName", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + contact = response.parse() + assert_matches_type(Contact, contact, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_method_create_overload_2(self, client: PostGrid) -> None: + contact = client.print_mail.contacts.create( + address_line1="addressLine1", + company_name="companyName", + country_code="countryCode", + ) + assert_matches_type(Contact, contact, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_create_with_all_params_overload_2(self, client: PostGrid) -> None: + contact = client.print_mail.contacts.create( + address_line1="addressLine1", + company_name="companyName", + country_code="countryCode", + address_line2="addressLine2", + city="city", + description="description", + email="email", + first_name="firstName", + force_verified_status=True, + job_title="jobTitle", + last_name="lastName", + metadata={"foo": "bar"}, + phone_number="phoneNumber", + postal_or_zip="postalOrZip", + province_or_state="provinceOrState", + skip_verification=True, + ) + assert_matches_type(Contact, contact, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_create_overload_2(self, client: PostGrid) -> None: + response = client.print_mail.contacts.with_raw_response.create( + address_line1="addressLine1", + company_name="companyName", + country_code="countryCode", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + contact = response.parse() + assert_matches_type(Contact, contact, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_create_overload_2(self, client: PostGrid) -> None: + with client.print_mail.contacts.with_streaming_response.create( + address_line1="addressLine1", + company_name="companyName", + country_code="countryCode", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + contact = response.parse() + assert_matches_type(Contact, contact, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_method_retrieve(self, client: PostGrid) -> None: + contact = client.print_mail.contacts.retrieve( + "id", + ) + assert_matches_type(Contact, contact, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_retrieve(self, client: PostGrid) -> None: + response = client.print_mail.contacts.with_raw_response.retrieve( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + contact = response.parse() + assert_matches_type(Contact, contact, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_retrieve(self, client: PostGrid) -> None: + with client.print_mail.contacts.with_streaming_response.retrieve( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + contact = response.parse() + assert_matches_type(Contact, contact, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_path_params_retrieve(self, client: PostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.print_mail.contacts.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip() + @parametrize + def test_method_list(self, client: PostGrid) -> None: + contact = client.print_mail.contacts.list() + assert_matches_type(SyncSkipLimit[Contact], contact, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_list_with_all_params(self, client: PostGrid) -> None: + contact = client.print_mail.contacts.list( + limit=0, + search="search", + skip=0, + ) + assert_matches_type(SyncSkipLimit[Contact], contact, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_list(self, client: PostGrid) -> None: + response = client.print_mail.contacts.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + contact = response.parse() + assert_matches_type(SyncSkipLimit[Contact], contact, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_list(self, client: PostGrid) -> None: + with client.print_mail.contacts.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + contact = response.parse() + assert_matches_type(SyncSkipLimit[Contact], contact, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_method_delete(self, client: PostGrid) -> None: + contact = client.print_mail.contacts.delete( + "id", + ) + assert_matches_type(ContactDeleteResponse, contact, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_delete(self, client: PostGrid) -> None: + response = client.print_mail.contacts.with_raw_response.delete( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + contact = response.parse() + assert_matches_type(ContactDeleteResponse, contact, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_delete(self, client: PostGrid) -> None: + with client.print_mail.contacts.with_streaming_response.delete( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + contact = response.parse() + assert_matches_type(ContactDeleteResponse, contact, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_path_params_delete(self, client: PostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.print_mail.contacts.with_raw_response.delete( + "", + ) + + +class TestAsyncContacts: + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip() + @parametrize + async def test_method_create_overload_1(self, async_client: AsyncPostGrid) -> None: + contact = await async_client.print_mail.contacts.create( + address_line1="addressLine1", + country_code="countryCode", + first_name="firstName", + ) + assert_matches_type(Contact, contact, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_create_with_all_params_overload_1(self, async_client: AsyncPostGrid) -> None: + contact = await async_client.print_mail.contacts.create( + address_line1="addressLine1", + country_code="countryCode", + first_name="firstName", + address_line2="addressLine2", + city="city", + company_name="companyName", + description="description", + email="email", + force_verified_status=True, + job_title="jobTitle", + last_name="lastName", + metadata={"foo": "bar"}, + phone_number="phoneNumber", + postal_or_zip="postalOrZip", + province_or_state="provinceOrState", + skip_verification=True, + ) + assert_matches_type(Contact, contact, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_create_overload_1(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.contacts.with_raw_response.create( + address_line1="addressLine1", + country_code="countryCode", + first_name="firstName", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + contact = await response.parse() + assert_matches_type(Contact, contact, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_create_overload_1(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.contacts.with_streaming_response.create( + address_line1="addressLine1", + country_code="countryCode", + first_name="firstName", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + contact = await response.parse() + assert_matches_type(Contact, contact, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_method_create_overload_2(self, async_client: AsyncPostGrid) -> None: + contact = await async_client.print_mail.contacts.create( + address_line1="addressLine1", + company_name="companyName", + country_code="countryCode", + ) + assert_matches_type(Contact, contact, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_create_with_all_params_overload_2(self, async_client: AsyncPostGrid) -> None: + contact = await async_client.print_mail.contacts.create( + address_line1="addressLine1", + company_name="companyName", + country_code="countryCode", + address_line2="addressLine2", + city="city", + description="description", + email="email", + first_name="firstName", + force_verified_status=True, + job_title="jobTitle", + last_name="lastName", + metadata={"foo": "bar"}, + phone_number="phoneNumber", + postal_or_zip="postalOrZip", + province_or_state="provinceOrState", + skip_verification=True, + ) + assert_matches_type(Contact, contact, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_create_overload_2(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.contacts.with_raw_response.create( + address_line1="addressLine1", + company_name="companyName", + country_code="countryCode", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + contact = await response.parse() + assert_matches_type(Contact, contact, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_create_overload_2(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.contacts.with_streaming_response.create( + address_line1="addressLine1", + company_name="companyName", + country_code="countryCode", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + contact = await response.parse() + assert_matches_type(Contact, contact, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_method_retrieve(self, async_client: AsyncPostGrid) -> None: + contact = await async_client.print_mail.contacts.retrieve( + "id", + ) + assert_matches_type(Contact, contact, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.contacts.with_raw_response.retrieve( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + contact = await response.parse() + assert_matches_type(Contact, contact, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.contacts.with_streaming_response.retrieve( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + contact = await response.parse() + assert_matches_type(Contact, contact, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncPostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.print_mail.contacts.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip() + @parametrize + async def test_method_list(self, async_client: AsyncPostGrid) -> None: + contact = await async_client.print_mail.contacts.list() + assert_matches_type(AsyncSkipLimit[Contact], contact, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncPostGrid) -> None: + contact = await async_client.print_mail.contacts.list( + limit=0, + search="search", + skip=0, + ) + assert_matches_type(AsyncSkipLimit[Contact], contact, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_list(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.contacts.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + contact = await response.parse() + assert_matches_type(AsyncSkipLimit[Contact], contact, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_list(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.contacts.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + contact = await response.parse() + assert_matches_type(AsyncSkipLimit[Contact], contact, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_method_delete(self, async_client: AsyncPostGrid) -> None: + contact = await async_client.print_mail.contacts.delete( + "id", + ) + assert_matches_type(ContactDeleteResponse, contact, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_delete(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.contacts.with_raw_response.delete( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + contact = await response.parse() + assert_matches_type(ContactDeleteResponse, contact, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.contacts.with_streaming_response.delete( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + contact = await response.parse() + assert_matches_type(ContactDeleteResponse, contact, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_path_params_delete(self, async_client: AsyncPostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.print_mail.contacts.with_raw_response.delete( + "", + ) diff --git a/tests/api_resources/print_mail/test_letters.py b/tests/api_resources/print_mail/test_letters.py new file mode 100644 index 0000000..e56cfad --- /dev/null +++ b/tests/api_resources/print_mail/test_letters.py @@ -0,0 +1,979 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from postgrid import PostGrid, AsyncPostGrid +from tests.utils import assert_matches_type +from postgrid._utils import parse_datetime +from postgrid.pagination import SyncSkipLimit, AsyncSkipLimit +from postgrid.types.print_mail import ( + Letter, + LetterRetrieveURLResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestLetters: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip() + @parametrize + def test_method_create_overload_1(self, client: PostGrid) -> None: + letter = client.print_mail.letters.create( + from_={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + html="html", + to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + ) + assert_matches_type(Letter, letter, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_create_with_all_params_overload_1(self, client: PostGrid) -> None: + letter = client.print_mail.letters.create( + from_={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + "address_line2": "addressLine2", + "city": "city", + "company_name": "companyName", + "description": "description", + "email": "email", + "force_verified_status": True, + "job_title": "jobTitle", + "last_name": "lastName", + "metadata": {"foo": "bar"}, + "phone_number": "phoneNumber", + "postal_or_zip": "postalOrZip", + "province_or_state": "provinceOrState", + "skip_verification": True, + }, + html="html", + to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + "address_line2": "addressLine2", + "city": "city", + "company_name": "companyName", + "description": "description", + "email": "email", + "force_verified_status": True, + "job_title": "jobTitle", + "last_name": "lastName", + "metadata": {"foo": "bar"}, + "phone_number": "phoneNumber", + "postal_or_zip": "postalOrZip", + "province_or_state": "provinceOrState", + "skip_verification": True, + }, + address_placement="top_first_page", + attached_pdf={ + "file": "https://example.com", + "placement": "before_template", + }, + color=True, + description="description", + double_sided=True, + envelope="envelope", + mailing_class="first_class", + merge_variables={"foo": "bar"}, + metadata={"foo": "bar"}, + perforated_page=1, + plastic_card={ + "size": "standard", + "double_sided": { + "back_html": "backHTML", + "back_template": "backTemplate", + "front_html": "frontHTML", + "front_template": "frontTemplate", + "pdf": "https://example.com", + }, + "single_sided": { + "html": "html", + "pdf": "https://example.com", + "template": "template", + }, + }, + return_envelope="returnEnvelope", + send_date=parse_datetime("2019-12-27T18:11:19.117Z"), + size="us_letter", + ) + assert_matches_type(Letter, letter, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_create_overload_1(self, client: PostGrid) -> None: + response = client.print_mail.letters.with_raw_response.create( + from_={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + html="html", + to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + letter = response.parse() + assert_matches_type(Letter, letter, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_create_overload_1(self, client: PostGrid) -> None: + with client.print_mail.letters.with_streaming_response.create( + from_={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + html="html", + to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + letter = response.parse() + assert_matches_type(Letter, letter, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_method_create_overload_2(self, client: PostGrid) -> None: + letter = client.print_mail.letters.create( + template="template", + ) + assert_matches_type(Letter, letter, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_create_overload_2(self, client: PostGrid) -> None: + response = client.print_mail.letters.with_raw_response.create( + template="template", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + letter = response.parse() + assert_matches_type(Letter, letter, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_create_overload_2(self, client: PostGrid) -> None: + with client.print_mail.letters.with_streaming_response.create( + template="template", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + letter = response.parse() + assert_matches_type(Letter, letter, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_method_create_overload_3(self, client: PostGrid) -> None: + letter = client.print_mail.letters.create( + from_={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + pdf="https://example.com", + to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + ) + assert_matches_type(Letter, letter, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_create_with_all_params_overload_3(self, client: PostGrid) -> None: + letter = client.print_mail.letters.create( + from_={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + "address_line2": "addressLine2", + "city": "city", + "company_name": "companyName", + "description": "description", + "email": "email", + "force_verified_status": True, + "job_title": "jobTitle", + "last_name": "lastName", + "metadata": {"foo": "bar"}, + "phone_number": "phoneNumber", + "postal_or_zip": "postalOrZip", + "province_or_state": "provinceOrState", + "skip_verification": True, + }, + pdf="https://example.com", + to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + "address_line2": "addressLine2", + "city": "city", + "company_name": "companyName", + "description": "description", + "email": "email", + "force_verified_status": True, + "job_title": "jobTitle", + "last_name": "lastName", + "metadata": {"foo": "bar"}, + "phone_number": "phoneNumber", + "postal_or_zip": "postalOrZip", + "province_or_state": "provinceOrState", + "skip_verification": True, + }, + address_placement="top_first_page", + attached_pdf={ + "file": "https://example.com", + "placement": "before_template", + }, + color=True, + description="description", + double_sided=True, + envelope="envelope", + mailing_class="first_class", + merge_variables={"foo": "bar"}, + metadata={"foo": "bar"}, + perforated_page=1, + plastic_card={ + "size": "standard", + "double_sided": { + "back_html": "backHTML", + "back_template": "backTemplate", + "front_html": "frontHTML", + "front_template": "frontTemplate", + "pdf": "https://example.com", + }, + "single_sided": { + "html": "html", + "pdf": "https://example.com", + "template": "template", + }, + }, + return_envelope="returnEnvelope", + send_date=parse_datetime("2019-12-27T18:11:19.117Z"), + size="us_letter", + ) + assert_matches_type(Letter, letter, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_create_overload_3(self, client: PostGrid) -> None: + response = client.print_mail.letters.with_raw_response.create( + from_={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + pdf="https://example.com", + to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + letter = response.parse() + assert_matches_type(Letter, letter, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_create_overload_3(self, client: PostGrid) -> None: + with client.print_mail.letters.with_streaming_response.create( + from_={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + pdf="https://example.com", + to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + letter = response.parse() + assert_matches_type(Letter, letter, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_method_retrieve(self, client: PostGrid) -> None: + letter = client.print_mail.letters.retrieve( + "id", + ) + assert_matches_type(Letter, letter, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_retrieve(self, client: PostGrid) -> None: + response = client.print_mail.letters.with_raw_response.retrieve( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + letter = response.parse() + assert_matches_type(Letter, letter, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_retrieve(self, client: PostGrid) -> None: + with client.print_mail.letters.with_streaming_response.retrieve( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + letter = response.parse() + assert_matches_type(Letter, letter, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_path_params_retrieve(self, client: PostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.print_mail.letters.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip() + @parametrize + def test_method_list(self, client: PostGrid) -> None: + letter = client.print_mail.letters.list() + assert_matches_type(SyncSkipLimit[Letter], letter, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_list_with_all_params(self, client: PostGrid) -> None: + letter = client.print_mail.letters.list( + limit=0, + search="search", + skip=0, + ) + assert_matches_type(SyncSkipLimit[Letter], letter, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_list(self, client: PostGrid) -> None: + response = client.print_mail.letters.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + letter = response.parse() + assert_matches_type(SyncSkipLimit[Letter], letter, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_list(self, client: PostGrid) -> None: + with client.print_mail.letters.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + letter = response.parse() + assert_matches_type(SyncSkipLimit[Letter], letter, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_method_delete(self, client: PostGrid) -> None: + letter = client.print_mail.letters.delete( + "id", + ) + assert_matches_type(Letter, letter, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_delete(self, client: PostGrid) -> None: + response = client.print_mail.letters.with_raw_response.delete( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + letter = response.parse() + assert_matches_type(Letter, letter, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_delete(self, client: PostGrid) -> None: + with client.print_mail.letters.with_streaming_response.delete( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + letter = response.parse() + assert_matches_type(Letter, letter, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_path_params_delete(self, client: PostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.print_mail.letters.with_raw_response.delete( + "", + ) + + @pytest.mark.skip() + @parametrize + def test_method_retrieve_url(self, client: PostGrid) -> None: + letter = client.print_mail.letters.retrieve_url( + "id", + ) + assert_matches_type(LetterRetrieveURLResponse, letter, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_retrieve_url(self, client: PostGrid) -> None: + response = client.print_mail.letters.with_raw_response.retrieve_url( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + letter = response.parse() + assert_matches_type(LetterRetrieveURLResponse, letter, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_retrieve_url(self, client: PostGrid) -> None: + with client.print_mail.letters.with_streaming_response.retrieve_url( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + letter = response.parse() + assert_matches_type(LetterRetrieveURLResponse, letter, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_path_params_retrieve_url(self, client: PostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.print_mail.letters.with_raw_response.retrieve_url( + "", + ) + + +class TestAsyncLetters: + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip() + @parametrize + async def test_method_create_overload_1(self, async_client: AsyncPostGrid) -> None: + letter = await async_client.print_mail.letters.create( + from_={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + html="html", + to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + ) + assert_matches_type(Letter, letter, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_create_with_all_params_overload_1(self, async_client: AsyncPostGrid) -> None: + letter = await async_client.print_mail.letters.create( + from_={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + "address_line2": "addressLine2", + "city": "city", + "company_name": "companyName", + "description": "description", + "email": "email", + "force_verified_status": True, + "job_title": "jobTitle", + "last_name": "lastName", + "metadata": {"foo": "bar"}, + "phone_number": "phoneNumber", + "postal_or_zip": "postalOrZip", + "province_or_state": "provinceOrState", + "skip_verification": True, + }, + html="html", + to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + "address_line2": "addressLine2", + "city": "city", + "company_name": "companyName", + "description": "description", + "email": "email", + "force_verified_status": True, + "job_title": "jobTitle", + "last_name": "lastName", + "metadata": {"foo": "bar"}, + "phone_number": "phoneNumber", + "postal_or_zip": "postalOrZip", + "province_or_state": "provinceOrState", + "skip_verification": True, + }, + address_placement="top_first_page", + attached_pdf={ + "file": "https://example.com", + "placement": "before_template", + }, + color=True, + description="description", + double_sided=True, + envelope="envelope", + mailing_class="first_class", + merge_variables={"foo": "bar"}, + metadata={"foo": "bar"}, + perforated_page=1, + plastic_card={ + "size": "standard", + "double_sided": { + "back_html": "backHTML", + "back_template": "backTemplate", + "front_html": "frontHTML", + "front_template": "frontTemplate", + "pdf": "https://example.com", + }, + "single_sided": { + "html": "html", + "pdf": "https://example.com", + "template": "template", + }, + }, + return_envelope="returnEnvelope", + send_date=parse_datetime("2019-12-27T18:11:19.117Z"), + size="us_letter", + ) + assert_matches_type(Letter, letter, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_create_overload_1(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.letters.with_raw_response.create( + from_={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + html="html", + to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + letter = await response.parse() + assert_matches_type(Letter, letter, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_create_overload_1(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.letters.with_streaming_response.create( + from_={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + html="html", + to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + letter = await response.parse() + assert_matches_type(Letter, letter, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_method_create_overload_2(self, async_client: AsyncPostGrid) -> None: + letter = await async_client.print_mail.letters.create( + template="template", + ) + assert_matches_type(Letter, letter, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_create_overload_2(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.letters.with_raw_response.create( + template="template", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + letter = await response.parse() + assert_matches_type(Letter, letter, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_create_overload_2(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.letters.with_streaming_response.create( + template="template", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + letter = await response.parse() + assert_matches_type(Letter, letter, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_method_create_overload_3(self, async_client: AsyncPostGrid) -> None: + letter = await async_client.print_mail.letters.create( + from_={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + pdf="https://example.com", + to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + ) + assert_matches_type(Letter, letter, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_create_with_all_params_overload_3(self, async_client: AsyncPostGrid) -> None: + letter = await async_client.print_mail.letters.create( + from_={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + "address_line2": "addressLine2", + "city": "city", + "company_name": "companyName", + "description": "description", + "email": "email", + "force_verified_status": True, + "job_title": "jobTitle", + "last_name": "lastName", + "metadata": {"foo": "bar"}, + "phone_number": "phoneNumber", + "postal_or_zip": "postalOrZip", + "province_or_state": "provinceOrState", + "skip_verification": True, + }, + pdf="https://example.com", + to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + "address_line2": "addressLine2", + "city": "city", + "company_name": "companyName", + "description": "description", + "email": "email", + "force_verified_status": True, + "job_title": "jobTitle", + "last_name": "lastName", + "metadata": {"foo": "bar"}, + "phone_number": "phoneNumber", + "postal_or_zip": "postalOrZip", + "province_or_state": "provinceOrState", + "skip_verification": True, + }, + address_placement="top_first_page", + attached_pdf={ + "file": "https://example.com", + "placement": "before_template", + }, + color=True, + description="description", + double_sided=True, + envelope="envelope", + mailing_class="first_class", + merge_variables={"foo": "bar"}, + metadata={"foo": "bar"}, + perforated_page=1, + plastic_card={ + "size": "standard", + "double_sided": { + "back_html": "backHTML", + "back_template": "backTemplate", + "front_html": "frontHTML", + "front_template": "frontTemplate", + "pdf": "https://example.com", + }, + "single_sided": { + "html": "html", + "pdf": "https://example.com", + "template": "template", + }, + }, + return_envelope="returnEnvelope", + send_date=parse_datetime("2019-12-27T18:11:19.117Z"), + size="us_letter", + ) + assert_matches_type(Letter, letter, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_create_overload_3(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.letters.with_raw_response.create( + from_={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + pdf="https://example.com", + to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + letter = await response.parse() + assert_matches_type(Letter, letter, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_create_overload_3(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.letters.with_streaming_response.create( + from_={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + pdf="https://example.com", + to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + letter = await response.parse() + assert_matches_type(Letter, letter, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_method_retrieve(self, async_client: AsyncPostGrid) -> None: + letter = await async_client.print_mail.letters.retrieve( + "id", + ) + assert_matches_type(Letter, letter, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.letters.with_raw_response.retrieve( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + letter = await response.parse() + assert_matches_type(Letter, letter, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.letters.with_streaming_response.retrieve( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + letter = await response.parse() + assert_matches_type(Letter, letter, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncPostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.print_mail.letters.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip() + @parametrize + async def test_method_list(self, async_client: AsyncPostGrid) -> None: + letter = await async_client.print_mail.letters.list() + assert_matches_type(AsyncSkipLimit[Letter], letter, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncPostGrid) -> None: + letter = await async_client.print_mail.letters.list( + limit=0, + search="search", + skip=0, + ) + assert_matches_type(AsyncSkipLimit[Letter], letter, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_list(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.letters.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + letter = await response.parse() + assert_matches_type(AsyncSkipLimit[Letter], letter, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_list(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.letters.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + letter = await response.parse() + assert_matches_type(AsyncSkipLimit[Letter], letter, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_method_delete(self, async_client: AsyncPostGrid) -> None: + letter = await async_client.print_mail.letters.delete( + "id", + ) + assert_matches_type(Letter, letter, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_delete(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.letters.with_raw_response.delete( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + letter = await response.parse() + assert_matches_type(Letter, letter, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.letters.with_streaming_response.delete( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + letter = await response.parse() + assert_matches_type(Letter, letter, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_path_params_delete(self, async_client: AsyncPostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.print_mail.letters.with_raw_response.delete( + "", + ) + + @pytest.mark.skip() + @parametrize + async def test_method_retrieve_url(self, async_client: AsyncPostGrid) -> None: + letter = await async_client.print_mail.letters.retrieve_url( + "id", + ) + assert_matches_type(LetterRetrieveURLResponse, letter, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_retrieve_url(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.letters.with_raw_response.retrieve_url( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + letter = await response.parse() + assert_matches_type(LetterRetrieveURLResponse, letter, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_retrieve_url(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.letters.with_streaming_response.retrieve_url( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + letter = await response.parse() + assert_matches_type(LetterRetrieveURLResponse, letter, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_path_params_retrieve_url(self, async_client: AsyncPostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.print_mail.letters.with_raw_response.retrieve_url( + "", + ) diff --git a/tests/api_resources/print_mail/test_mailing_list_imports.py b/tests/api_resources/print_mail/test_mailing_list_imports.py new file mode 100644 index 0000000..6fe9f0c --- /dev/null +++ b/tests/api_resources/print_mail/test_mailing_list_imports.py @@ -0,0 +1,566 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from postgrid import PostGrid, AsyncPostGrid +from tests.utils import assert_matches_type +from postgrid.pagination import SyncSkipLimit, AsyncSkipLimit +from postgrid.types.print_mail import ( + MailingListImportResponse, + MailingListImportDeleteResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestMailingListImports: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip() + @parametrize + def test_method_create(self, client: PostGrid) -> None: + mailing_list_import = client.print_mail.mailing_list_imports.create( + file="https://signed-upload-url.csv", + file_type="csv", + receiver_address_mapping={ + "description": "Description", + "firstName": "First Name", + "lastName": "Last Name", + "email": "Email", + "addressLine1": "Address", + "city": "City", + "postalOrZip": "Postal Code", + "provinceOrState": "State", + "countryCode": "Country", + }, + ) + assert_matches_type(MailingListImportResponse, mailing_list_import, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_create_with_all_params(self, client: PostGrid) -> None: + mailing_list_import = client.print_mail.mailing_list_imports.create( + file="https://signed-upload-url.csv", + file_type="csv", + receiver_address_mapping={ + "description": "Description", + "firstName": "First Name", + "lastName": "Last Name", + "email": "Email", + "addressLine1": "Address", + "city": "City", + "postalOrZip": "Postal Code", + "provinceOrState": "State", + "countryCode": "Country", + }, + description="description", + metadata={"foo": "bar"}, + receiver_merge_variable_mapping={"foo": "string"}, + sender_address_mapping={"foo": "string"}, + sender_merge_variable_mapping={"foo": "string"}, + idempotency_key="idempotency-key", + ) + assert_matches_type(MailingListImportResponse, mailing_list_import, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_create(self, client: PostGrid) -> None: + response = client.print_mail.mailing_list_imports.with_raw_response.create( + file="https://signed-upload-url.csv", + file_type="csv", + receiver_address_mapping={ + "description": "Description", + "firstName": "First Name", + "lastName": "Last Name", + "email": "Email", + "addressLine1": "Address", + "city": "City", + "postalOrZip": "Postal Code", + "provinceOrState": "State", + "countryCode": "Country", + }, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + mailing_list_import = response.parse() + assert_matches_type(MailingListImportResponse, mailing_list_import, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_create(self, client: PostGrid) -> None: + with client.print_mail.mailing_list_imports.with_streaming_response.create( + file="https://signed-upload-url.csv", + file_type="csv", + receiver_address_mapping={ + "description": "Description", + "firstName": "First Name", + "lastName": "Last Name", + "email": "Email", + "addressLine1": "Address", + "city": "City", + "postalOrZip": "Postal Code", + "provinceOrState": "State", + "countryCode": "Country", + }, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + mailing_list_import = response.parse() + assert_matches_type(MailingListImportResponse, mailing_list_import, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_method_retrieve(self, client: PostGrid) -> None: + mailing_list_import = client.print_mail.mailing_list_imports.retrieve( + "id", + ) + assert_matches_type(MailingListImportResponse, mailing_list_import, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_retrieve(self, client: PostGrid) -> None: + response = client.print_mail.mailing_list_imports.with_raw_response.retrieve( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + mailing_list_import = response.parse() + assert_matches_type(MailingListImportResponse, mailing_list_import, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_retrieve(self, client: PostGrid) -> None: + with client.print_mail.mailing_list_imports.with_streaming_response.retrieve( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + mailing_list_import = response.parse() + assert_matches_type(MailingListImportResponse, mailing_list_import, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_path_params_retrieve(self, client: PostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.print_mail.mailing_list_imports.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip() + @parametrize + def test_method_update(self, client: PostGrid) -> None: + mailing_list_import = client.print_mail.mailing_list_imports.update( + id="id", + ) + assert_matches_type(MailingListImportResponse, mailing_list_import, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_update_with_all_params(self, client: PostGrid) -> None: + mailing_list_import = client.print_mail.mailing_list_imports.update( + id="id", + description="Corrected description", + metadata={"batch": "spring_sale"}, + ) + assert_matches_type(MailingListImportResponse, mailing_list_import, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_update(self, client: PostGrid) -> None: + response = client.print_mail.mailing_list_imports.with_raw_response.update( + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + mailing_list_import = response.parse() + assert_matches_type(MailingListImportResponse, mailing_list_import, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_update(self, client: PostGrid) -> None: + with client.print_mail.mailing_list_imports.with_streaming_response.update( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + mailing_list_import = response.parse() + assert_matches_type(MailingListImportResponse, mailing_list_import, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_path_params_update(self, client: PostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.print_mail.mailing_list_imports.with_raw_response.update( + id="", + ) + + @pytest.mark.skip() + @parametrize + def test_method_list(self, client: PostGrid) -> None: + mailing_list_import = client.print_mail.mailing_list_imports.list() + assert_matches_type(SyncSkipLimit[MailingListImportResponse], mailing_list_import, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_list_with_all_params(self, client: PostGrid) -> None: + mailing_list_import = client.print_mail.mailing_list_imports.list( + limit=0, + search="search", + skip=0, + ) + assert_matches_type(SyncSkipLimit[MailingListImportResponse], mailing_list_import, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_list(self, client: PostGrid) -> None: + response = client.print_mail.mailing_list_imports.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + mailing_list_import = response.parse() + assert_matches_type(SyncSkipLimit[MailingListImportResponse], mailing_list_import, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_list(self, client: PostGrid) -> None: + with client.print_mail.mailing_list_imports.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + mailing_list_import = response.parse() + assert_matches_type(SyncSkipLimit[MailingListImportResponse], mailing_list_import, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_method_delete(self, client: PostGrid) -> None: + mailing_list_import = client.print_mail.mailing_list_imports.delete( + "id", + ) + assert_matches_type(MailingListImportDeleteResponse, mailing_list_import, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_delete(self, client: PostGrid) -> None: + response = client.print_mail.mailing_list_imports.with_raw_response.delete( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + mailing_list_import = response.parse() + assert_matches_type(MailingListImportDeleteResponse, mailing_list_import, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_delete(self, client: PostGrid) -> None: + with client.print_mail.mailing_list_imports.with_streaming_response.delete( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + mailing_list_import = response.parse() + assert_matches_type(MailingListImportDeleteResponse, mailing_list_import, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_path_params_delete(self, client: PostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.print_mail.mailing_list_imports.with_raw_response.delete( + "", + ) + + +class TestAsyncMailingListImports: + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip() + @parametrize + async def test_method_create(self, async_client: AsyncPostGrid) -> None: + mailing_list_import = await async_client.print_mail.mailing_list_imports.create( + file="https://signed-upload-url.csv", + file_type="csv", + receiver_address_mapping={ + "description": "Description", + "firstName": "First Name", + "lastName": "Last Name", + "email": "Email", + "addressLine1": "Address", + "city": "City", + "postalOrZip": "Postal Code", + "provinceOrState": "State", + "countryCode": "Country", + }, + ) + assert_matches_type(MailingListImportResponse, mailing_list_import, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncPostGrid) -> None: + mailing_list_import = await async_client.print_mail.mailing_list_imports.create( + file="https://signed-upload-url.csv", + file_type="csv", + receiver_address_mapping={ + "description": "Description", + "firstName": "First Name", + "lastName": "Last Name", + "email": "Email", + "addressLine1": "Address", + "city": "City", + "postalOrZip": "Postal Code", + "provinceOrState": "State", + "countryCode": "Country", + }, + description="description", + metadata={"foo": "bar"}, + receiver_merge_variable_mapping={"foo": "string"}, + sender_address_mapping={"foo": "string"}, + sender_merge_variable_mapping={"foo": "string"}, + idempotency_key="idempotency-key", + ) + assert_matches_type(MailingListImportResponse, mailing_list_import, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_create(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.mailing_list_imports.with_raw_response.create( + file="https://signed-upload-url.csv", + file_type="csv", + receiver_address_mapping={ + "description": "Description", + "firstName": "First Name", + "lastName": "Last Name", + "email": "Email", + "addressLine1": "Address", + "city": "City", + "postalOrZip": "Postal Code", + "provinceOrState": "State", + "countryCode": "Country", + }, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + mailing_list_import = await response.parse() + assert_matches_type(MailingListImportResponse, mailing_list_import, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_create(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.mailing_list_imports.with_streaming_response.create( + file="https://signed-upload-url.csv", + file_type="csv", + receiver_address_mapping={ + "description": "Description", + "firstName": "First Name", + "lastName": "Last Name", + "email": "Email", + "addressLine1": "Address", + "city": "City", + "postalOrZip": "Postal Code", + "provinceOrState": "State", + "countryCode": "Country", + }, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + mailing_list_import = await response.parse() + assert_matches_type(MailingListImportResponse, mailing_list_import, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_method_retrieve(self, async_client: AsyncPostGrid) -> None: + mailing_list_import = await async_client.print_mail.mailing_list_imports.retrieve( + "id", + ) + assert_matches_type(MailingListImportResponse, mailing_list_import, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.mailing_list_imports.with_raw_response.retrieve( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + mailing_list_import = await response.parse() + assert_matches_type(MailingListImportResponse, mailing_list_import, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.mailing_list_imports.with_streaming_response.retrieve( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + mailing_list_import = await response.parse() + assert_matches_type(MailingListImportResponse, mailing_list_import, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncPostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.print_mail.mailing_list_imports.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip() + @parametrize + async def test_method_update(self, async_client: AsyncPostGrid) -> None: + mailing_list_import = await async_client.print_mail.mailing_list_imports.update( + id="id", + ) + assert_matches_type(MailingListImportResponse, mailing_list_import, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_update_with_all_params(self, async_client: AsyncPostGrid) -> None: + mailing_list_import = await async_client.print_mail.mailing_list_imports.update( + id="id", + description="Corrected description", + metadata={"batch": "spring_sale"}, + ) + assert_matches_type(MailingListImportResponse, mailing_list_import, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_update(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.mailing_list_imports.with_raw_response.update( + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + mailing_list_import = await response.parse() + assert_matches_type(MailingListImportResponse, mailing_list_import, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_update(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.mailing_list_imports.with_streaming_response.update( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + mailing_list_import = await response.parse() + assert_matches_type(MailingListImportResponse, mailing_list_import, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_path_params_update(self, async_client: AsyncPostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.print_mail.mailing_list_imports.with_raw_response.update( + id="", + ) + + @pytest.mark.skip() + @parametrize + async def test_method_list(self, async_client: AsyncPostGrid) -> None: + mailing_list_import = await async_client.print_mail.mailing_list_imports.list() + assert_matches_type(AsyncSkipLimit[MailingListImportResponse], mailing_list_import, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncPostGrid) -> None: + mailing_list_import = await async_client.print_mail.mailing_list_imports.list( + limit=0, + search="search", + skip=0, + ) + assert_matches_type(AsyncSkipLimit[MailingListImportResponse], mailing_list_import, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_list(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.mailing_list_imports.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + mailing_list_import = await response.parse() + assert_matches_type(AsyncSkipLimit[MailingListImportResponse], mailing_list_import, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_list(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.mailing_list_imports.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + mailing_list_import = await response.parse() + assert_matches_type(AsyncSkipLimit[MailingListImportResponse], mailing_list_import, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_method_delete(self, async_client: AsyncPostGrid) -> None: + mailing_list_import = await async_client.print_mail.mailing_list_imports.delete( + "id", + ) + assert_matches_type(MailingListImportDeleteResponse, mailing_list_import, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_delete(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.mailing_list_imports.with_raw_response.delete( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + mailing_list_import = await response.parse() + assert_matches_type(MailingListImportDeleteResponse, mailing_list_import, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.mailing_list_imports.with_streaming_response.delete( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + mailing_list_import = await response.parse() + assert_matches_type(MailingListImportDeleteResponse, mailing_list_import, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_path_params_delete(self, async_client: AsyncPostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.print_mail.mailing_list_imports.with_raw_response.delete( + "", + ) diff --git a/tests/api_resources/print_mail/test_mailing_lists.py b/tests/api_resources/print_mail/test_mailing_lists.py new file mode 100644 index 0000000..62cafea --- /dev/null +++ b/tests/api_resources/print_mail/test_mailing_lists.py @@ -0,0 +1,559 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from postgrid import PostGrid, AsyncPostGrid +from tests.utils import assert_matches_type +from postgrid.pagination import SyncSkipLimit, AsyncSkipLimit +from postgrid.types.print_mail import ( + MailingList, + MailingListUpdate, + MailingListDeleteResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestMailingLists: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip() + @parametrize + def test_method_create(self, client: PostGrid) -> None: + mailing_list = client.print_mail.mailing_lists.create() + assert_matches_type(MailingList, mailing_list, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_create_with_all_params(self, client: PostGrid) -> None: + mailing_list = client.print_mail.mailing_lists.create( + description="Test Mailing List", + metadata={"campaign": "bar"}, + idempotency_key="idempotency-key", + ) + assert_matches_type(MailingList, mailing_list, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_create(self, client: PostGrid) -> None: + response = client.print_mail.mailing_lists.with_raw_response.create() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + mailing_list = response.parse() + assert_matches_type(MailingList, mailing_list, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_create(self, client: PostGrid) -> None: + with client.print_mail.mailing_lists.with_streaming_response.create() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + mailing_list = response.parse() + assert_matches_type(MailingList, mailing_list, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_method_retrieve(self, client: PostGrid) -> None: + mailing_list = client.print_mail.mailing_lists.retrieve( + "id", + ) + assert_matches_type(MailingList, mailing_list, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_retrieve(self, client: PostGrid) -> None: + response = client.print_mail.mailing_lists.with_raw_response.retrieve( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + mailing_list = response.parse() + assert_matches_type(MailingList, mailing_list, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_retrieve(self, client: PostGrid) -> None: + with client.print_mail.mailing_lists.with_streaming_response.retrieve( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + mailing_list = response.parse() + assert_matches_type(MailingList, mailing_list, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_path_params_retrieve(self, client: PostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.print_mail.mailing_lists.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip() + @parametrize + def test_method_update(self, client: PostGrid) -> None: + mailing_list = client.print_mail.mailing_lists.update( + id="id", + ) + assert_matches_type(MailingListUpdate, mailing_list, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_update_with_all_params(self, client: PostGrid) -> None: + mailing_list = client.print_mail.mailing_lists.update( + id="id", + description="Updated Mailing List Description", + metadata={"foo": "bar"}, + ) + assert_matches_type(MailingListUpdate, mailing_list, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_update(self, client: PostGrid) -> None: + response = client.print_mail.mailing_lists.with_raw_response.update( + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + mailing_list = response.parse() + assert_matches_type(MailingListUpdate, mailing_list, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_update(self, client: PostGrid) -> None: + with client.print_mail.mailing_lists.with_streaming_response.update( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + mailing_list = response.parse() + assert_matches_type(MailingListUpdate, mailing_list, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_path_params_update(self, client: PostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.print_mail.mailing_lists.with_raw_response.update( + id="", + ) + + @pytest.mark.skip() + @parametrize + def test_method_list(self, client: PostGrid) -> None: + mailing_list = client.print_mail.mailing_lists.list() + assert_matches_type(SyncSkipLimit[MailingList], mailing_list, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_list_with_all_params(self, client: PostGrid) -> None: + mailing_list = client.print_mail.mailing_lists.list( + limit=0, + search="search", + skip=0, + ) + assert_matches_type(SyncSkipLimit[MailingList], mailing_list, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_list(self, client: PostGrid) -> None: + response = client.print_mail.mailing_lists.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + mailing_list = response.parse() + assert_matches_type(SyncSkipLimit[MailingList], mailing_list, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_list(self, client: PostGrid) -> None: + with client.print_mail.mailing_lists.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + mailing_list = response.parse() + assert_matches_type(SyncSkipLimit[MailingList], mailing_list, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_method_delete(self, client: PostGrid) -> None: + mailing_list = client.print_mail.mailing_lists.delete( + "id", + ) + assert_matches_type(MailingListDeleteResponse, mailing_list, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_delete(self, client: PostGrid) -> None: + response = client.print_mail.mailing_lists.with_raw_response.delete( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + mailing_list = response.parse() + assert_matches_type(MailingListDeleteResponse, mailing_list, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_delete(self, client: PostGrid) -> None: + with client.print_mail.mailing_lists.with_streaming_response.delete( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + mailing_list = response.parse() + assert_matches_type(MailingListDeleteResponse, mailing_list, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_path_params_delete(self, client: PostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.print_mail.mailing_lists.with_raw_response.delete( + "", + ) + + @pytest.mark.skip() + @parametrize + def test_method_jobs(self, client: PostGrid) -> None: + mailing_list = client.print_mail.mailing_lists.jobs( + id="id", + ) + assert_matches_type(MailingList, mailing_list, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_jobs_with_all_params(self, client: PostGrid) -> None: + mailing_list = client.print_mail.mailing_lists.jobs( + id="id", + add_contacts=["string"], + add_mailing_list_imports=["string"], + remove_contacts=["string"], + remove_mailing_list_imports=["mailing_list_import_123", "mailing_list_import_456"], + ) + assert_matches_type(MailingList, mailing_list, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_jobs(self, client: PostGrid) -> None: + response = client.print_mail.mailing_lists.with_raw_response.jobs( + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + mailing_list = response.parse() + assert_matches_type(MailingList, mailing_list, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_jobs(self, client: PostGrid) -> None: + with client.print_mail.mailing_lists.with_streaming_response.jobs( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + mailing_list = response.parse() + assert_matches_type(MailingList, mailing_list, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_path_params_jobs(self, client: PostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.print_mail.mailing_lists.with_raw_response.jobs( + id="", + ) + + +class TestAsyncMailingLists: + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip() + @parametrize + async def test_method_create(self, async_client: AsyncPostGrid) -> None: + mailing_list = await async_client.print_mail.mailing_lists.create() + assert_matches_type(MailingList, mailing_list, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncPostGrid) -> None: + mailing_list = await async_client.print_mail.mailing_lists.create( + description="Test Mailing List", + metadata={"campaign": "bar"}, + idempotency_key="idempotency-key", + ) + assert_matches_type(MailingList, mailing_list, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_create(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.mailing_lists.with_raw_response.create() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + mailing_list = await response.parse() + assert_matches_type(MailingList, mailing_list, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_create(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.mailing_lists.with_streaming_response.create() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + mailing_list = await response.parse() + assert_matches_type(MailingList, mailing_list, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_method_retrieve(self, async_client: AsyncPostGrid) -> None: + mailing_list = await async_client.print_mail.mailing_lists.retrieve( + "id", + ) + assert_matches_type(MailingList, mailing_list, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.mailing_lists.with_raw_response.retrieve( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + mailing_list = await response.parse() + assert_matches_type(MailingList, mailing_list, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.mailing_lists.with_streaming_response.retrieve( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + mailing_list = await response.parse() + assert_matches_type(MailingList, mailing_list, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncPostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.print_mail.mailing_lists.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip() + @parametrize + async def test_method_update(self, async_client: AsyncPostGrid) -> None: + mailing_list = await async_client.print_mail.mailing_lists.update( + id="id", + ) + assert_matches_type(MailingListUpdate, mailing_list, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_update_with_all_params(self, async_client: AsyncPostGrid) -> None: + mailing_list = await async_client.print_mail.mailing_lists.update( + id="id", + description="Updated Mailing List Description", + metadata={"foo": "bar"}, + ) + assert_matches_type(MailingListUpdate, mailing_list, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_update(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.mailing_lists.with_raw_response.update( + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + mailing_list = await response.parse() + assert_matches_type(MailingListUpdate, mailing_list, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_update(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.mailing_lists.with_streaming_response.update( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + mailing_list = await response.parse() + assert_matches_type(MailingListUpdate, mailing_list, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_path_params_update(self, async_client: AsyncPostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.print_mail.mailing_lists.with_raw_response.update( + id="", + ) + + @pytest.mark.skip() + @parametrize + async def test_method_list(self, async_client: AsyncPostGrid) -> None: + mailing_list = await async_client.print_mail.mailing_lists.list() + assert_matches_type(AsyncSkipLimit[MailingList], mailing_list, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncPostGrid) -> None: + mailing_list = await async_client.print_mail.mailing_lists.list( + limit=0, + search="search", + skip=0, + ) + assert_matches_type(AsyncSkipLimit[MailingList], mailing_list, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_list(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.mailing_lists.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + mailing_list = await response.parse() + assert_matches_type(AsyncSkipLimit[MailingList], mailing_list, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_list(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.mailing_lists.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + mailing_list = await response.parse() + assert_matches_type(AsyncSkipLimit[MailingList], mailing_list, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_method_delete(self, async_client: AsyncPostGrid) -> None: + mailing_list = await async_client.print_mail.mailing_lists.delete( + "id", + ) + assert_matches_type(MailingListDeleteResponse, mailing_list, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_delete(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.mailing_lists.with_raw_response.delete( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + mailing_list = await response.parse() + assert_matches_type(MailingListDeleteResponse, mailing_list, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.mailing_lists.with_streaming_response.delete( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + mailing_list = await response.parse() + assert_matches_type(MailingListDeleteResponse, mailing_list, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_path_params_delete(self, async_client: AsyncPostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.print_mail.mailing_lists.with_raw_response.delete( + "", + ) + + @pytest.mark.skip() + @parametrize + async def test_method_jobs(self, async_client: AsyncPostGrid) -> None: + mailing_list = await async_client.print_mail.mailing_lists.jobs( + id="id", + ) + assert_matches_type(MailingList, mailing_list, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_jobs_with_all_params(self, async_client: AsyncPostGrid) -> None: + mailing_list = await async_client.print_mail.mailing_lists.jobs( + id="id", + add_contacts=["string"], + add_mailing_list_imports=["string"], + remove_contacts=["string"], + remove_mailing_list_imports=["mailing_list_import_123", "mailing_list_import_456"], + ) + assert_matches_type(MailingList, mailing_list, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_jobs(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.mailing_lists.with_raw_response.jobs( + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + mailing_list = await response.parse() + assert_matches_type(MailingList, mailing_list, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_jobs(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.mailing_lists.with_streaming_response.jobs( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + mailing_list = await response.parse() + assert_matches_type(MailingList, mailing_list, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_path_params_jobs(self, async_client: AsyncPostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.print_mail.mailing_lists.with_raw_response.jobs( + id="", + ) diff --git a/tests/api_resources/print_mail/test_postcards.py b/tests/api_resources/print_mail/test_postcards.py new file mode 100644 index 0000000..90e4685 --- /dev/null +++ b/tests/api_resources/print_mail/test_postcards.py @@ -0,0 +1,1049 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from postgrid import PostGrid, AsyncPostGrid +from tests.utils import assert_matches_type +from postgrid._utils import parse_datetime +from postgrid.pagination import SyncSkipLimit, AsyncSkipLimit +from postgrid.types.print_mail import ( + Postcard, + PostcardRetrieveURLResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestPostcards: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip() + @parametrize + def test_method_create_overload_1(self, client: PostGrid) -> None: + postcard = client.print_mail.postcards.create( + back_html="backHTML", + front_html="frontHTML", + size="6x4", + to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + ) + assert_matches_type(Postcard, postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_create_with_all_params_overload_1(self, client: PostGrid) -> None: + postcard = client.print_mail.postcards.create( + back_html="backHTML", + front_html="frontHTML", + size="6x4", + to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + "address_line2": "addressLine2", + "city": "city", + "company_name": "companyName", + "description": "description", + "email": "email", + "force_verified_status": True, + "job_title": "jobTitle", + "last_name": "lastName", + "metadata": {"foo": "bar"}, + "phone_number": "phoneNumber", + "postal_or_zip": "postalOrZip", + "province_or_state": "provinceOrState", + "skip_verification": True, + }, + description="description", + from_={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + "address_line2": "addressLine2", + "city": "city", + "company_name": "companyName", + "description": "description", + "email": "email", + "force_verified_status": True, + "job_title": "jobTitle", + "last_name": "lastName", + "metadata": {"foo": "bar"}, + "phone_number": "phoneNumber", + "postal_or_zip": "postalOrZip", + "province_or_state": "provinceOrState", + "skip_verification": True, + }, + mailing_class="first_class", + merge_variables={"foo": "bar"}, + metadata={"foo": "bar"}, + send_date=parse_datetime("2019-12-27T18:11:19.117Z"), + ) + assert_matches_type(Postcard, postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_create_overload_1(self, client: PostGrid) -> None: + response = client.print_mail.postcards.with_raw_response.create( + back_html="backHTML", + front_html="frontHTML", + size="6x4", + to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + postcard = response.parse() + assert_matches_type(Postcard, postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_create_overload_1(self, client: PostGrid) -> None: + with client.print_mail.postcards.with_streaming_response.create( + back_html="backHTML", + front_html="frontHTML", + size="6x4", + to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + postcard = response.parse() + assert_matches_type(Postcard, postcard, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_method_create_overload_2(self, client: PostGrid) -> None: + postcard = client.print_mail.postcards.create( + back_template="backTemplate", + front_template="frontTemplate", + ) + assert_matches_type(Postcard, postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_create_overload_2(self, client: PostGrid) -> None: + response = client.print_mail.postcards.with_raw_response.create( + back_template="backTemplate", + front_template="frontTemplate", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + postcard = response.parse() + assert_matches_type(Postcard, postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_create_overload_2(self, client: PostGrid) -> None: + with client.print_mail.postcards.with_streaming_response.create( + back_template="backTemplate", + front_template="frontTemplate", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + postcard = response.parse() + assert_matches_type(Postcard, postcard, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_method_create_overload_3(self, client: PostGrid) -> None: + postcard = client.print_mail.postcards.create( + pdf="https://example.com", + size="6x4", + to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + ) + assert_matches_type(Postcard, postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_create_with_all_params_overload_3(self, client: PostGrid) -> None: + postcard = client.print_mail.postcards.create( + pdf="https://example.com", + size="6x4", + to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + "address_line2": "addressLine2", + "city": "city", + "company_name": "companyName", + "description": "description", + "email": "email", + "force_verified_status": True, + "job_title": "jobTitle", + "last_name": "lastName", + "metadata": {"foo": "bar"}, + "phone_number": "phoneNumber", + "postal_or_zip": "postalOrZip", + "province_or_state": "provinceOrState", + "skip_verification": True, + }, + description="description", + from_={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + "address_line2": "addressLine2", + "city": "city", + "company_name": "companyName", + "description": "description", + "email": "email", + "force_verified_status": True, + "job_title": "jobTitle", + "last_name": "lastName", + "metadata": {"foo": "bar"}, + "phone_number": "phoneNumber", + "postal_or_zip": "postalOrZip", + "province_or_state": "provinceOrState", + "skip_verification": True, + }, + mailing_class="first_class", + merge_variables={"foo": "bar"}, + metadata={"foo": "bar"}, + send_date=parse_datetime("2019-12-27T18:11:19.117Z"), + ) + assert_matches_type(Postcard, postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_create_overload_3(self, client: PostGrid) -> None: + response = client.print_mail.postcards.with_raw_response.create( + pdf="https://example.com", + size="6x4", + to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + postcard = response.parse() + assert_matches_type(Postcard, postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_create_overload_3(self, client: PostGrid) -> None: + with client.print_mail.postcards.with_streaming_response.create( + pdf="https://example.com", + size="6x4", + to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + postcard = response.parse() + assert_matches_type(Postcard, postcard, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_method_create_overload_4(self, client: PostGrid) -> None: + postcard = client.print_mail.postcards.create( + pdf="U3RhaW5sZXNzIHJvY2tz", + size="6x4", + to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + ) + assert_matches_type(Postcard, postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_create_with_all_params_overload_4(self, client: PostGrid) -> None: + postcard = client.print_mail.postcards.create( + pdf="U3RhaW5sZXNzIHJvY2tz", + size="6x4", + to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + "address_line2": "addressLine2", + "city": "city", + "company_name": "companyName", + "description": "description", + "email": "email", + "force_verified_status": True, + "job_title": "jobTitle", + "last_name": "lastName", + "metadata": {"foo": "bar"}, + "phone_number": "phoneNumber", + "postal_or_zip": "postalOrZip", + "province_or_state": "provinceOrState", + "skip_verification": True, + }, + description="description", + from_={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + "address_line2": "addressLine2", + "city": "city", + "company_name": "companyName", + "description": "description", + "email": "email", + "force_verified_status": True, + "job_title": "jobTitle", + "last_name": "lastName", + "metadata": {"foo": "bar"}, + "phone_number": "phoneNumber", + "postal_or_zip": "postalOrZip", + "province_or_state": "provinceOrState", + "skip_verification": True, + }, + mailing_class="first_class", + merge_variables={"foo": "bar"}, + metadata={"foo": "bar"}, + send_date=parse_datetime("2019-12-27T18:11:19.117Z"), + ) + assert_matches_type(Postcard, postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_create_overload_4(self, client: PostGrid) -> None: + response = client.print_mail.postcards.with_raw_response.create( + pdf="U3RhaW5sZXNzIHJvY2tz", + size="6x4", + to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + postcard = response.parse() + assert_matches_type(Postcard, postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_create_overload_4(self, client: PostGrid) -> None: + with client.print_mail.postcards.with_streaming_response.create( + pdf="U3RhaW5sZXNzIHJvY2tz", + size="6x4", + to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + postcard = response.parse() + assert_matches_type(Postcard, postcard, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_method_retrieve(self, client: PostGrid) -> None: + postcard = client.print_mail.postcards.retrieve( + "id", + ) + assert_matches_type(Postcard, postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_retrieve(self, client: PostGrid) -> None: + response = client.print_mail.postcards.with_raw_response.retrieve( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + postcard = response.parse() + assert_matches_type(Postcard, postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_retrieve(self, client: PostGrid) -> None: + with client.print_mail.postcards.with_streaming_response.retrieve( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + postcard = response.parse() + assert_matches_type(Postcard, postcard, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_path_params_retrieve(self, client: PostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.print_mail.postcards.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip() + @parametrize + def test_method_list(self, client: PostGrid) -> None: + postcard = client.print_mail.postcards.list() + assert_matches_type(SyncSkipLimit[Postcard], postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_list_with_all_params(self, client: PostGrid) -> None: + postcard = client.print_mail.postcards.list( + limit=0, + search="search", + skip=0, + ) + assert_matches_type(SyncSkipLimit[Postcard], postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_list(self, client: PostGrid) -> None: + response = client.print_mail.postcards.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + postcard = response.parse() + assert_matches_type(SyncSkipLimit[Postcard], postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_list(self, client: PostGrid) -> None: + with client.print_mail.postcards.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + postcard = response.parse() + assert_matches_type(SyncSkipLimit[Postcard], postcard, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_method_delete(self, client: PostGrid) -> None: + postcard = client.print_mail.postcards.delete( + "id", + ) + assert_matches_type(Postcard, postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_delete(self, client: PostGrid) -> None: + response = client.print_mail.postcards.with_raw_response.delete( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + postcard = response.parse() + assert_matches_type(Postcard, postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_delete(self, client: PostGrid) -> None: + with client.print_mail.postcards.with_streaming_response.delete( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + postcard = response.parse() + assert_matches_type(Postcard, postcard, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_path_params_delete(self, client: PostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.print_mail.postcards.with_raw_response.delete( + "", + ) + + @pytest.mark.skip() + @parametrize + def test_method_retrieve_url(self, client: PostGrid) -> None: + postcard = client.print_mail.postcards.retrieve_url( + "id", + ) + assert_matches_type(PostcardRetrieveURLResponse, postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_retrieve_url(self, client: PostGrid) -> None: + response = client.print_mail.postcards.with_raw_response.retrieve_url( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + postcard = response.parse() + assert_matches_type(PostcardRetrieveURLResponse, postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_retrieve_url(self, client: PostGrid) -> None: + with client.print_mail.postcards.with_streaming_response.retrieve_url( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + postcard = response.parse() + assert_matches_type(PostcardRetrieveURLResponse, postcard, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_path_params_retrieve_url(self, client: PostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.print_mail.postcards.with_raw_response.retrieve_url( + "", + ) + + +class TestAsyncPostcards: + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip() + @parametrize + async def test_method_create_overload_1(self, async_client: AsyncPostGrid) -> None: + postcard = await async_client.print_mail.postcards.create( + back_html="backHTML", + front_html="frontHTML", + size="6x4", + to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + ) + assert_matches_type(Postcard, postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_create_with_all_params_overload_1(self, async_client: AsyncPostGrid) -> None: + postcard = await async_client.print_mail.postcards.create( + back_html="backHTML", + front_html="frontHTML", + size="6x4", + to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + "address_line2": "addressLine2", + "city": "city", + "company_name": "companyName", + "description": "description", + "email": "email", + "force_verified_status": True, + "job_title": "jobTitle", + "last_name": "lastName", + "metadata": {"foo": "bar"}, + "phone_number": "phoneNumber", + "postal_or_zip": "postalOrZip", + "province_or_state": "provinceOrState", + "skip_verification": True, + }, + description="description", + from_={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + "address_line2": "addressLine2", + "city": "city", + "company_name": "companyName", + "description": "description", + "email": "email", + "force_verified_status": True, + "job_title": "jobTitle", + "last_name": "lastName", + "metadata": {"foo": "bar"}, + "phone_number": "phoneNumber", + "postal_or_zip": "postalOrZip", + "province_or_state": "provinceOrState", + "skip_verification": True, + }, + mailing_class="first_class", + merge_variables={"foo": "bar"}, + metadata={"foo": "bar"}, + send_date=parse_datetime("2019-12-27T18:11:19.117Z"), + ) + assert_matches_type(Postcard, postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_create_overload_1(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.postcards.with_raw_response.create( + back_html="backHTML", + front_html="frontHTML", + size="6x4", + to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + postcard = await response.parse() + assert_matches_type(Postcard, postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_create_overload_1(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.postcards.with_streaming_response.create( + back_html="backHTML", + front_html="frontHTML", + size="6x4", + to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + postcard = await response.parse() + assert_matches_type(Postcard, postcard, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_method_create_overload_2(self, async_client: AsyncPostGrid) -> None: + postcard = await async_client.print_mail.postcards.create( + back_template="backTemplate", + front_template="frontTemplate", + ) + assert_matches_type(Postcard, postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_create_overload_2(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.postcards.with_raw_response.create( + back_template="backTemplate", + front_template="frontTemplate", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + postcard = await response.parse() + assert_matches_type(Postcard, postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_create_overload_2(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.postcards.with_streaming_response.create( + back_template="backTemplate", + front_template="frontTemplate", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + postcard = await response.parse() + assert_matches_type(Postcard, postcard, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_method_create_overload_3(self, async_client: AsyncPostGrid) -> None: + postcard = await async_client.print_mail.postcards.create( + pdf="https://example.com", + size="6x4", + to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + ) + assert_matches_type(Postcard, postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_create_with_all_params_overload_3(self, async_client: AsyncPostGrid) -> None: + postcard = await async_client.print_mail.postcards.create( + pdf="https://example.com", + size="6x4", + to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + "address_line2": "addressLine2", + "city": "city", + "company_name": "companyName", + "description": "description", + "email": "email", + "force_verified_status": True, + "job_title": "jobTitle", + "last_name": "lastName", + "metadata": {"foo": "bar"}, + "phone_number": "phoneNumber", + "postal_or_zip": "postalOrZip", + "province_or_state": "provinceOrState", + "skip_verification": True, + }, + description="description", + from_={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + "address_line2": "addressLine2", + "city": "city", + "company_name": "companyName", + "description": "description", + "email": "email", + "force_verified_status": True, + "job_title": "jobTitle", + "last_name": "lastName", + "metadata": {"foo": "bar"}, + "phone_number": "phoneNumber", + "postal_or_zip": "postalOrZip", + "province_or_state": "provinceOrState", + "skip_verification": True, + }, + mailing_class="first_class", + merge_variables={"foo": "bar"}, + metadata={"foo": "bar"}, + send_date=parse_datetime("2019-12-27T18:11:19.117Z"), + ) + assert_matches_type(Postcard, postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_create_overload_3(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.postcards.with_raw_response.create( + pdf="https://example.com", + size="6x4", + to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + postcard = await response.parse() + assert_matches_type(Postcard, postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_create_overload_3(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.postcards.with_streaming_response.create( + pdf="https://example.com", + size="6x4", + to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + postcard = await response.parse() + assert_matches_type(Postcard, postcard, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_method_create_overload_4(self, async_client: AsyncPostGrid) -> None: + postcard = await async_client.print_mail.postcards.create( + pdf="U3RhaW5sZXNzIHJvY2tz", + size="6x4", + to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + ) + assert_matches_type(Postcard, postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_create_with_all_params_overload_4(self, async_client: AsyncPostGrid) -> None: + postcard = await async_client.print_mail.postcards.create( + pdf="U3RhaW5sZXNzIHJvY2tz", + size="6x4", + to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + "address_line2": "addressLine2", + "city": "city", + "company_name": "companyName", + "description": "description", + "email": "email", + "force_verified_status": True, + "job_title": "jobTitle", + "last_name": "lastName", + "metadata": {"foo": "bar"}, + "phone_number": "phoneNumber", + "postal_or_zip": "postalOrZip", + "province_or_state": "provinceOrState", + "skip_verification": True, + }, + description="description", + from_={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + "address_line2": "addressLine2", + "city": "city", + "company_name": "companyName", + "description": "description", + "email": "email", + "force_verified_status": True, + "job_title": "jobTitle", + "last_name": "lastName", + "metadata": {"foo": "bar"}, + "phone_number": "phoneNumber", + "postal_or_zip": "postalOrZip", + "province_or_state": "provinceOrState", + "skip_verification": True, + }, + mailing_class="first_class", + merge_variables={"foo": "bar"}, + metadata={"foo": "bar"}, + send_date=parse_datetime("2019-12-27T18:11:19.117Z"), + ) + assert_matches_type(Postcard, postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_create_overload_4(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.postcards.with_raw_response.create( + pdf="U3RhaW5sZXNzIHJvY2tz", + size="6x4", + to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + postcard = await response.parse() + assert_matches_type(Postcard, postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_create_overload_4(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.postcards.with_streaming_response.create( + pdf="U3RhaW5sZXNzIHJvY2tz", + size="6x4", + to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + postcard = await response.parse() + assert_matches_type(Postcard, postcard, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_method_retrieve(self, async_client: AsyncPostGrid) -> None: + postcard = await async_client.print_mail.postcards.retrieve( + "id", + ) + assert_matches_type(Postcard, postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.postcards.with_raw_response.retrieve( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + postcard = await response.parse() + assert_matches_type(Postcard, postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.postcards.with_streaming_response.retrieve( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + postcard = await response.parse() + assert_matches_type(Postcard, postcard, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncPostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.print_mail.postcards.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip() + @parametrize + async def test_method_list(self, async_client: AsyncPostGrid) -> None: + postcard = await async_client.print_mail.postcards.list() + assert_matches_type(AsyncSkipLimit[Postcard], postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncPostGrid) -> None: + postcard = await async_client.print_mail.postcards.list( + limit=0, + search="search", + skip=0, + ) + assert_matches_type(AsyncSkipLimit[Postcard], postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_list(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.postcards.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + postcard = await response.parse() + assert_matches_type(AsyncSkipLimit[Postcard], postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_list(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.postcards.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + postcard = await response.parse() + assert_matches_type(AsyncSkipLimit[Postcard], postcard, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_method_delete(self, async_client: AsyncPostGrid) -> None: + postcard = await async_client.print_mail.postcards.delete( + "id", + ) + assert_matches_type(Postcard, postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_delete(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.postcards.with_raw_response.delete( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + postcard = await response.parse() + assert_matches_type(Postcard, postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.postcards.with_streaming_response.delete( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + postcard = await response.parse() + assert_matches_type(Postcard, postcard, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_path_params_delete(self, async_client: AsyncPostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.print_mail.postcards.with_raw_response.delete( + "", + ) + + @pytest.mark.skip() + @parametrize + async def test_method_retrieve_url(self, async_client: AsyncPostGrid) -> None: + postcard = await async_client.print_mail.postcards.retrieve_url( + "id", + ) + assert_matches_type(PostcardRetrieveURLResponse, postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_retrieve_url(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.postcards.with_raw_response.retrieve_url( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + postcard = await response.parse() + assert_matches_type(PostcardRetrieveURLResponse, postcard, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_retrieve_url(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.postcards.with_streaming_response.retrieve_url( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + postcard = await response.parse() + assert_matches_type(PostcardRetrieveURLResponse, postcard, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_path_params_retrieve_url(self, async_client: AsyncPostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.print_mail.postcards.with_raw_response.retrieve_url( + "", + ) diff --git a/tests/api_resources/print_mail/test_reports.py b/tests/api_resources/print_mail/test_reports.py new file mode 100644 index 0000000..5b6e854 --- /dev/null +++ b/tests/api_resources/print_mail/test_reports.py @@ -0,0 +1,553 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from postgrid import PostGrid, AsyncPostGrid +from tests.utils import assert_matches_type +from postgrid.pagination import SyncSkipLimit, AsyncSkipLimit +from postgrid.types.print_mail import ( + Report, + DeletedResponse, +) +from postgrid.types.print_mail.reports import ReportSample + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestReports: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip() + @parametrize + def test_method_create(self, client: PostGrid) -> None: + report = client.print_mail.reports.create( + sql_query="SELECT id, status FROM orders WHERE created_at > ?", + ) + assert_matches_type(Report, report, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_create_with_all_params(self, client: PostGrid) -> None: + report = client.print_mail.reports.create( + sql_query="SELECT id, status FROM orders WHERE created_at > ?", + description="Recent Orders", + metadata={"team": "Sales"}, + ) + assert_matches_type(Report, report, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_create(self, client: PostGrid) -> None: + response = client.print_mail.reports.with_raw_response.create( + sql_query="SELECT id, status FROM orders WHERE created_at > ?", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + report = response.parse() + assert_matches_type(Report, report, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_create(self, client: PostGrid) -> None: + with client.print_mail.reports.with_streaming_response.create( + sql_query="SELECT id, status FROM orders WHERE created_at > ?", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + report = response.parse() + assert_matches_type(Report, report, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_method_retrieve(self, client: PostGrid) -> None: + report = client.print_mail.reports.retrieve( + "id", + ) + assert_matches_type(Report, report, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_retrieve(self, client: PostGrid) -> None: + response = client.print_mail.reports.with_raw_response.retrieve( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + report = response.parse() + assert_matches_type(Report, report, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_retrieve(self, client: PostGrid) -> None: + with client.print_mail.reports.with_streaming_response.retrieve( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + report = response.parse() + assert_matches_type(Report, report, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_path_params_retrieve(self, client: PostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.print_mail.reports.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip() + @parametrize + def test_method_update(self, client: PostGrid) -> None: + report = client.print_mail.reports.update( + id="id", + ) + assert_matches_type(Report, report, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_update_with_all_params(self, client: PostGrid) -> None: + report = client.print_mail.reports.update( + id="id", + description="Recent Orders (Updated)", + metadata={"foo": "string"}, + sql_query="sqlQuery", + ) + assert_matches_type(Report, report, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_update(self, client: PostGrid) -> None: + response = client.print_mail.reports.with_raw_response.update( + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + report = response.parse() + assert_matches_type(Report, report, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_update(self, client: PostGrid) -> None: + with client.print_mail.reports.with_streaming_response.update( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + report = response.parse() + assert_matches_type(Report, report, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_path_params_update(self, client: PostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.print_mail.reports.with_raw_response.update( + id="", + ) + + @pytest.mark.skip() + @parametrize + def test_method_list(self, client: PostGrid) -> None: + report = client.print_mail.reports.list() + assert_matches_type(SyncSkipLimit[Report], report, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_list_with_all_params(self, client: PostGrid) -> None: + report = client.print_mail.reports.list( + limit=0, + search="search", + skip=0, + ) + assert_matches_type(SyncSkipLimit[Report], report, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_list(self, client: PostGrid) -> None: + response = client.print_mail.reports.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + report = response.parse() + assert_matches_type(SyncSkipLimit[Report], report, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_list(self, client: PostGrid) -> None: + with client.print_mail.reports.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + report = response.parse() + assert_matches_type(SyncSkipLimit[Report], report, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_method_delete(self, client: PostGrid) -> None: + report = client.print_mail.reports.delete( + "id", + ) + assert_matches_type(DeletedResponse, report, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_delete(self, client: PostGrid) -> None: + response = client.print_mail.reports.with_raw_response.delete( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + report = response.parse() + assert_matches_type(DeletedResponse, report, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_delete(self, client: PostGrid) -> None: + with client.print_mail.reports.with_streaming_response.delete( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + report = response.parse() + assert_matches_type(DeletedResponse, report, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_path_params_delete(self, client: PostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.print_mail.reports.with_raw_response.delete( + "", + ) + + @pytest.mark.skip() + @parametrize + def test_method_sample(self, client: PostGrid) -> None: + report = client.print_mail.reports.sample( + sql_query="sqlQuery", + ) + assert_matches_type(ReportSample, report, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_sample_with_all_params(self, client: PostGrid) -> None: + report = client.print_mail.reports.sample( + sql_query="sqlQuery", + limit=1000, + params=["string"], + ) + assert_matches_type(ReportSample, report, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_sample(self, client: PostGrid) -> None: + response = client.print_mail.reports.with_raw_response.sample( + sql_query="sqlQuery", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + report = response.parse() + assert_matches_type(ReportSample, report, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_sample(self, client: PostGrid) -> None: + with client.print_mail.reports.with_streaming_response.sample( + sql_query="sqlQuery", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + report = response.parse() + assert_matches_type(ReportSample, report, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncReports: + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip() + @parametrize + async def test_method_create(self, async_client: AsyncPostGrid) -> None: + report = await async_client.print_mail.reports.create( + sql_query="SELECT id, status FROM orders WHERE created_at > ?", + ) + assert_matches_type(Report, report, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncPostGrid) -> None: + report = await async_client.print_mail.reports.create( + sql_query="SELECT id, status FROM orders WHERE created_at > ?", + description="Recent Orders", + metadata={"team": "Sales"}, + ) + assert_matches_type(Report, report, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_create(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.reports.with_raw_response.create( + sql_query="SELECT id, status FROM orders WHERE created_at > ?", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + report = await response.parse() + assert_matches_type(Report, report, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_create(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.reports.with_streaming_response.create( + sql_query="SELECT id, status FROM orders WHERE created_at > ?", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + report = await response.parse() + assert_matches_type(Report, report, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_method_retrieve(self, async_client: AsyncPostGrid) -> None: + report = await async_client.print_mail.reports.retrieve( + "id", + ) + assert_matches_type(Report, report, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.reports.with_raw_response.retrieve( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + report = await response.parse() + assert_matches_type(Report, report, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.reports.with_streaming_response.retrieve( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + report = await response.parse() + assert_matches_type(Report, report, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncPostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.print_mail.reports.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip() + @parametrize + async def test_method_update(self, async_client: AsyncPostGrid) -> None: + report = await async_client.print_mail.reports.update( + id="id", + ) + assert_matches_type(Report, report, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_update_with_all_params(self, async_client: AsyncPostGrid) -> None: + report = await async_client.print_mail.reports.update( + id="id", + description="Recent Orders (Updated)", + metadata={"foo": "string"}, + sql_query="sqlQuery", + ) + assert_matches_type(Report, report, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_update(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.reports.with_raw_response.update( + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + report = await response.parse() + assert_matches_type(Report, report, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_update(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.reports.with_streaming_response.update( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + report = await response.parse() + assert_matches_type(Report, report, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_path_params_update(self, async_client: AsyncPostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.print_mail.reports.with_raw_response.update( + id="", + ) + + @pytest.mark.skip() + @parametrize + async def test_method_list(self, async_client: AsyncPostGrid) -> None: + report = await async_client.print_mail.reports.list() + assert_matches_type(AsyncSkipLimit[Report], report, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncPostGrid) -> None: + report = await async_client.print_mail.reports.list( + limit=0, + search="search", + skip=0, + ) + assert_matches_type(AsyncSkipLimit[Report], report, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_list(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.reports.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + report = await response.parse() + assert_matches_type(AsyncSkipLimit[Report], report, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_list(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.reports.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + report = await response.parse() + assert_matches_type(AsyncSkipLimit[Report], report, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_method_delete(self, async_client: AsyncPostGrid) -> None: + report = await async_client.print_mail.reports.delete( + "id", + ) + assert_matches_type(DeletedResponse, report, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_delete(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.reports.with_raw_response.delete( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + report = await response.parse() + assert_matches_type(DeletedResponse, report, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.reports.with_streaming_response.delete( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + report = await response.parse() + assert_matches_type(DeletedResponse, report, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_path_params_delete(self, async_client: AsyncPostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.print_mail.reports.with_raw_response.delete( + "", + ) + + @pytest.mark.skip() + @parametrize + async def test_method_sample(self, async_client: AsyncPostGrid) -> None: + report = await async_client.print_mail.reports.sample( + sql_query="sqlQuery", + ) + assert_matches_type(ReportSample, report, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_sample_with_all_params(self, async_client: AsyncPostGrid) -> None: + report = await async_client.print_mail.reports.sample( + sql_query="sqlQuery", + limit=1000, + params=["string"], + ) + assert_matches_type(ReportSample, report, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_sample(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.reports.with_raw_response.sample( + sql_query="sqlQuery", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + report = await response.parse() + assert_matches_type(ReportSample, report, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_sample(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.reports.with_streaming_response.sample( + sql_query="sqlQuery", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + report = await response.parse() + assert_matches_type(ReportSample, report, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/print_mail/test_self_mailers.py b/tests/api_resources/print_mail/test_self_mailers.py new file mode 100644 index 0000000..cf4dfd9 --- /dev/null +++ b/tests/api_resources/print_mail/test_self_mailers.py @@ -0,0 +1,1139 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from postgrid import PostGrid, AsyncPostGrid +from tests.utils import assert_matches_type +from postgrid._utils import parse_datetime +from postgrid.pagination import SyncSkipLimit, AsyncSkipLimit +from postgrid.types.print_mail import ( + SelfMailer, + SelfMailerRetrieveURLResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestSelfMailers: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip() + @parametrize + def test_method_create_overload_1(self, client: PostGrid) -> None: + self_mailer = client.print_mail.self_mailers.create( + from_={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + inside_html="insideHTML", + outside_html="outsideHTML", + size="8.5x11_bifold", + to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + ) + assert_matches_type(SelfMailer, self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_create_with_all_params_overload_1(self, client: PostGrid) -> None: + self_mailer = client.print_mail.self_mailers.create( + from_={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + "address_line2": "addressLine2", + "city": "city", + "company_name": "companyName", + "description": "description", + "email": "email", + "force_verified_status": True, + "job_title": "jobTitle", + "last_name": "lastName", + "metadata": {"foo": "bar"}, + "phone_number": "phoneNumber", + "postal_or_zip": "postalOrZip", + "province_or_state": "provinceOrState", + "skip_verification": True, + }, + inside_html="insideHTML", + outside_html="outsideHTML", + size="8.5x11_bifold", + to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + "address_line2": "addressLine2", + "city": "city", + "company_name": "companyName", + "description": "description", + "email": "email", + "force_verified_status": True, + "job_title": "jobTitle", + "last_name": "lastName", + "metadata": {"foo": "bar"}, + "phone_number": "phoneNumber", + "postal_or_zip": "postalOrZip", + "province_or_state": "provinceOrState", + "skip_verification": True, + }, + description="description", + mailing_class="first_class", + merge_variables={"foo": "bar"}, + metadata={"foo": "bar"}, + send_date=parse_datetime("2019-12-27T18:11:19.117Z"), + ) + assert_matches_type(SelfMailer, self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_create_overload_1(self, client: PostGrid) -> None: + response = client.print_mail.self_mailers.with_raw_response.create( + from_={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + inside_html="insideHTML", + outside_html="outsideHTML", + size="8.5x11_bifold", + to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + self_mailer = response.parse() + assert_matches_type(SelfMailer, self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_create_overload_1(self, client: PostGrid) -> None: + with client.print_mail.self_mailers.with_streaming_response.create( + from_={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + inside_html="insideHTML", + outside_html="outsideHTML", + size="8.5x11_bifold", + to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + self_mailer = response.parse() + assert_matches_type(SelfMailer, self_mailer, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_method_create_overload_2(self, client: PostGrid) -> None: + self_mailer = client.print_mail.self_mailers.create( + inside_template="insideTemplate", + outside_template="outsideTemplate", + ) + assert_matches_type(SelfMailer, self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_create_overload_2(self, client: PostGrid) -> None: + response = client.print_mail.self_mailers.with_raw_response.create( + inside_template="insideTemplate", + outside_template="outsideTemplate", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + self_mailer = response.parse() + assert_matches_type(SelfMailer, self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_create_overload_2(self, client: PostGrid) -> None: + with client.print_mail.self_mailers.with_streaming_response.create( + inside_template="insideTemplate", + outside_template="outsideTemplate", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + self_mailer = response.parse() + assert_matches_type(SelfMailer, self_mailer, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_method_create_overload_3(self, client: PostGrid) -> None: + self_mailer = client.print_mail.self_mailers.create( + from_={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + pdf="https://example.com", + size="8.5x11_bifold", + to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + ) + assert_matches_type(SelfMailer, self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_create_with_all_params_overload_3(self, client: PostGrid) -> None: + self_mailer = client.print_mail.self_mailers.create( + from_={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + "address_line2": "addressLine2", + "city": "city", + "company_name": "companyName", + "description": "description", + "email": "email", + "force_verified_status": True, + "job_title": "jobTitle", + "last_name": "lastName", + "metadata": {"foo": "bar"}, + "phone_number": "phoneNumber", + "postal_or_zip": "postalOrZip", + "province_or_state": "provinceOrState", + "skip_verification": True, + }, + pdf="https://example.com", + size="8.5x11_bifold", + to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + "address_line2": "addressLine2", + "city": "city", + "company_name": "companyName", + "description": "description", + "email": "email", + "force_verified_status": True, + "job_title": "jobTitle", + "last_name": "lastName", + "metadata": {"foo": "bar"}, + "phone_number": "phoneNumber", + "postal_or_zip": "postalOrZip", + "province_or_state": "provinceOrState", + "skip_verification": True, + }, + description="description", + mailing_class="first_class", + merge_variables={"foo": "bar"}, + metadata={"foo": "bar"}, + send_date=parse_datetime("2019-12-27T18:11:19.117Z"), + ) + assert_matches_type(SelfMailer, self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_create_overload_3(self, client: PostGrid) -> None: + response = client.print_mail.self_mailers.with_raw_response.create( + from_={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + pdf="https://example.com", + size="8.5x11_bifold", + to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + self_mailer = response.parse() + assert_matches_type(SelfMailer, self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_create_overload_3(self, client: PostGrid) -> None: + with client.print_mail.self_mailers.with_streaming_response.create( + from_={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + pdf="https://example.com", + size="8.5x11_bifold", + to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + self_mailer = response.parse() + assert_matches_type(SelfMailer, self_mailer, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_method_create_overload_4(self, client: PostGrid) -> None: + self_mailer = client.print_mail.self_mailers.create( + from_={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + pdf="U3RhaW5sZXNzIHJvY2tz", + size="8.5x11_bifold", + to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + ) + assert_matches_type(SelfMailer, self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_create_with_all_params_overload_4(self, client: PostGrid) -> None: + self_mailer = client.print_mail.self_mailers.create( + from_={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + "address_line2": "addressLine2", + "city": "city", + "company_name": "companyName", + "description": "description", + "email": "email", + "force_verified_status": True, + "job_title": "jobTitle", + "last_name": "lastName", + "metadata": {"foo": "bar"}, + "phone_number": "phoneNumber", + "postal_or_zip": "postalOrZip", + "province_or_state": "provinceOrState", + "skip_verification": True, + }, + pdf="U3RhaW5sZXNzIHJvY2tz", + size="8.5x11_bifold", + to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + "address_line2": "addressLine2", + "city": "city", + "company_name": "companyName", + "description": "description", + "email": "email", + "force_verified_status": True, + "job_title": "jobTitle", + "last_name": "lastName", + "metadata": {"foo": "bar"}, + "phone_number": "phoneNumber", + "postal_or_zip": "postalOrZip", + "province_or_state": "provinceOrState", + "skip_verification": True, + }, + description="description", + mailing_class="first_class", + merge_variables={"foo": "bar"}, + metadata={"foo": "bar"}, + send_date=parse_datetime("2019-12-27T18:11:19.117Z"), + ) + assert_matches_type(SelfMailer, self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_create_overload_4(self, client: PostGrid) -> None: + response = client.print_mail.self_mailers.with_raw_response.create( + from_={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + pdf="U3RhaW5sZXNzIHJvY2tz", + size="8.5x11_bifold", + to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + self_mailer = response.parse() + assert_matches_type(SelfMailer, self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_create_overload_4(self, client: PostGrid) -> None: + with client.print_mail.self_mailers.with_streaming_response.create( + from_={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + pdf="U3RhaW5sZXNzIHJvY2tz", + size="8.5x11_bifold", + to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + self_mailer = response.parse() + assert_matches_type(SelfMailer, self_mailer, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_method_retrieve(self, client: PostGrid) -> None: + self_mailer = client.print_mail.self_mailers.retrieve( + "id", + ) + assert_matches_type(SelfMailer, self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_retrieve(self, client: PostGrid) -> None: + response = client.print_mail.self_mailers.with_raw_response.retrieve( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + self_mailer = response.parse() + assert_matches_type(SelfMailer, self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_retrieve(self, client: PostGrid) -> None: + with client.print_mail.self_mailers.with_streaming_response.retrieve( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + self_mailer = response.parse() + assert_matches_type(SelfMailer, self_mailer, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_path_params_retrieve(self, client: PostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.print_mail.self_mailers.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip() + @parametrize + def test_method_list(self, client: PostGrid) -> None: + self_mailer = client.print_mail.self_mailers.list() + assert_matches_type(SyncSkipLimit[SelfMailer], self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_list_with_all_params(self, client: PostGrid) -> None: + self_mailer = client.print_mail.self_mailers.list( + limit=0, + search="search", + skip=0, + ) + assert_matches_type(SyncSkipLimit[SelfMailer], self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_list(self, client: PostGrid) -> None: + response = client.print_mail.self_mailers.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + self_mailer = response.parse() + assert_matches_type(SyncSkipLimit[SelfMailer], self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_list(self, client: PostGrid) -> None: + with client.print_mail.self_mailers.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + self_mailer = response.parse() + assert_matches_type(SyncSkipLimit[SelfMailer], self_mailer, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_method_delete(self, client: PostGrid) -> None: + self_mailer = client.print_mail.self_mailers.delete( + "id", + ) + assert_matches_type(SelfMailer, self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_delete(self, client: PostGrid) -> None: + response = client.print_mail.self_mailers.with_raw_response.delete( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + self_mailer = response.parse() + assert_matches_type(SelfMailer, self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_delete(self, client: PostGrid) -> None: + with client.print_mail.self_mailers.with_streaming_response.delete( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + self_mailer = response.parse() + assert_matches_type(SelfMailer, self_mailer, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_path_params_delete(self, client: PostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.print_mail.self_mailers.with_raw_response.delete( + "", + ) + + @pytest.mark.skip() + @parametrize + def test_method_retrieve_url(self, client: PostGrid) -> None: + self_mailer = client.print_mail.self_mailers.retrieve_url( + "id", + ) + assert_matches_type(SelfMailerRetrieveURLResponse, self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_retrieve_url(self, client: PostGrid) -> None: + response = client.print_mail.self_mailers.with_raw_response.retrieve_url( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + self_mailer = response.parse() + assert_matches_type(SelfMailerRetrieveURLResponse, self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_retrieve_url(self, client: PostGrid) -> None: + with client.print_mail.self_mailers.with_streaming_response.retrieve_url( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + self_mailer = response.parse() + assert_matches_type(SelfMailerRetrieveURLResponse, self_mailer, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_path_params_retrieve_url(self, client: PostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.print_mail.self_mailers.with_raw_response.retrieve_url( + "", + ) + + +class TestAsyncSelfMailers: + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip() + @parametrize + async def test_method_create_overload_1(self, async_client: AsyncPostGrid) -> None: + self_mailer = await async_client.print_mail.self_mailers.create( + from_={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + inside_html="insideHTML", + outside_html="outsideHTML", + size="8.5x11_bifold", + to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + ) + assert_matches_type(SelfMailer, self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_create_with_all_params_overload_1(self, async_client: AsyncPostGrid) -> None: + self_mailer = await async_client.print_mail.self_mailers.create( + from_={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + "address_line2": "addressLine2", + "city": "city", + "company_name": "companyName", + "description": "description", + "email": "email", + "force_verified_status": True, + "job_title": "jobTitle", + "last_name": "lastName", + "metadata": {"foo": "bar"}, + "phone_number": "phoneNumber", + "postal_or_zip": "postalOrZip", + "province_or_state": "provinceOrState", + "skip_verification": True, + }, + inside_html="insideHTML", + outside_html="outsideHTML", + size="8.5x11_bifold", + to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + "address_line2": "addressLine2", + "city": "city", + "company_name": "companyName", + "description": "description", + "email": "email", + "force_verified_status": True, + "job_title": "jobTitle", + "last_name": "lastName", + "metadata": {"foo": "bar"}, + "phone_number": "phoneNumber", + "postal_or_zip": "postalOrZip", + "province_or_state": "provinceOrState", + "skip_verification": True, + }, + description="description", + mailing_class="first_class", + merge_variables={"foo": "bar"}, + metadata={"foo": "bar"}, + send_date=parse_datetime("2019-12-27T18:11:19.117Z"), + ) + assert_matches_type(SelfMailer, self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_create_overload_1(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.self_mailers.with_raw_response.create( + from_={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + inside_html="insideHTML", + outside_html="outsideHTML", + size="8.5x11_bifold", + to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + self_mailer = await response.parse() + assert_matches_type(SelfMailer, self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_create_overload_1(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.self_mailers.with_streaming_response.create( + from_={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + inside_html="insideHTML", + outside_html="outsideHTML", + size="8.5x11_bifold", + to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + self_mailer = await response.parse() + assert_matches_type(SelfMailer, self_mailer, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_method_create_overload_2(self, async_client: AsyncPostGrid) -> None: + self_mailer = await async_client.print_mail.self_mailers.create( + inside_template="insideTemplate", + outside_template="outsideTemplate", + ) + assert_matches_type(SelfMailer, self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_create_overload_2(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.self_mailers.with_raw_response.create( + inside_template="insideTemplate", + outside_template="outsideTemplate", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + self_mailer = await response.parse() + assert_matches_type(SelfMailer, self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_create_overload_2(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.self_mailers.with_streaming_response.create( + inside_template="insideTemplate", + outside_template="outsideTemplate", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + self_mailer = await response.parse() + assert_matches_type(SelfMailer, self_mailer, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_method_create_overload_3(self, async_client: AsyncPostGrid) -> None: + self_mailer = await async_client.print_mail.self_mailers.create( + from_={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + pdf="https://example.com", + size="8.5x11_bifold", + to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + ) + assert_matches_type(SelfMailer, self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_create_with_all_params_overload_3(self, async_client: AsyncPostGrid) -> None: + self_mailer = await async_client.print_mail.self_mailers.create( + from_={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + "address_line2": "addressLine2", + "city": "city", + "company_name": "companyName", + "description": "description", + "email": "email", + "force_verified_status": True, + "job_title": "jobTitle", + "last_name": "lastName", + "metadata": {"foo": "bar"}, + "phone_number": "phoneNumber", + "postal_or_zip": "postalOrZip", + "province_or_state": "provinceOrState", + "skip_verification": True, + }, + pdf="https://example.com", + size="8.5x11_bifold", + to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + "address_line2": "addressLine2", + "city": "city", + "company_name": "companyName", + "description": "description", + "email": "email", + "force_verified_status": True, + "job_title": "jobTitle", + "last_name": "lastName", + "metadata": {"foo": "bar"}, + "phone_number": "phoneNumber", + "postal_or_zip": "postalOrZip", + "province_or_state": "provinceOrState", + "skip_verification": True, + }, + description="description", + mailing_class="first_class", + merge_variables={"foo": "bar"}, + metadata={"foo": "bar"}, + send_date=parse_datetime("2019-12-27T18:11:19.117Z"), + ) + assert_matches_type(SelfMailer, self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_create_overload_3(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.self_mailers.with_raw_response.create( + from_={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + pdf="https://example.com", + size="8.5x11_bifold", + to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + self_mailer = await response.parse() + assert_matches_type(SelfMailer, self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_create_overload_3(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.self_mailers.with_streaming_response.create( + from_={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + pdf="https://example.com", + size="8.5x11_bifold", + to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + self_mailer = await response.parse() + assert_matches_type(SelfMailer, self_mailer, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_method_create_overload_4(self, async_client: AsyncPostGrid) -> None: + self_mailer = await async_client.print_mail.self_mailers.create( + from_={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + pdf="U3RhaW5sZXNzIHJvY2tz", + size="8.5x11_bifold", + to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + ) + assert_matches_type(SelfMailer, self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_create_with_all_params_overload_4(self, async_client: AsyncPostGrid) -> None: + self_mailer = await async_client.print_mail.self_mailers.create( + from_={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + "address_line2": "addressLine2", + "city": "city", + "company_name": "companyName", + "description": "description", + "email": "email", + "force_verified_status": True, + "job_title": "jobTitle", + "last_name": "lastName", + "metadata": {"foo": "bar"}, + "phone_number": "phoneNumber", + "postal_or_zip": "postalOrZip", + "province_or_state": "provinceOrState", + "skip_verification": True, + }, + pdf="U3RhaW5sZXNzIHJvY2tz", + size="8.5x11_bifold", + to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + "address_line2": "addressLine2", + "city": "city", + "company_name": "companyName", + "description": "description", + "email": "email", + "force_verified_status": True, + "job_title": "jobTitle", + "last_name": "lastName", + "metadata": {"foo": "bar"}, + "phone_number": "phoneNumber", + "postal_or_zip": "postalOrZip", + "province_or_state": "provinceOrState", + "skip_verification": True, + }, + description="description", + mailing_class="first_class", + merge_variables={"foo": "bar"}, + metadata={"foo": "bar"}, + send_date=parse_datetime("2019-12-27T18:11:19.117Z"), + ) + assert_matches_type(SelfMailer, self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_create_overload_4(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.self_mailers.with_raw_response.create( + from_={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + pdf="U3RhaW5sZXNzIHJvY2tz", + size="8.5x11_bifold", + to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + self_mailer = await response.parse() + assert_matches_type(SelfMailer, self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_create_overload_4(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.self_mailers.with_streaming_response.create( + from_={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + pdf="U3RhaW5sZXNzIHJvY2tz", + size="8.5x11_bifold", + to={ + "address_line1": "addressLine1", + "country_code": "countryCode", + "first_name": "firstName", + }, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + self_mailer = await response.parse() + assert_matches_type(SelfMailer, self_mailer, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_method_retrieve(self, async_client: AsyncPostGrid) -> None: + self_mailer = await async_client.print_mail.self_mailers.retrieve( + "id", + ) + assert_matches_type(SelfMailer, self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.self_mailers.with_raw_response.retrieve( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + self_mailer = await response.parse() + assert_matches_type(SelfMailer, self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.self_mailers.with_streaming_response.retrieve( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + self_mailer = await response.parse() + assert_matches_type(SelfMailer, self_mailer, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncPostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.print_mail.self_mailers.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip() + @parametrize + async def test_method_list(self, async_client: AsyncPostGrid) -> None: + self_mailer = await async_client.print_mail.self_mailers.list() + assert_matches_type(AsyncSkipLimit[SelfMailer], self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncPostGrid) -> None: + self_mailer = await async_client.print_mail.self_mailers.list( + limit=0, + search="search", + skip=0, + ) + assert_matches_type(AsyncSkipLimit[SelfMailer], self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_list(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.self_mailers.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + self_mailer = await response.parse() + assert_matches_type(AsyncSkipLimit[SelfMailer], self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_list(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.self_mailers.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + self_mailer = await response.parse() + assert_matches_type(AsyncSkipLimit[SelfMailer], self_mailer, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_method_delete(self, async_client: AsyncPostGrid) -> None: + self_mailer = await async_client.print_mail.self_mailers.delete( + "id", + ) + assert_matches_type(SelfMailer, self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_delete(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.self_mailers.with_raw_response.delete( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + self_mailer = await response.parse() + assert_matches_type(SelfMailer, self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.self_mailers.with_streaming_response.delete( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + self_mailer = await response.parse() + assert_matches_type(SelfMailer, self_mailer, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_path_params_delete(self, async_client: AsyncPostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.print_mail.self_mailers.with_raw_response.delete( + "", + ) + + @pytest.mark.skip() + @parametrize + async def test_method_retrieve_url(self, async_client: AsyncPostGrid) -> None: + self_mailer = await async_client.print_mail.self_mailers.retrieve_url( + "id", + ) + assert_matches_type(SelfMailerRetrieveURLResponse, self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_retrieve_url(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.self_mailers.with_raw_response.retrieve_url( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + self_mailer = await response.parse() + assert_matches_type(SelfMailerRetrieveURLResponse, self_mailer, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_retrieve_url(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.self_mailers.with_streaming_response.retrieve_url( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + self_mailer = await response.parse() + assert_matches_type(SelfMailerRetrieveURLResponse, self_mailer, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_path_params_retrieve_url(self, async_client: AsyncPostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.print_mail.self_mailers.with_raw_response.retrieve_url( + "", + ) diff --git a/tests/api_resources/print_mail/test_sub_organizations.py b/tests/api_resources/print_mail/test_sub_organizations.py new file mode 100644 index 0000000..7a90184 --- /dev/null +++ b/tests/api_resources/print_mail/test_sub_organizations.py @@ -0,0 +1,411 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from postgrid import PostGrid, AsyncPostGrid +from tests.utils import assert_matches_type +from postgrid.pagination import SyncSkipLimit, AsyncSkipLimit +from postgrid.types.print_mail import ( + SubOrganization, + SubOrganizationUpdateResponse, + SubOrganizationRetrieveUsersResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestSubOrganizations: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip() + @parametrize + def test_method_retrieve(self, client: PostGrid) -> None: + sub_organization = client.print_mail.sub_organizations.retrieve( + "id", + ) + assert_matches_type(SubOrganization, sub_organization, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_retrieve(self, client: PostGrid) -> None: + response = client.print_mail.sub_organizations.with_raw_response.retrieve( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + sub_organization = response.parse() + assert_matches_type(SubOrganization, sub_organization, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_retrieve(self, client: PostGrid) -> None: + with client.print_mail.sub_organizations.with_streaming_response.retrieve( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + sub_organization = response.parse() + assert_matches_type(SubOrganization, sub_organization, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_path_params_retrieve(self, client: PostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.print_mail.sub_organizations.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip() + @parametrize + def test_method_update(self, client: PostGrid) -> None: + sub_organization = client.print_mail.sub_organizations.update( + country_code="CA", + email="suborg@postgrid.com", + name="Calvin", + organization_name="PostGrid", + password="very-strong-password", + ) + assert_matches_type(SubOrganizationUpdateResponse, sub_organization, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_update_with_all_params(self, client: PostGrid) -> None: + sub_organization = client.print_mail.sub_organizations.update( + country_code="CA", + email="suborg@postgrid.com", + name="Calvin", + organization_name="PostGrid", + password="very-strong-password", + phone_number="9059059059", + ) + assert_matches_type(SubOrganizationUpdateResponse, sub_organization, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_update(self, client: PostGrid) -> None: + response = client.print_mail.sub_organizations.with_raw_response.update( + country_code="CA", + email="suborg@postgrid.com", + name="Calvin", + organization_name="PostGrid", + password="very-strong-password", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + sub_organization = response.parse() + assert_matches_type(SubOrganizationUpdateResponse, sub_organization, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_update(self, client: PostGrid) -> None: + with client.print_mail.sub_organizations.with_streaming_response.update( + country_code="CA", + email="suborg@postgrid.com", + name="Calvin", + organization_name="PostGrid", + password="very-strong-password", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + sub_organization = response.parse() + assert_matches_type(SubOrganizationUpdateResponse, sub_organization, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_method_list(self, client: PostGrid) -> None: + sub_organization = client.print_mail.sub_organizations.list() + assert_matches_type(SyncSkipLimit[SubOrganization], sub_organization, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_list_with_all_params(self, client: PostGrid) -> None: + sub_organization = client.print_mail.sub_organizations.list( + limit=0, + search="search", + skip=0, + ) + assert_matches_type(SyncSkipLimit[SubOrganization], sub_organization, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_list(self, client: PostGrid) -> None: + response = client.print_mail.sub_organizations.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + sub_organization = response.parse() + assert_matches_type(SyncSkipLimit[SubOrganization], sub_organization, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_list(self, client: PostGrid) -> None: + with client.print_mail.sub_organizations.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + sub_organization = response.parse() + assert_matches_type(SyncSkipLimit[SubOrganization], sub_organization, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_method_retrieve_users(self, client: PostGrid) -> None: + sub_organization = client.print_mail.sub_organizations.retrieve_users( + id="id", + ) + assert_matches_type(SubOrganizationRetrieveUsersResponse, sub_organization, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_retrieve_users_with_all_params(self, client: PostGrid) -> None: + sub_organization = client.print_mail.sub_organizations.retrieve_users( + id="id", + limit=0, + search="search", + skip=0, + ) + assert_matches_type(SubOrganizationRetrieveUsersResponse, sub_organization, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_retrieve_users(self, client: PostGrid) -> None: + response = client.print_mail.sub_organizations.with_raw_response.retrieve_users( + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + sub_organization = response.parse() + assert_matches_type(SubOrganizationRetrieveUsersResponse, sub_organization, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_retrieve_users(self, client: PostGrid) -> None: + with client.print_mail.sub_organizations.with_streaming_response.retrieve_users( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + sub_organization = response.parse() + assert_matches_type(SubOrganizationRetrieveUsersResponse, sub_organization, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_path_params_retrieve_users(self, client: PostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.print_mail.sub_organizations.with_raw_response.retrieve_users( + id="", + ) + + +class TestAsyncSubOrganizations: + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip() + @parametrize + async def test_method_retrieve(self, async_client: AsyncPostGrid) -> None: + sub_organization = await async_client.print_mail.sub_organizations.retrieve( + "id", + ) + assert_matches_type(SubOrganization, sub_organization, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.sub_organizations.with_raw_response.retrieve( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + sub_organization = await response.parse() + assert_matches_type(SubOrganization, sub_organization, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.sub_organizations.with_streaming_response.retrieve( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + sub_organization = await response.parse() + assert_matches_type(SubOrganization, sub_organization, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncPostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.print_mail.sub_organizations.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip() + @parametrize + async def test_method_update(self, async_client: AsyncPostGrid) -> None: + sub_organization = await async_client.print_mail.sub_organizations.update( + country_code="CA", + email="suborg@postgrid.com", + name="Calvin", + organization_name="PostGrid", + password="very-strong-password", + ) + assert_matches_type(SubOrganizationUpdateResponse, sub_organization, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_update_with_all_params(self, async_client: AsyncPostGrid) -> None: + sub_organization = await async_client.print_mail.sub_organizations.update( + country_code="CA", + email="suborg@postgrid.com", + name="Calvin", + organization_name="PostGrid", + password="very-strong-password", + phone_number="9059059059", + ) + assert_matches_type(SubOrganizationUpdateResponse, sub_organization, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_update(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.sub_organizations.with_raw_response.update( + country_code="CA", + email="suborg@postgrid.com", + name="Calvin", + organization_name="PostGrid", + password="very-strong-password", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + sub_organization = await response.parse() + assert_matches_type(SubOrganizationUpdateResponse, sub_organization, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_update(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.sub_organizations.with_streaming_response.update( + country_code="CA", + email="suborg@postgrid.com", + name="Calvin", + organization_name="PostGrid", + password="very-strong-password", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + sub_organization = await response.parse() + assert_matches_type(SubOrganizationUpdateResponse, sub_organization, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_method_list(self, async_client: AsyncPostGrid) -> None: + sub_organization = await async_client.print_mail.sub_organizations.list() + assert_matches_type(AsyncSkipLimit[SubOrganization], sub_organization, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncPostGrid) -> None: + sub_organization = await async_client.print_mail.sub_organizations.list( + limit=0, + search="search", + skip=0, + ) + assert_matches_type(AsyncSkipLimit[SubOrganization], sub_organization, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_list(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.sub_organizations.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + sub_organization = await response.parse() + assert_matches_type(AsyncSkipLimit[SubOrganization], sub_organization, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_list(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.sub_organizations.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + sub_organization = await response.parse() + assert_matches_type(AsyncSkipLimit[SubOrganization], sub_organization, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_method_retrieve_users(self, async_client: AsyncPostGrid) -> None: + sub_organization = await async_client.print_mail.sub_organizations.retrieve_users( + id="id", + ) + assert_matches_type(SubOrganizationRetrieveUsersResponse, sub_organization, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_retrieve_users_with_all_params(self, async_client: AsyncPostGrid) -> None: + sub_organization = await async_client.print_mail.sub_organizations.retrieve_users( + id="id", + limit=0, + search="search", + skip=0, + ) + assert_matches_type(SubOrganizationRetrieveUsersResponse, sub_organization, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_retrieve_users(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.sub_organizations.with_raw_response.retrieve_users( + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + sub_organization = await response.parse() + assert_matches_type(SubOrganizationRetrieveUsersResponse, sub_organization, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_retrieve_users(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.sub_organizations.with_streaming_response.retrieve_users( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + sub_organization = await response.parse() + assert_matches_type(SubOrganizationRetrieveUsersResponse, sub_organization, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_path_params_retrieve_users(self, async_client: AsyncPostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.print_mail.sub_organizations.with_raw_response.retrieve_users( + id="", + ) diff --git a/tests/api_resources/print_mail/test_templates.py b/tests/api_resources/print_mail/test_templates.py new file mode 100644 index 0000000..d486ace --- /dev/null +++ b/tests/api_resources/print_mail/test_templates.py @@ -0,0 +1,452 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from postgrid import PostGrid, AsyncPostGrid +from tests.utils import assert_matches_type +from postgrid.pagination import SyncSkipLimit, AsyncSkipLimit +from postgrid.types.print_mail import ( + Template, + TemplateDeleteResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestTemplates: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip() + @parametrize + def test_method_create(self, client: PostGrid) -> None: + template = client.print_mail.templates.create() + assert_matches_type(Template, template, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_create_with_all_params(self, client: PostGrid) -> None: + template = client.print_mail.templates.create( + description="Test", + html="Hello {{to.firstName}}", + metadata={"foo": "bar"}, + ) + assert_matches_type(Template, template, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_create(self, client: PostGrid) -> None: + response = client.print_mail.templates.with_raw_response.create() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + template = response.parse() + assert_matches_type(Template, template, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_create(self, client: PostGrid) -> None: + with client.print_mail.templates.with_streaming_response.create() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + template = response.parse() + assert_matches_type(Template, template, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_method_retrieve(self, client: PostGrid) -> None: + template = client.print_mail.templates.retrieve( + "id", + ) + assert_matches_type(Template, template, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_retrieve(self, client: PostGrid) -> None: + response = client.print_mail.templates.with_raw_response.retrieve( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + template = response.parse() + assert_matches_type(Template, template, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_retrieve(self, client: PostGrid) -> None: + with client.print_mail.templates.with_streaming_response.retrieve( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + template = response.parse() + assert_matches_type(Template, template, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_path_params_retrieve(self, client: PostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.print_mail.templates.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip() + @parametrize + def test_method_update(self, client: PostGrid) -> None: + template = client.print_mail.templates.update( + id="id", + ) + assert_matches_type(Template, template, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_update_with_all_params(self, client: PostGrid) -> None: + template = client.print_mail.templates.update( + id="id", + description="Test", + html="Hello {{to.firstName}}!", + metadata={"foo": "bar"}, + ) + assert_matches_type(Template, template, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_update(self, client: PostGrid) -> None: + response = client.print_mail.templates.with_raw_response.update( + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + template = response.parse() + assert_matches_type(Template, template, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_update(self, client: PostGrid) -> None: + with client.print_mail.templates.with_streaming_response.update( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + template = response.parse() + assert_matches_type(Template, template, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_path_params_update(self, client: PostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.print_mail.templates.with_raw_response.update( + id="", + ) + + @pytest.mark.skip() + @parametrize + def test_method_list(self, client: PostGrid) -> None: + template = client.print_mail.templates.list() + assert_matches_type(SyncSkipLimit[Template], template, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_list_with_all_params(self, client: PostGrid) -> None: + template = client.print_mail.templates.list( + limit=0, + search="search", + skip=0, + ) + assert_matches_type(SyncSkipLimit[Template], template, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_list(self, client: PostGrid) -> None: + response = client.print_mail.templates.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + template = response.parse() + assert_matches_type(SyncSkipLimit[Template], template, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_list(self, client: PostGrid) -> None: + with client.print_mail.templates.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + template = response.parse() + assert_matches_type(SyncSkipLimit[Template], template, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_method_delete(self, client: PostGrid) -> None: + template = client.print_mail.templates.delete( + "id", + ) + assert_matches_type(TemplateDeleteResponse, template, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_delete(self, client: PostGrid) -> None: + response = client.print_mail.templates.with_raw_response.delete( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + template = response.parse() + assert_matches_type(TemplateDeleteResponse, template, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_delete(self, client: PostGrid) -> None: + with client.print_mail.templates.with_streaming_response.delete( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + template = response.parse() + assert_matches_type(TemplateDeleteResponse, template, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_path_params_delete(self, client: PostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.print_mail.templates.with_raw_response.delete( + "", + ) + + +class TestAsyncTemplates: + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip() + @parametrize + async def test_method_create(self, async_client: AsyncPostGrid) -> None: + template = await async_client.print_mail.templates.create() + assert_matches_type(Template, template, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncPostGrid) -> None: + template = await async_client.print_mail.templates.create( + description="Test", + html="Hello {{to.firstName}}", + metadata={"foo": "bar"}, + ) + assert_matches_type(Template, template, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_create(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.templates.with_raw_response.create() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + template = await response.parse() + assert_matches_type(Template, template, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_create(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.templates.with_streaming_response.create() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + template = await response.parse() + assert_matches_type(Template, template, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_method_retrieve(self, async_client: AsyncPostGrid) -> None: + template = await async_client.print_mail.templates.retrieve( + "id", + ) + assert_matches_type(Template, template, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.templates.with_raw_response.retrieve( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + template = await response.parse() + assert_matches_type(Template, template, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.templates.with_streaming_response.retrieve( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + template = await response.parse() + assert_matches_type(Template, template, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncPostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.print_mail.templates.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip() + @parametrize + async def test_method_update(self, async_client: AsyncPostGrid) -> None: + template = await async_client.print_mail.templates.update( + id="id", + ) + assert_matches_type(Template, template, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_update_with_all_params(self, async_client: AsyncPostGrid) -> None: + template = await async_client.print_mail.templates.update( + id="id", + description="Test", + html="Hello {{to.firstName}}!", + metadata={"foo": "bar"}, + ) + assert_matches_type(Template, template, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_update(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.templates.with_raw_response.update( + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + template = await response.parse() + assert_matches_type(Template, template, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_update(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.templates.with_streaming_response.update( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + template = await response.parse() + assert_matches_type(Template, template, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_path_params_update(self, async_client: AsyncPostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.print_mail.templates.with_raw_response.update( + id="", + ) + + @pytest.mark.skip() + @parametrize + async def test_method_list(self, async_client: AsyncPostGrid) -> None: + template = await async_client.print_mail.templates.list() + assert_matches_type(AsyncSkipLimit[Template], template, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncPostGrid) -> None: + template = await async_client.print_mail.templates.list( + limit=0, + search="search", + skip=0, + ) + assert_matches_type(AsyncSkipLimit[Template], template, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_list(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.templates.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + template = await response.parse() + assert_matches_type(AsyncSkipLimit[Template], template, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_list(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.templates.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + template = await response.parse() + assert_matches_type(AsyncSkipLimit[Template], template, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_method_delete(self, async_client: AsyncPostGrid) -> None: + template = await async_client.print_mail.templates.delete( + "id", + ) + assert_matches_type(TemplateDeleteResponse, template, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_delete(self, async_client: AsyncPostGrid) -> None: + response = await async_client.print_mail.templates.with_raw_response.delete( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + template = await response.parse() + assert_matches_type(TemplateDeleteResponse, template, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncPostGrid) -> None: + async with async_client.print_mail.templates.with_streaming_response.delete( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + template = await response.parse() + assert_matches_type(TemplateDeleteResponse, template, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_path_params_delete(self, async_client: AsyncPostGrid) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.print_mail.templates.with_raw_response.delete( + "", + ) diff --git a/tests/api_resources/test_address_verification.py b/tests/api_resources/test_address_verification.py new file mode 100644 index 0000000..ea166f9 --- /dev/null +++ b/tests/api_resources/test_address_verification.py @@ -0,0 +1,254 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from postgrid import PostGrid, AsyncPostGrid +from tests.utils import assert_matches_type +from postgrid.types import AddressVerificationVerifyResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestAddressVerification: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip() + @parametrize + def test_method_verify_overload_1(self, client: PostGrid) -> None: + address_verification = client.address_verification.verify( + address="address", + ) + assert_matches_type(AddressVerificationVerifyResponse, address_verification, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_verify_with_all_params_overload_1(self, client: PostGrid) -> None: + address_verification = client.address_verification.verify( + address="address", + geocode=True, + include_details=True, + proper_case=True, + ) + assert_matches_type(AddressVerificationVerifyResponse, address_verification, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_verify_overload_1(self, client: PostGrid) -> None: + response = client.address_verification.with_raw_response.verify( + address="address", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + address_verification = response.parse() + assert_matches_type(AddressVerificationVerifyResponse, address_verification, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_verify_overload_1(self, client: PostGrid) -> None: + with client.address_verification.with_streaming_response.verify( + address="address", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + address_verification = response.parse() + assert_matches_type(AddressVerificationVerifyResponse, address_verification, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_method_verify_overload_2(self, client: PostGrid) -> None: + address_verification = client.address_verification.verify( + address={ + "city": "city", + "country": "ca", + "line1": "line1", + "postal_or_zip": "postalOrZip", + "province_or_state": "provinceOrState", + }, + ) + assert_matches_type(AddressVerificationVerifyResponse, address_verification, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_verify_with_all_params_overload_2(self, client: PostGrid) -> None: + address_verification = client.address_verification.verify( + address={ + "city": "city", + "country": "ca", + "line1": "line1", + "postal_or_zip": "postalOrZip", + "province_or_state": "provinceOrState", + "line2": "line2", + "recipient": "recipient", + }, + geocode=True, + include_details=True, + proper_case=True, + ) + assert_matches_type(AddressVerificationVerifyResponse, address_verification, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_verify_overload_2(self, client: PostGrid) -> None: + response = client.address_verification.with_raw_response.verify( + address={ + "city": "city", + "country": "ca", + "line1": "line1", + "postal_or_zip": "postalOrZip", + "province_or_state": "provinceOrState", + }, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + address_verification = response.parse() + assert_matches_type(AddressVerificationVerifyResponse, address_verification, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_verify_overload_2(self, client: PostGrid) -> None: + with client.address_verification.with_streaming_response.verify( + address={ + "city": "city", + "country": "ca", + "line1": "line1", + "postal_or_zip": "postalOrZip", + "province_or_state": "provinceOrState", + }, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + address_verification = response.parse() + assert_matches_type(AddressVerificationVerifyResponse, address_verification, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncAddressVerification: + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip() + @parametrize + async def test_method_verify_overload_1(self, async_client: AsyncPostGrid) -> None: + address_verification = await async_client.address_verification.verify( + address="address", + ) + assert_matches_type(AddressVerificationVerifyResponse, address_verification, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_verify_with_all_params_overload_1(self, async_client: AsyncPostGrid) -> None: + address_verification = await async_client.address_verification.verify( + address="address", + geocode=True, + include_details=True, + proper_case=True, + ) + assert_matches_type(AddressVerificationVerifyResponse, address_verification, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_verify_overload_1(self, async_client: AsyncPostGrid) -> None: + response = await async_client.address_verification.with_raw_response.verify( + address="address", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + address_verification = await response.parse() + assert_matches_type(AddressVerificationVerifyResponse, address_verification, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_verify_overload_1(self, async_client: AsyncPostGrid) -> None: + async with async_client.address_verification.with_streaming_response.verify( + address="address", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + address_verification = await response.parse() + assert_matches_type(AddressVerificationVerifyResponse, address_verification, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_method_verify_overload_2(self, async_client: AsyncPostGrid) -> None: + address_verification = await async_client.address_verification.verify( + address={ + "city": "city", + "country": "ca", + "line1": "line1", + "postal_or_zip": "postalOrZip", + "province_or_state": "provinceOrState", + }, + ) + assert_matches_type(AddressVerificationVerifyResponse, address_verification, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_verify_with_all_params_overload_2(self, async_client: AsyncPostGrid) -> None: + address_verification = await async_client.address_verification.verify( + address={ + "city": "city", + "country": "ca", + "line1": "line1", + "postal_or_zip": "postalOrZip", + "province_or_state": "provinceOrState", + "line2": "line2", + "recipient": "recipient", + }, + geocode=True, + include_details=True, + proper_case=True, + ) + assert_matches_type(AddressVerificationVerifyResponse, address_verification, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_verify_overload_2(self, async_client: AsyncPostGrid) -> None: + response = await async_client.address_verification.with_raw_response.verify( + address={ + "city": "city", + "country": "ca", + "line1": "line1", + "postal_or_zip": "postalOrZip", + "province_or_state": "provinceOrState", + }, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + address_verification = await response.parse() + assert_matches_type(AddressVerificationVerifyResponse, address_verification, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_verify_overload_2(self, async_client: AsyncPostGrid) -> None: + async with async_client.address_verification.with_streaming_response.verify( + address={ + "city": "city", + "country": "ca", + "line1": "line1", + "postal_or_zip": "postalOrZip", + "province_or_state": "provinceOrState", + }, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + address_verification = await response.parse() + assert_matches_type(AddressVerificationVerifyResponse, address_verification, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_intl_address_verification.py b/tests/api_resources/test_intl_address_verification.py new file mode 100644 index 0000000..c0c800f --- /dev/null +++ b/tests/api_resources/test_intl_address_verification.py @@ -0,0 +1,250 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from postgrid import PostGrid, AsyncPostGrid +from tests.utils import assert_matches_type +from postgrid.types import IntlAddressVerificationVerifyResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestIntlAddressVerification: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip() + @parametrize + def test_method_verify_overload_1(self, client: PostGrid) -> None: + intl_address_verification = client.intl_address_verification.verify( + address={ + "country": "country", + "line1": "line1", + "postal_or_zip": "postalOrZip", + "province_or_state": "provinceOrState", + }, + ) + assert_matches_type(IntlAddressVerificationVerifyResponse, intl_address_verification, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_verify_with_all_params_overload_1(self, client: PostGrid) -> None: + intl_address_verification = client.intl_address_verification.verify( + address={ + "country": "country", + "line1": "line1", + "postal_or_zip": "postalOrZip", + "province_or_state": "provinceOrState", + "city": "city", + "line2": "line2", + "line3": "line3", + "line4": "line4", + }, + geo_data=True, + include_details=True, + proper_case=True, + ) + assert_matches_type(IntlAddressVerificationVerifyResponse, intl_address_verification, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_verify_overload_1(self, client: PostGrid) -> None: + response = client.intl_address_verification.with_raw_response.verify( + address={ + "country": "country", + "line1": "line1", + "postal_or_zip": "postalOrZip", + "province_or_state": "provinceOrState", + }, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + intl_address_verification = response.parse() + assert_matches_type(IntlAddressVerificationVerifyResponse, intl_address_verification, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_verify_overload_1(self, client: PostGrid) -> None: + with client.intl_address_verification.with_streaming_response.verify( + address={ + "country": "country", + "line1": "line1", + "postal_or_zip": "postalOrZip", + "province_or_state": "provinceOrState", + }, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + intl_address_verification = response.parse() + assert_matches_type(IntlAddressVerificationVerifyResponse, intl_address_verification, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + def test_method_verify_overload_2(self, client: PostGrid) -> None: + intl_address_verification = client.intl_address_verification.verify( + address="address", + ) + assert_matches_type(IntlAddressVerificationVerifyResponse, intl_address_verification, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_method_verify_with_all_params_overload_2(self, client: PostGrid) -> None: + intl_address_verification = client.intl_address_verification.verify( + address="address", + geo_data=True, + include_details=True, + proper_case=True, + ) + assert_matches_type(IntlAddressVerificationVerifyResponse, intl_address_verification, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_raw_response_verify_overload_2(self, client: PostGrid) -> None: + response = client.intl_address_verification.with_raw_response.verify( + address="address", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + intl_address_verification = response.parse() + assert_matches_type(IntlAddressVerificationVerifyResponse, intl_address_verification, path=["response"]) + + @pytest.mark.skip() + @parametrize + def test_streaming_response_verify_overload_2(self, client: PostGrid) -> None: + with client.intl_address_verification.with_streaming_response.verify( + address="address", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + intl_address_verification = response.parse() + assert_matches_type(IntlAddressVerificationVerifyResponse, intl_address_verification, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncIntlAddressVerification: + parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip() + @parametrize + async def test_method_verify_overload_1(self, async_client: AsyncPostGrid) -> None: + intl_address_verification = await async_client.intl_address_verification.verify( + address={ + "country": "country", + "line1": "line1", + "postal_or_zip": "postalOrZip", + "province_or_state": "provinceOrState", + }, + ) + assert_matches_type(IntlAddressVerificationVerifyResponse, intl_address_verification, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_verify_with_all_params_overload_1(self, async_client: AsyncPostGrid) -> None: + intl_address_verification = await async_client.intl_address_verification.verify( + address={ + "country": "country", + "line1": "line1", + "postal_or_zip": "postalOrZip", + "province_or_state": "provinceOrState", + "city": "city", + "line2": "line2", + "line3": "line3", + "line4": "line4", + }, + geo_data=True, + include_details=True, + proper_case=True, + ) + assert_matches_type(IntlAddressVerificationVerifyResponse, intl_address_verification, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_verify_overload_1(self, async_client: AsyncPostGrid) -> None: + response = await async_client.intl_address_verification.with_raw_response.verify( + address={ + "country": "country", + "line1": "line1", + "postal_or_zip": "postalOrZip", + "province_or_state": "provinceOrState", + }, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + intl_address_verification = await response.parse() + assert_matches_type(IntlAddressVerificationVerifyResponse, intl_address_verification, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_verify_overload_1(self, async_client: AsyncPostGrid) -> None: + async with async_client.intl_address_verification.with_streaming_response.verify( + address={ + "country": "country", + "line1": "line1", + "postal_or_zip": "postalOrZip", + "province_or_state": "provinceOrState", + }, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + intl_address_verification = await response.parse() + assert_matches_type(IntlAddressVerificationVerifyResponse, intl_address_verification, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip() + @parametrize + async def test_method_verify_overload_2(self, async_client: AsyncPostGrid) -> None: + intl_address_verification = await async_client.intl_address_verification.verify( + address="address", + ) + assert_matches_type(IntlAddressVerificationVerifyResponse, intl_address_verification, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_method_verify_with_all_params_overload_2(self, async_client: AsyncPostGrid) -> None: + intl_address_verification = await async_client.intl_address_verification.verify( + address="address", + geo_data=True, + include_details=True, + proper_case=True, + ) + assert_matches_type(IntlAddressVerificationVerifyResponse, intl_address_verification, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_raw_response_verify_overload_2(self, async_client: AsyncPostGrid) -> None: + response = await async_client.intl_address_verification.with_raw_response.verify( + address="address", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + intl_address_verification = await response.parse() + assert_matches_type(IntlAddressVerificationVerifyResponse, intl_address_verification, path=["response"]) + + @pytest.mark.skip() + @parametrize + async def test_streaming_response_verify_overload_2(self, async_client: AsyncPostGrid) -> None: + async with async_client.intl_address_verification.with_streaming_response.verify( + address="address", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + intl_address_verification = await response.parse() + assert_matches_type(IntlAddressVerificationVerifyResponse, intl_address_verification, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/assets/signature.png b/tests/assets/signature.png deleted file mode 100644 index e700dd88ea9958579bfc60f91f5d59fd41d9e49b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14041 zcmdse0T@>1}sH6X&o=qovgrZu^w;ayjF4%0p%DLH{rg2vVx+Vu|;WulIs2$Q4*UJ zt!yC+_Im-={HV+-8#C9K+ym2|U`#L4uimsXp zn(}WxJk~59318dUlkjTWLbP1+*NO^YsBI6MhUa$&#Hv2dpGX`)+c$-9Knz&ecv4t6 zU1XBetNM9}HN*1b7VV4|N{1{Ch;~HuH4`?}A_8R3e7{^XcKt zyB`0*>GFUhuZgyvSty6MB5v!X;t3{~kqo=|8e$LGGCl3Ye&I7aN2~N$-pk!uDR=G;+M#kWw~p zwT~Thr&=0tM@bk~h^exw`AY?Jvw?e_Gxq=q4*>~fo1JOl zLEVGQE78AE{0q3lry{Dj!1Op^IwTpsz(BDB=W*YI1&*_A2>(^h4p41EhdG=w*mW5d zQd_e=zvI~;=K1SpRkmzkn%_fnDcN6hjZL>)hA(Sl8*kme@ux1}&$2StiS9=1{tQ|kir3ESBgObAakdzO za;rXNL%+D#bZ-#a?KPYh351`+oPDgyIxUz{8U$|x13J@- z1^_!80UaE59;M-9SF)yR^a~I*xbL8c1U-0ov%uw6THX(C;Sejl#FAF;Nl98JONhL# z=#mgc{LboGQ;!CwcLQ#C#~G17FZ0!=ZfhPD!D+veyqq-_1b{*pK#jicd>1umaEi@U zL6-!tE(6ou~3abPaPyumecEmOb^JNyH}htJ&p8_k(Y6 zBL+nMl%rw+%X@ZaUiG^l%-aa>_p9qS^G!Gl-baJCZUNwC4ayxOcVS^5L4K)RGi@B@ zX=#_xU0lvuGpS7?#37DU!;+4VO9DM1`4i<}wp! zsg@Ie9gA1aOgqmwy$E(+6VG#|1lA`4*3WLP_(2-g*gTABqvAFxV!6Lfpsx-6N)E)3 z0(eZpazFk3*GcsAXCVtVq?ysjkzIE(o=VsSVz?YKelmRy0qjTspq8D@FLKDrXFVsD ziC0|cKojFn4?0hM_v9u$Edd>eTSN5p#45qp1znd|E%iAV$l(5ryxPKZYygp8Hvu4* zm!s_oC%u~9n$USJ<}P14*tPDp@(RY?n85^?VOZHkRS5U+o*_L_cGS2_c(-NdNIawW zf&R_|O27{Qp5he~>$*utNb89VAB{q;7Pn6wVNb`K{B1hpxu2}vBt>sJpsD*&MZQz| z`(5LfV?p7}iK};6he|QUZLF@3Zql6^h@0S~orus`g$H7>wThvkWoFTFt*!J?B0Kxd zfM)zk5U283MStjmzPgvVnC zHL0Hz#63`ify zWi3@;b*38miMWe>dEc%QPX7PM-9VI1-l2ek$4-jSU9?* zQX}ORlNIkiq;d8sx0A1Y6MzN8lC}enh}QwF#<;Xdr;L_JZ7&hzZugc^NVa{^o2UtZ zh~3g(DB&1^uhe4b5g+iEQS8v*Iz~VjU@?8o!5GS>T9f+s@0RL`+enXUJ)9QzkD(jr zzmq3!hGo1mzL& zb?jdWIU-4TH)yy5+;Op0(n-1rS(};Oam)L&sfT0|UM#-k$8ZI+{@oeB;sGTO?@gx5 z`>$;zw%!|SA$wbGw)am9E{{C3LMi?_<^`b0OR43g7#aUMvx$j-=C4tM7H{NkeD)Y{ z>62R!Zx?jms_h<+lUHYeFW+gFq7$d%vos=XyiUfT+@9YG9N?bs?Qiz)$E4B*u+AX#Gcr61ysG&X)0*}HJY3?d6^$xI&V#*qMK!+^PSb_K2IUQr~>X;W(t zJ#=!6XqVpUKlYw|WR_i+1Z3|23T<&h{tr?8)N}Z!R=|WT-^N9Wb=@-1-xEco3Xt9R z+^l{$44B4dEmluuPcLMHPO52Z&8X-H06;SE(O`r8g~oNQnf6Tb7Zz$EbS*;h`}2!a zll6U@J#2u9tiW!dR6KKM5b$!D#Ea|9+sODC8 zT0hqw2bCWA<+T0$11o|BIQc$l%Ox< zJ1uOBBmOdhNuTt_=~%%FfC&pFdj7wLDpDibYADr0mcuy9VdRtm;#TBQX(DAqZ>ShZ~ zld0{~`Q4-5zm?A1qj3SOgaOY8I0f?+(-Cm^ky%ZNm0kKzczbcAcftc;E;gPVPy}8E z890-ohXPZ(93AZMoj6ua*{l3LsL=sCTI=(S@=kQ;HKMBu`gCQ-*7ZFr{f(=h07dUf zZskf+e0o+D%zkypmb27t;4K!Yk2xqe)!36=`RcjyMRCSGy88B++P|k9EF3Bz>jdDL zVbMh&x34FbVz|)9bzvOA_BXz}2h1a4*1O)Pe@fOqyTohkmn^@KtrwVO!3gZVK*k|L zl-|AhJ~TQy3BT@HqL&+Z@`0KCMwKYQJ5ksaNq(o#$H4|ok_H;n>>M|qnYgK&i=b>= z;yWiJf+i4;& zGf$J67C61cV=7bt`fIr|kiV(95`oz>_(S5(gO>V#Doyvk466^DF}VPd1w5JtsGs)i zKvg;M*KR{&&3gan{bNI!swn6G3J+T*-~cb&W45+tt;ZQpk7Ae zMwwrTV@yAHTeY{w{r#btdT?(T^r zr%b1C&1i7(&qVe%4vuGIpaPV>){NB+CWqECke76I?1^oXLV**zVt!o`en(u)Pss2n z0VKCsR|_OQD@Q;@(gP9`BESXdc=pt@4f^lUvF>)CX^lroG<-(|3y)VBAGr7zFCMDB z<@4*j@^)x8fBzH&0}=%}@z{@aPUIUpwR(I#Yp;?+c27aDJOgfj`7q|XZ%H|0)B3X( zWJe-d(C1HbLWHuYB=owxlGf-W-A*7PWGXbgMTnegK~Yz<03!xebMe^kEn_V#-+uH# zp1?>iwZ*L$k8jtmzT|FWs#cVNH{tyLAU6LN^5+QiR^KRhqQWK9F{$&gC;b@u9B)VrLqh_y-b~dB&6r(UI=Av?4Z(aRjv+Ct*&#-ZQf8S z!GJ&rqQWEEPZ1{K#F$%4+Q(;=Sh#E}A#a8;k503?D7w({7)F!B~{bT=mSIoDJ|Cr`s*f$cdsj@y~yt zj%1I37-PrDjslzJ7+UpXbCqDKMh;EYSnSK$)`i)F7s;PnEygCNJcVbyBK7G|r8M=t zoYWcJ1~kIFyf}IKaF~nu;qILWnz9Bn!6UbpQMZ0Mzqxq}eg1`Advfrl3@d;8N+I30 zr+5ACbP<>Kb{#k+m(IxY(|5`$F|M&6S538NA@HA5^|~*79I7s42i*(4`~X@lX+@7%J9KR1tRsN(%)ue>Q|c6R1a{CR@D7#kbS@1{x` zh88}nu#-uNw`6H=P73QHH&%7#F<&Yznvor(ncMVxt3OZf&8t+=Yegyq(HP662{5@k z9Hdp_QH!1wf(0HpXp#G9{6y#YC-#lgIJWQw4z`{445s>U>NS?n6@<9Dd;tmRea@*p zRaM15BCa@Q)}$j_`Hhjd{rnoqwMiHN!n2eG_Us}KIJq2|rw2La9dZNpDI*nTx5hF6 z8FVVTWf`AB;0;D4#%5VKwnDnDs{PN>Q4#91i>ZT_G}8m>QbZBya-~oyX-6a`hCH4u zda1)CyVG$dLf=RSDUOXZ4HV?s(&!l6>FFw@P&_^vn^gwV-}OLuI;W<@Gr5>@?^<5@ zWR;L%AxQT{2EDctgcY67yy?TIgLxjO$R_;Kw$z3z)1dFsyC@A2aYCTZ5+c(|VO;$9 zZi~CFIbj*D^P8yktv-Y=M4Y(Phb(D#N41czL)?06;7O&!ShibbC#g%r_A7e7YaIWR z68V#>gX(N8sa(c{1JxX*<_+67QSUYt!Icuyi6}o2wVdTO32R5HbMlI-gbzx+Rv9p~ z`BGju?Ewm2YjU7Hf6ml5CBu_3t2|!k_PLWew`r1oHTv&a^_h!{1Po52Pyh3PW`t1m z>Om8icH&(Gntv;>2esJ7K8EV~FtFC@lqnXLfC+rgrm;yYIg*4$lKJu`mA=5a!a+)HJoC%F#! zy$L29)zA9?M)wW6Resn^*lAOGZ}^AgB#yn!ihVfOBA04ZOS6zhb-g|I0WJ1|$UOZ| z(E=-kjPyW9+7?rtfYylDf)V{FwP&3V0gfSOy4k~J$zTynbnIJgu;~1$3tlsJnF_kV zgFOE3!)ScxyTS2EmGC?GO*11FgMvS8PHpXvuU>7hmFp`m77n=D8;TI&GyqXQfD)&J ziebl$;P35JVZ(~T?5Xy+AJen_bpt0Cw}l(we>w|meb9NkBS}Z2<;x6 zfV~ljdY$O!2c)uJk+xQnzVDmPi)Rdsk~SMujm;#R!E)lb&DiB8%Hd8jnLY%5A!8q` z9`9{|{BR0qelD0VzAoNkH8p&`FHNx z1MH{7;i((8vC}7a~})O1ZO^SR~o%i+9#r(gh~# z>bJ7258@3k63N@I2x6pwzl{TSPF^X13G8JpzMAEZ(X#Xa$|2t z#?hr7ieH?;)fplyVJx6U6yk%GoWKjA_bD0a2#XM`Bbf(V9uOJ&ef#+0Bya~)g&)BA z+sLfrXsrJQ(EBP{zjHR#0COSD&U@Y0h#6^hYlVSGUFQ#zPF|}#7RdgDw)>nL(d5f- z{Y(&3p+nL3z+osAZG!V@OHK9#o!#CyXGUaw`S4Ee0x4vSxAyeJSn{gm^4hc3(dxk; zL*U$fLHMmFrzZ}0AtB}5L#sIi!?2pqY!iv*@p{C93V19;{bTQx-I048yPWTOAd#9g z6XUzuB!=g+mvYy5aA)~L!WJExo(nh?YikEz&t0{Z>V8~wylw}l-j)~P0d|=+2Sy3l z?s9~M%i^pe+6y(+twTgl`XZKh6LnF@k8AqtkxfAb^rVgatFNH)-p}dj_ag6-BXITf z+pS$W$DhLj6np1sB;CfBezc99Hb*ReCThpnIk-v*0m$<%msyB@>vfYvn?#^=NSmsO zn|G7Mnd*WryEzZn%b2#j0qX7-WZ*FAALa9Jyk2xXYVulzNlm$a+OxB#X$cQ(fkMQ6 z;-4X3=!e0!l?VIrLs9E&;xdeY@G}mM?#71Ek-?`&TBJEqCY!jlFt(1@&b7LLQE%v5kwrxw zd6@6&hJNnlli?CN8%9KCDxX%oDCzvzxqNYej%T}PV*l*CdhZb8Ku3L$qqx(Ah_+dJ z>$ZWplNyJpGHi4*w}GlYb*{Tlw!cG2bF8sxd(GLAQ|yn`2&C~NSh;v}4xjaOexp+D z=O`y_b-~u&OPl!;vyf!^*@WYj1@6xdPcQZN3DDX!eu2?0784Y^sPax;e_j}^R)ffyV`_)@9QA4;pgl;{Rfum(9U<1D1oc4`<5 z+nz7vA5(jI?AJ$w`C+q}@;uKGALf5>58)Ek`@~<}WL-E}lju7^p(kSuf~B)8mUBH= z7qT0HXD_R!wChT4M#>2JkoFO1blz_Ce7(hzA7iETXJ2yAJl6ZxydW4aSu+QU_qoGn zvuJ&=B={#KjnYA%)h9`fty-3E4%{iHUz=@b>iYJi1rAS0c0`PfBwlNHuT9W*dp@ zWsaS0uR;YvZLIz*9!GObxM`QO$JB;Ml5MU*+GbeX*=cM%)qb011b$yjYGc;#oR$JQ zH|IJwpgr-P`a)-K&T*vKpn+WZzyLlGw0-cgIdER09W#S}5s(rz6XCncPtUR@MdwkU zs&{-We6~kf-EPH?(lBnhZ)#(9;kMNep1Gz^`}CPB9wC2JwsF2gqqNDs3Dr(oCO%}N z%u(&^8JyrQV%;wNhB%;HHZ=W0ol=;!++0iVcj0G|vpPW>PQo_L>8XssG!`hnH|A_@ zMjjz$x1G_h+`osXh-T7Q?gdU>ogq8YhkkupG`D&4Ve#&8W~pgtyzvE9jElrvp2+QJ18vX=JD664`Mpyi?{dSD^nsJuP`N z*%$ZhQM6eDuPJq;6#QWcR9mL)E1E91OBM;fHtV)H9-Xj#hk#C)@*7k~hNK)~IEXmzsz-8F8NnE*WaEP=|((*T<+@ z-q@j4D`I{k4rs_=D$QPQq2;!QGfgw-jHZB`@Af`WHcni}C!}ek-5Ii@0fc>VbFANX zD&B3{9rIfd5^gYVM&V|t*-57c^;<)VcXG339dt#lBU*6ZuYME^k*GBFvwz<0 zv#L3r1c<-o@@%JXHoQ~L)O+C0h`~;P%5!x01m_cs3$+pH+VNYkzvm;16_vHq-DvjD z+VMZ)4aVO#WcsLUO{L4H@O1Wpl>;)Yu zU88DXDQzF`)y_IH7angou9exsf*ZV}e0UNi8VI4uig^WatJu7L{K!Jw%@qB;)jUP; zr1%FnAL2;eDDlr_OT>^yy9$~Z{v6?xlm!i-s@RvwCUaVQE0tD!dU!||GXM*Ocgs|n zfRS19&SlIBeuw1nN1G-m!A>H2rI7w~4UU(q$>i-nmiBh04Rr1_@0IwMnm$k$A6~5n z0h{}khWmcNck_WGRGX>9MfL+4#$??Y>|L!5kfS@xB%=D=*j?h=oh3X}Qpk~H$ zJ-NJc@;HH_jbxj>c2AT%Xh#4ER$aKBz1pk{h`{ZWNg-Yb4yk62l+CX?wnH>tmYiREPv5s9@}lJ3jr2zHjO5{2)!OX!^R6Gg`s$9PlU83CiQ*oxS#t zKNY-Jd}A^ghN*>CK9VpR4g#A@yUL9Cx2a`RpL_3A)7`0#!9gqd@q4I5yB+S>3TN9J5A4@+UVeOQI#QKdQ1gvG0gi3+eyj1*RYOp@sHwG~C zXeq?gz;X#W*gWc+v^b`dv9NqGU&{O8-6#>lMSMWPEsiRgS6Avav&>wWi}L54WaSk*T29AWWPP#VC_!oK0i5}b>HY9V zmlkTaVR_)VeXzfdBWX0w^yD&@_WWH|Kj+U_8vgd=Ho9uR>s@N3VZ-dqml}NsYi`pf z0S%1$P8O!%`?1(PGg6;u|NTTdkQUIJ%25ZeHa5A48jKH2yNW^-tH%ke?hw!_d&LFS zj)6rG2~Rv|6<;kl?$?tG>hU-m$LRB<{Fbe{ir>ldRQ1x9V0M_MZW(-UiVGxyF{iWR z^`<#e*ebfZMh>`q+G1hhPt~a0xxJ8D@7VzTQe!Yl>38R8eZ-03xRK2?q9}JuW1&`N zpvDz~{N3_Hwy5tV(NCxaqR2xRQHgi%s#_6rIfW-*7IXe;qhHjqN)$P0EPe|u_k}zM z-nL2zu0G!NsF!r--`WF`z>(y$nK74dmtL86iI+tqeEC79-Pc{^r$jQxuh-K^(nImGF5 z-$JEx*mJ!;8Jcg=LFRW4kQ{7{cfbrB{L9ZdL`uZpLbH$fnra(ScbYx)F)!amu;AV& zOzr-0#88I>31A_h`osP>RfjoezARQ;7y{r!H5hp-3yV&!8@dtWt*#+!Z{ZFD(k zSLj=_!}DtL`Qjt=XyaLt?Y*4ZcHWIKz7PvH_|)!DChL&hODJt0Wn^N9GE=p+^hrh} z= zC8XeQn%?zBMsf0HxZ7n}Y~8I|F?6(Wc718$kH+O45!rLKFy1vWeedh90V5bf(g9#$Js-YFXNYwF++p>Q@L5GF+s9RzR}LZYjL2q3`sy^7Ig_LEj5yDS zTY=s-#xcbv9PJ4%k*V=Hi_n~Ss#N10P7`f-zEa6}M}lipsuZ=Fo6gnRF*&ie_ygq< zcU5;>@8k2VGd$&SCU0w5KrTQM)~UD`49^p9Y@g-44Lq-}KcT1n#<&GA`XHMVhoySOhz-iNY=%0z&=>`xTz zYJ{96pOIYEMSIw26Tc$#?l3-a?|Z48kT&poGd^e0{k$~Js!XxI@h`;+l8+=)BfcoU zR~&wp?1}u6zx{22j3ENJHs8P>=UOLyEBgdg+B0i15IB#NRSu3woLG|F>_>lfy_Oz4`r&sGM1XQ?}u_%Pf><_wDjwAHsxAmHt< zUyaTl`Y_`H`~b#aYFx;CZo1Q08)HxRx;fl=yxmfhX0jy41K;uK7YF6yHGAE-Cc1<} zVv@E`b5zyf)6vS{3xGjl3bG?RZ0a7AD#Ht-7^Aj2v)7{0w&(^! z^Ub3_PF4QG-2=Ib$~#@Gt@VleS3U~L?;UZp6-aMxUkCNQCrKqD|dvJo|b!`>@p`t>=o1NRi~-iUYvvLiVZ z=^ktWNOpN+i#=SMl;5A>m{pIp=PAk@kbW8afVsATnudv?;m^@lZ%AF}dLLu6QUThV z;t1k3cMn2FwCCSJ2HclZ`7+b(K487^PW(PkKC!A!sV=ot`H+CloKN?%jCSqG%9C6d0)Ti14*medYz~bYpUAT7fxDW+jl>Py_Ac_?TJ^Tz zc5y?DN0QHfnb)x?5H-xKq9GU>%8Lt)j;j9f$QQX2-%u2I=Sc|Ynj*UeTfHHBUY_1%z z?Lec9-r;LQ9#lo0b*m8|ToE>q<$WeddSO7kh4tiKzJZ4gHK)=(hN8ix0$wl&96( zZf?qb$biY(K&NrmWFsc(Z9#d4hs9#Vf(Ufj7hKPFC%Qd9L@pD^8ekp5;13{(MP4*} z_h537^*~I|5O6Jd7j&jWZLcz#^Hg*6wBUTN5T~D* z6TTq{V}Z0tdJRXIm(nwf+WC715qASEHcY7nh(6|Q=ZJ>#o@b|)EhzlQqGE8m8*G9L zF)u$tkq)^E4E5*~cA7v=(IfQrY{M9%scCGZAyjbv8Zz=PcMKbEK#f6PIyG$zGUX3y z;H}dbh^U~KG`jse&PsM@$5<%thFb#oPUUgJr(8*MatH2i6H+0)0o}MimvbS5GY5*Q zafvsK5Ge-S;5pDGw{-#N(wHmzF%=j8bz@hm;iF9m{dT6)6RN>4i*g(kKW;DrSd>rF zt2pA1IwSh%e$A8~2JTr(KIS#_>k4w4rTMY0v%}a4Pt6KOa^ibW|UHpE)p0Xa4wC?pF<*CcN`+1U$UYffh?EbWkM_A@82ZIS+U> zjy2}Dmf&PIkpturz~BMK*OK%~k9b;F0+f*_awm`2qMAl0NNX#*^0uaq82)&1I!|T~ zPjdaZVYy*}!buH`M*Qb40ys~d-~1kav}X>_=^0YcM_&6&pZoAdo_!_xq4VK}`U*H> zj;XpJ7o+vm-D>ZNt^ym%LVOec!%5br=jGD~%fHyb!nwrbVqZsxFlWyO$Rm7o*ihQm zpFCB=wTVLdl!5hbMp-JpVwX;R)5p1J@aVZ=!-&-pbJCoOkPQBu(?@fQjVFmvHp-9~* z`XeejHz%`6wIZAP#wO+^JZLcwFv9@?Rxey%jzG{tRrrxm&kDtC6EI52a!nzV} zl@8g--F^^Qj+x{C!f?|9q|qGRJF!%l~uvyY4qr+$HKy>;A%I&zqI)Lqu!J%|0w4hhBT0JvaiZ=_XbHF9t(7e4r>n7 zmJ$=(ut{&h;uGGRc{=v&7>QUwGQPwA3*cwj1s>Hr@k;R~5PK!}k6b z7JHu4i}8?Zgz^B!<0nOA(&HP#8jzPLR=yV_U_BLw{W2$qm*A+g9`0~Qo)(XxiwC_V z|7EU*a4>&J0AI!t4tZD$_qj`z5JA)#ufYZC*PYJRKeuJ~|0~w&II_6@7S@fPR|WnH z+5F|@8JCOGHPpVW|DeG@{PC-Xw4}k+ZdT6hL96mW;KUH-6M5C(+jx7rA2;++K%GhH zSCP3V`$P4%1Iy&@6%mCEZjhI%=FET1sYw4yEKOwQJX0Uginx{e6;)lhRmMrV{CE9V zw4ieWpT>?^8)Ldg1;j$WX>i%H7z1kSjjRFwUw2axuXC>Za+V+4>%XaDTog245L%m~ z^u2AZ{U3WRND2Zv$7^ndsoMEUTbvVOeC;IYx2x1Y-2G8q2!-r`pnsjt;ELl9f+Wt3 z7s44{Bb(9ikJ~T~^K9=z?f)=X5-&wBeJwA5S&iMGuW7#?$(NIO#QSn!3rb_~AI}b# zWc|syk!XOgMdGo&WP`}PY;Wm@>VFxeQovi3Kj@NEU&UI;7vg~P-;%jfING?7jP7l& z?Ac+26w?E$f03aJmI%2zu%0Y`uHrl1HfwAtibQW_k^YUXl+}$j9zmGuF3It!D_abP zJf^45;MM)VzLd%}02Z2$9kXm)tUk?&u1^X1hX@X4KDiN2`5?o8X@CZrRI5lv@_%9c ze}Aj=-!3DSiv#cvaFZkP|C50Y$`S(Lcq5?%U^tpeGn?Ohk#qAO>0-ivf;j+cmA*z) v+P{GSr~TGH4G4hSzW@7^sQ-_axuVC$X)Cl3!{!4%S;JD4SCfOwybAe0G`d None: + pytest_asyncio_tests = (item for item in items if is_async_test(item)) + session_scope_marker = pytest.mark.asyncio(loop_scope="session") + for async_test in pytest_asyncio_tests: + async_test.add_marker(session_scope_marker, append=False) + + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + +address_verification_api_key = "My Address Verification API Key" +print_mail_api_key = "My Print Mail API Key" + + +@pytest.fixture(scope="session") +def client(request: FixtureRequest) -> Iterator[PostGrid]: + strict = getattr(request, "param", True) + if not isinstance(strict, bool): + raise TypeError(f"Unexpected fixture parameter type {type(strict)}, expected {bool}") + + with PostGrid( + base_url=base_url, + address_verification_api_key=address_verification_api_key, + print_mail_api_key=print_mail_api_key, + _strict_response_validation=strict, + ) as client: + yield client + + +@pytest.fixture(scope="session") +async def async_client(request: FixtureRequest) -> AsyncIterator[AsyncPostGrid]: + strict = getattr(request, "param", True) + if not isinstance(strict, bool): + raise TypeError(f"Unexpected fixture parameter type {type(strict)}, expected {bool}") + + async with AsyncPostGrid( + base_url=base_url, + address_verification_api_key=address_verification_api_key, + print_mail_api_key=print_mail_api_key, + _strict_response_validation=strict, + ) as client: + yield client diff --git a/tests/sample_file.txt b/tests/sample_file.txt new file mode 100644 index 0000000..af5626b --- /dev/null +++ b/tests/sample_file.txt @@ -0,0 +1 @@ +Hello, world! diff --git a/tests/test_av.py b/tests/test_av.py deleted file mode 100644 index 6bd2083..0000000 --- a/tests/test_av.py +++ /dev/null @@ -1,132 +0,0 @@ -import postgrid -import os - - -def setup_module(): - # Use live key to test - postgrid.av_key = os.environ.get("AV_API_KEY") - - -def test_lookup_info(): - res = postgrid.LookupInfo.lookup_info() - assert isinstance(res, postgrid.LookupInfo) - assert res.status == "success" - assert hasattr(res, "data") - - -def test_verify_freeform(): - res = postgrid.Verification.verify("22-20 bay st, floor 11, toronto, on") - - assert isinstance(res, postgrid.Verification) - assert res.status == "success" - assert hasattr(res, "data") - assert res.data.country == "ca" - assert res.data.postal_or_zip == "M5J 2N8" - assert res.data.line1 == "22-20 BAY ST FLOOR 11" - - -def test_autocomplete_previews(): - res = postgrid.Autocomplete.previews( - partial_street="77 bloor st", country_filter="CA", prov_instead_of_pc=False - ) - - assert isinstance(res, postgrid.Autocomplete) - assert res.status == "success" - assert hasattr(res, "data") - assert len(res.data) >= 1 - - -def test_autocomplete_address(): - res = postgrid.Autocomplete.address("20 bay st", "Toronto") - - assert isinstance(res, postgrid.Autocomplete) - assert res.status == "success" - assert hasattr(res, "data") - assert res.data[0].address.address == "20 BAY ST" - assert res.data[0].address.pc == "M5H 4A6" - - -def test_batch_verify_address(): - addresses = { - "addresses": [ - { - "line1": "145 mulberry st ph d", - "city": "new york", - "province_or_state": "new york", - "postal_or_zip": "10013", - "country": "US", - }, - { - "line1": "90 canal st", - "city": "boston", - "province_or_state": "ma", - "country": "US", - }, - "22-20 bay st, floor 11, toronto on, ca", - ] - } - res = postgrid.Verification.batch_verify(addresses, False, True, False) - - assert isinstance(res, postgrid.Verification) - assert res.status == "success" - assert hasattr(res, "data") - assert len(res.data.results) == 3 - assert res.data.results[0].verified_address.line1 == "145 Mulberry St Ph D" - - -def test_verify_structured(): - address = { - "recipient": "Milk Bar", - "line1": "251 E 13th St", - "city": "new york city", - "province_or_state": "ny", - "country": "us", - } - res = postgrid.Verification.verify(address) - - assert isinstance(res, postgrid.Verification) - assert res.status == "success" - assert hasattr(res, "data") - assert res.data.country == "us" - assert res.data.postal_or_zip == "10003" - assert res.data.line1 == "251 E 13TH ST" - - -def test_suggest_address(): - address = { - "line1": "2220 lakeshore blvd", - "city": "toronto", - "province_or_state": "on", - "country": "ca", - } - res = postgrid.Suggestion.suggest_addresses(address, True, False, False) - - assert isinstance(res, postgrid.Suggestion) - assert res.status == "success" - assert hasattr(res, "data") - assert hasattr(res.data[0], "details") - assert res.data[0].city == "TORONTO" - assert res.data[0].country == "ca" - assert res.data[0].country_name == "CANADA" - assert res.data[0].postal_or_zip == "M8V 0E3" - assert res.data[0].line1 == "2220 LAKE SHORE BLVD W" - - -def test_parse_address(): - res = postgrid.Parse.parse_address("20 bay st toronto, on m9v4v1, canada") - - assert isinstance(res, postgrid.Parse) - assert res.status == "success" - assert hasattr(res, "data") - assert res.data.city == "toronto" - assert res.data.country == "canada" - assert res.data.road == "bay st" - - -def test_lookup_city(): - res = postgrid.CityState.lookup_city_state("m9v4v1") - - assert isinstance(res, postgrid.CityState) - assert res.status == "success" - assert hasattr(res, "data") - assert res.data[0].province_or_state == "ON" diff --git a/tests/test_cheque.py b/tests/test_cheque.py deleted file mode 100644 index 06402b8..0000000 --- a/tests/test_cheque.py +++ /dev/null @@ -1,75 +0,0 @@ -import postgrid -import os -import time - - -def setup_module(): - postgrid.pm_key = os.environ.get("PM_API_KEY") - - -def create_test_cheque(): - bank_account = postgrid.BankAccount.create( - bank_name="Test SDK", - bank_primary_line="47 Park Ave", - bank_secondary_line="New York NY 10013", - bank_country_code="US", - account_number="12345678", - routing_number="002301323", - signature_image=open("tests/assets/signature.png", "rb"), - ) - - return postgrid.Cheque.create( - to={ - "first_name": "Test", - "last_name": "Contact", - "company_name": "Test Company", - "address_line1": "20 Bay St, Toronto, ON M9V 4V1", - "country_code": "CA", - "job_title": "Test", - "metadata": {"test": [10, 20]}, - }, - from_={ - "company_name": "PostGrid", - "address_line1": "20 Bay St, Toronto, ON M9V 4V1", - "country_code": "CA", - }, - bank_account=bank_account.id, - amount=10000, - memo="Hello", - letter_html="Hello, {{to.firstName}}", - ) - - -def test_create(): - cheque = create_test_cheque() - - assert isinstance(cheque, postgrid.Cheque) - assert isinstance(cheque.letter_html, str) - - -def test_preview_generated(): - cheque = create_test_cheque() - - assert isinstance(cheque, postgrid.Cheque) - - time.sleep(10) - - cheque = postgrid.Cheque.retrieve(cheque.id) - - assert isinstance(cheque.url, str) - - -def test_delete(): - cheque = create_test_cheque() - res = postgrid.Cheque.delete(cheque.id) - - assert isinstance(res, postgrid.Cheque) - - -def test_delete_with_note(): - cheque = create_test_cheque() - note = "cancelled due to issues test" - res = postgrid.Cheque.delete_with_note(cheque.id, note) - - assert isinstance(res, postgrid.Cheque) - assert res.cancellation["note"] == note diff --git a/tests/test_client.py b/tests/test_client.py new file mode 100644 index 0000000..cc339ea --- /dev/null +++ b/tests/test_client.py @@ -0,0 +1,1896 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import gc +import os +import sys +import json +import time +import asyncio +import inspect +import subprocess +import tracemalloc +from typing import Any, Union, cast +from textwrap import dedent +from unittest import mock +from typing_extensions import Literal + +import httpx +import pytest +from respx import MockRouter +from pydantic import ValidationError + +from postgrid import PostGrid, AsyncPostGrid, APIResponseValidationError +from postgrid._types import Omit +from postgrid._utils import maybe_transform +from postgrid._models import BaseModel, FinalRequestOptions +from postgrid._constants import RAW_RESPONSE_HEADER +from postgrid._exceptions import APIStatusError, APITimeoutError, APIResponseValidationError +from postgrid._base_client import ( + DEFAULT_TIMEOUT, + HTTPX_DEFAULT_TIMEOUT, + BaseClient, + make_request_options, +) +from postgrid.types.address_verification_verify_params import StandardFreeformAddressInput + +from .utils import update_env + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") +address_verification_api_key = "My Address Verification API Key" +print_mail_api_key = "My Print Mail API Key" + + +def _get_params(client: BaseClient[Any, Any]) -> dict[str, str]: + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + url = httpx.URL(request.url) + return dict(url.params) + + +def _low_retry_timeout(*_args: Any, **_kwargs: Any) -> float: + return 0.1 + + +def _get_open_connections(client: PostGrid | AsyncPostGrid) -> int: + transport = client._client._transport + assert isinstance(transport, httpx.HTTPTransport) or isinstance(transport, httpx.AsyncHTTPTransport) + + pool = transport._pool + return len(pool._requests) + + +class TestPostGrid: + client = PostGrid( + base_url=base_url, + address_verification_api_key=address_verification_api_key, + print_mail_api_key=print_mail_api_key, + _strict_response_validation=True, + ) + + @pytest.mark.respx(base_url=base_url) + def test_raw_response(self, respx_mock: MockRouter) -> None: + respx_mock.post("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + + response = self.client.post("/foo", cast_to=httpx.Response) + assert response.status_code == 200 + assert isinstance(response, httpx.Response) + assert response.json() == {"foo": "bar"} + + @pytest.mark.respx(base_url=base_url) + def test_raw_response_for_binary(self, respx_mock: MockRouter) -> None: + respx_mock.post("/foo").mock( + return_value=httpx.Response(200, headers={"Content-Type": "application/binary"}, content='{"foo": "bar"}') + ) + + response = self.client.post("/foo", cast_to=httpx.Response) + assert response.status_code == 200 + assert isinstance(response, httpx.Response) + assert response.json() == {"foo": "bar"} + + def test_copy(self) -> None: + copied = self.client.copy() + assert id(copied) != id(self.client) + + copied = self.client.copy(address_verification_api_key="another My Address Verification API Key") + assert copied.address_verification_api_key == "another My Address Verification API Key" + assert self.client.address_verification_api_key == "My Address Verification API Key" + + copied = self.client.copy(print_mail_api_key="another My Print Mail API Key") + assert copied.print_mail_api_key == "another My Print Mail API Key" + assert self.client.print_mail_api_key == "My Print Mail API Key" + + def test_copy_default_options(self) -> None: + # options that have a default are overridden correctly + copied = self.client.copy(max_retries=7) + assert copied.max_retries == 7 + assert self.client.max_retries == 2 + + copied2 = copied.copy(max_retries=6) + assert copied2.max_retries == 6 + assert copied.max_retries == 7 + + # timeout + assert isinstance(self.client.timeout, httpx.Timeout) + copied = self.client.copy(timeout=None) + assert copied.timeout is None + assert isinstance(self.client.timeout, httpx.Timeout) + + def test_copy_default_headers(self) -> None: + client = PostGrid( + base_url=base_url, + address_verification_api_key=address_verification_api_key, + print_mail_api_key=print_mail_api_key, + _strict_response_validation=True, + default_headers={"X-Foo": "bar"}, + ) + assert client.default_headers["X-Foo"] == "bar" + + # does not override the already given value when not specified + copied = client.copy() + assert copied.default_headers["X-Foo"] == "bar" + + # merges already given headers + copied = client.copy(default_headers={"X-Bar": "stainless"}) + assert copied.default_headers["X-Foo"] == "bar" + assert copied.default_headers["X-Bar"] == "stainless" + + # uses new values for any already given headers + copied = client.copy(default_headers={"X-Foo": "stainless"}) + assert copied.default_headers["X-Foo"] == "stainless" + + # set_default_headers + + # completely overrides already set values + copied = client.copy(set_default_headers={}) + assert copied.default_headers.get("X-Foo") is None + + copied = client.copy(set_default_headers={"X-Bar": "Robert"}) + assert copied.default_headers["X-Bar"] == "Robert" + + with pytest.raises( + ValueError, + match="`default_headers` and `set_default_headers` arguments are mutually exclusive", + ): + client.copy(set_default_headers={}, default_headers={"X-Foo": "Bar"}) + + def test_copy_default_query(self) -> None: + client = PostGrid( + base_url=base_url, + address_verification_api_key=address_verification_api_key, + print_mail_api_key=print_mail_api_key, + _strict_response_validation=True, + default_query={"foo": "bar"}, + ) + assert _get_params(client)["foo"] == "bar" + + # does not override the already given value when not specified + copied = client.copy() + assert _get_params(copied)["foo"] == "bar" + + # merges already given params + copied = client.copy(default_query={"bar": "stainless"}) + params = _get_params(copied) + assert params["foo"] == "bar" + assert params["bar"] == "stainless" + + # uses new values for any already given headers + copied = client.copy(default_query={"foo": "stainless"}) + assert _get_params(copied)["foo"] == "stainless" + + # set_default_query + + # completely overrides already set values + copied = client.copy(set_default_query={}) + assert _get_params(copied) == {} + + copied = client.copy(set_default_query={"bar": "Robert"}) + assert _get_params(copied)["bar"] == "Robert" + + with pytest.raises( + ValueError, + # TODO: update + match="`default_query` and `set_default_query` arguments are mutually exclusive", + ): + client.copy(set_default_query={}, default_query={"foo": "Bar"}) + + def test_copy_signature(self) -> None: + # ensure the same parameters that can be passed to the client are defined in the `.copy()` method + init_signature = inspect.signature( + # mypy doesn't like that we access the `__init__` property. + self.client.__init__, # type: ignore[misc] + ) + copy_signature = inspect.signature(self.client.copy) + exclude_params = {"transport", "proxies", "_strict_response_validation"} + + for name in init_signature.parameters.keys(): + if name in exclude_params: + continue + + copy_param = copy_signature.parameters.get(name) + assert copy_param is not None, f"copy() signature is missing the {name} param" + + def test_copy_build_request(self) -> None: + options = FinalRequestOptions(method="get", url="/foo") + + def build_request(options: FinalRequestOptions) -> None: + client = self.client.copy() + client._build_request(options) + + # ensure that the machinery is warmed up before tracing starts. + build_request(options) + gc.collect() + + tracemalloc.start(1000) + + snapshot_before = tracemalloc.take_snapshot() + + ITERATIONS = 10 + for _ in range(ITERATIONS): + build_request(options) + + gc.collect() + snapshot_after = tracemalloc.take_snapshot() + + tracemalloc.stop() + + def add_leak(leaks: list[tracemalloc.StatisticDiff], diff: tracemalloc.StatisticDiff) -> None: + if diff.count == 0: + # Avoid false positives by considering only leaks (i.e. allocations that persist). + return + + if diff.count % ITERATIONS != 0: + # Avoid false positives by considering only leaks that appear per iteration. + return + + for frame in diff.traceback: + if any( + frame.filename.endswith(fragment) + for fragment in [ + # to_raw_response_wrapper leaks through the @functools.wraps() decorator. + # + # removing the decorator fixes the leak for reasons we don't understand. + "postgrid/_legacy_response.py", + "postgrid/_response.py", + # pydantic.BaseModel.model_dump || pydantic.BaseModel.dict leak memory for some reason. + "postgrid/_compat.py", + # Standard library leaks we don't care about. + "/logging/__init__.py", + ] + ): + return + + leaks.append(diff) + + leaks: list[tracemalloc.StatisticDiff] = [] + for diff in snapshot_after.compare_to(snapshot_before, "traceback"): + add_leak(leaks, diff) + if leaks: + for leak in leaks: + print("MEMORY LEAK:", leak) + for frame in leak.traceback: + print(frame) + raise AssertionError() + + def test_request_timeout(self) -> None: + request = self.client._build_request(FinalRequestOptions(method="get", url="/foo")) + timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore + assert timeout == DEFAULT_TIMEOUT + + request = self.client._build_request( + FinalRequestOptions(method="get", url="/foo", timeout=httpx.Timeout(100.0)) + ) + timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore + assert timeout == httpx.Timeout(100.0) + + def test_client_timeout_option(self) -> None: + client = PostGrid( + base_url=base_url, + address_verification_api_key=address_verification_api_key, + print_mail_api_key=print_mail_api_key, + _strict_response_validation=True, + timeout=httpx.Timeout(0), + ) + + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore + assert timeout == httpx.Timeout(0) + + def test_http_client_timeout_option(self) -> None: + # custom timeout given to the httpx client should be used + with httpx.Client(timeout=None) as http_client: + client = PostGrid( + base_url=base_url, + address_verification_api_key=address_verification_api_key, + print_mail_api_key=print_mail_api_key, + _strict_response_validation=True, + http_client=http_client, + ) + + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore + assert timeout == httpx.Timeout(None) + + # no timeout given to the httpx client should not use the httpx default + with httpx.Client() as http_client: + client = PostGrid( + base_url=base_url, + address_verification_api_key=address_verification_api_key, + print_mail_api_key=print_mail_api_key, + _strict_response_validation=True, + http_client=http_client, + ) + + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore + assert timeout == DEFAULT_TIMEOUT + + # explicitly passing the default timeout currently results in it being ignored + with httpx.Client(timeout=HTTPX_DEFAULT_TIMEOUT) as http_client: + client = PostGrid( + base_url=base_url, + address_verification_api_key=address_verification_api_key, + print_mail_api_key=print_mail_api_key, + _strict_response_validation=True, + http_client=http_client, + ) + + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore + assert timeout == DEFAULT_TIMEOUT # our default + + async def test_invalid_http_client(self) -> None: + with pytest.raises(TypeError, match="Invalid `http_client` arg"): + async with httpx.AsyncClient() as http_client: + PostGrid( + base_url=base_url, + address_verification_api_key=address_verification_api_key, + print_mail_api_key=print_mail_api_key, + _strict_response_validation=True, + http_client=cast(Any, http_client), + ) + + def test_default_headers_option(self) -> None: + client = PostGrid( + base_url=base_url, + address_verification_api_key=address_verification_api_key, + print_mail_api_key=print_mail_api_key, + _strict_response_validation=True, + default_headers={"X-Foo": "bar"}, + ) + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + assert request.headers.get("x-foo") == "bar" + assert request.headers.get("x-stainless-lang") == "python" + + client2 = PostGrid( + base_url=base_url, + address_verification_api_key=address_verification_api_key, + print_mail_api_key=print_mail_api_key, + _strict_response_validation=True, + default_headers={ + "X-Foo": "stainless", + "X-Stainless-Lang": "my-overriding-header", + }, + ) + request = client2._build_request(FinalRequestOptions(method="get", url="/foo")) + assert request.headers.get("x-foo") == "stainless" + assert request.headers.get("x-stainless-lang") == "my-overriding-header" + + def test_validate_headers(self) -> None: + client = PostGrid( + base_url=base_url, + address_verification_api_key=address_verification_api_key, + print_mail_api_key=print_mail_api_key, + _strict_response_validation=True, + ) + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + assert request.headers.get("X-API-Key") == address_verification_api_key + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + assert request.headers.get("X-API-Key") == print_mail_api_key + + with update_env( + **{ + "POSTGRID_ADDRESS_VERIFICATION_API_KEY": Omit(), + "POSTGRID_PRINT_MAIL_API_KEY": Omit(), + } + ): + client2 = PostGrid( + base_url=base_url, + address_verification_api_key=None, + print_mail_api_key=None, + _strict_response_validation=True, + ) + + with pytest.raises( + TypeError, + match="Could not resolve authentication method. Expected either address_verification_api_key or print_mail_api_key to be set. Or for one of the `X-API-Key` or `X-API-Key` headers to be explicitly omitted", + ): + client2._build_request(FinalRequestOptions(method="get", url="/foo")) + + request2 = client2._build_request(FinalRequestOptions(method="get", url="/foo", headers={"X-API-Key": Omit()})) + assert request2.headers.get("X-API-Key") is None + request2 = client2._build_request(FinalRequestOptions(method="get", url="/foo", headers={"X-API-Key": Omit()})) + assert request2.headers.get("X-API-Key") is None + + def test_default_query_option(self) -> None: + client = PostGrid( + base_url=base_url, + address_verification_api_key=address_verification_api_key, + print_mail_api_key=print_mail_api_key, + _strict_response_validation=True, + default_query={"query_param": "bar"}, + ) + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + url = httpx.URL(request.url) + assert dict(url.params) == {"query_param": "bar"} + + request = client._build_request( + FinalRequestOptions( + method="get", + url="/foo", + params={"foo": "baz", "query_param": "overridden"}, + ) + ) + url = httpx.URL(request.url) + assert dict(url.params) == {"foo": "baz", "query_param": "overridden"} + + def test_request_extra_json(self) -> None: + request = self.client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + json_data={"foo": "bar"}, + extra_json={"baz": False}, + ), + ) + data = json.loads(request.content.decode("utf-8")) + assert data == {"foo": "bar", "baz": False} + + request = self.client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + extra_json={"baz": False}, + ), + ) + data = json.loads(request.content.decode("utf-8")) + assert data == {"baz": False} + + # `extra_json` takes priority over `json_data` when keys clash + request = self.client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + json_data={"foo": "bar", "baz": True}, + extra_json={"baz": None}, + ), + ) + data = json.loads(request.content.decode("utf-8")) + assert data == {"foo": "bar", "baz": None} + + def test_request_extra_headers(self) -> None: + request = self.client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + **make_request_options(extra_headers={"X-Foo": "Foo"}), + ), + ) + assert request.headers.get("X-Foo") == "Foo" + + # `extra_headers` takes priority over `default_headers` when keys clash + request = self.client.with_options(default_headers={"X-Bar": "true"})._build_request( + FinalRequestOptions( + method="post", + url="/foo", + **make_request_options( + extra_headers={"X-Bar": "false"}, + ), + ), + ) + assert request.headers.get("X-Bar") == "false" + + def test_request_extra_query(self) -> None: + request = self.client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + **make_request_options( + extra_query={"my_query_param": "Foo"}, + ), + ), + ) + params = dict(request.url.params) + assert params == {"my_query_param": "Foo"} + + # if both `query` and `extra_query` are given, they are merged + request = self.client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + **make_request_options( + query={"bar": "1"}, + extra_query={"foo": "2"}, + ), + ), + ) + params = dict(request.url.params) + assert params == {"bar": "1", "foo": "2"} + + # `extra_query` takes priority over `query` when keys clash + request = self.client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + **make_request_options( + query={"foo": "1"}, + extra_query={"foo": "2"}, + ), + ), + ) + params = dict(request.url.params) + assert params == {"foo": "2"} + + def test_multipart_repeating_array(self, client: PostGrid) -> None: + request = client._build_request( + FinalRequestOptions.construct( + method="get", + url="/foo", + headers={"Content-Type": "multipart/form-data; boundary=6b7ba517decee4a450543ea6ae821c82"}, + json_data={"array": ["foo", "bar"]}, + files=[("foo.txt", b"hello world")], + ) + ) + + assert request.read().split(b"\r\n") == [ + b"--6b7ba517decee4a450543ea6ae821c82", + b'Content-Disposition: form-data; name="array[]"', + b"", + b"foo", + b"--6b7ba517decee4a450543ea6ae821c82", + b'Content-Disposition: form-data; name="array[]"', + b"", + b"bar", + b"--6b7ba517decee4a450543ea6ae821c82", + b'Content-Disposition: form-data; name="foo.txt"; filename="upload"', + b"Content-Type: application/octet-stream", + b"", + b"hello world", + b"--6b7ba517decee4a450543ea6ae821c82--", + b"", + ] + + @pytest.mark.respx(base_url=base_url) + def test_basic_union_response(self, respx_mock: MockRouter) -> None: + class Model1(BaseModel): + name: str + + class Model2(BaseModel): + foo: str + + respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + + response = self.client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) + assert isinstance(response, Model2) + assert response.foo == "bar" + + @pytest.mark.respx(base_url=base_url) + def test_union_response_different_types(self, respx_mock: MockRouter) -> None: + """Union of objects with the same field name using a different type""" + + class Model1(BaseModel): + foo: int + + class Model2(BaseModel): + foo: str + + respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + + response = self.client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) + assert isinstance(response, Model2) + assert response.foo == "bar" + + respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": 1})) + + response = self.client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) + assert isinstance(response, Model1) + assert response.foo == 1 + + @pytest.mark.respx(base_url=base_url) + def test_non_application_json_content_type_for_json_data(self, respx_mock: MockRouter) -> None: + """ + Response that sets Content-Type to something other than application/json but returns json data + """ + + class Model(BaseModel): + foo: int + + respx_mock.get("/foo").mock( + return_value=httpx.Response( + 200, + content=json.dumps({"foo": 2}), + headers={"Content-Type": "application/text"}, + ) + ) + + response = self.client.get("/foo", cast_to=Model) + assert isinstance(response, Model) + assert response.foo == 2 + + def test_base_url_setter(self) -> None: + client = PostGrid( + base_url="https://example.com/from_init", + address_verification_api_key=address_verification_api_key, + print_mail_api_key=print_mail_api_key, + _strict_response_validation=True, + ) + assert client.base_url == "https://example.com/from_init/" + + client.base_url = "https://example.com/from_setter" # type: ignore[assignment] + + assert client.base_url == "https://example.com/from_setter/" + + def test_base_url_env(self) -> None: + with update_env(POST_GRID_BASE_URL="http://localhost:5000/from/env"): + client = PostGrid( + address_verification_api_key=address_verification_api_key, + print_mail_api_key=print_mail_api_key, + _strict_response_validation=True, + ) + assert client.base_url == "http://localhost:5000/from/env/" + + @pytest.mark.parametrize( + "client", + [ + PostGrid( + base_url="http://localhost:5000/custom/path/", + address_verification_api_key=address_verification_api_key, + print_mail_api_key=print_mail_api_key, + _strict_response_validation=True, + ), + PostGrid( + base_url="http://localhost:5000/custom/path/", + address_verification_api_key=address_verification_api_key, + print_mail_api_key=print_mail_api_key, + _strict_response_validation=True, + http_client=httpx.Client(), + ), + ], + ids=["standard", "custom http client"], + ) + def test_base_url_trailing_slash(self, client: PostGrid) -> None: + request = client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + json_data={"foo": "bar"}, + ), + ) + assert request.url == "http://localhost:5000/custom/path/foo" + + @pytest.mark.parametrize( + "client", + [ + PostGrid( + base_url="http://localhost:5000/custom/path/", + address_verification_api_key=address_verification_api_key, + print_mail_api_key=print_mail_api_key, + _strict_response_validation=True, + ), + PostGrid( + base_url="http://localhost:5000/custom/path/", + address_verification_api_key=address_verification_api_key, + print_mail_api_key=print_mail_api_key, + _strict_response_validation=True, + http_client=httpx.Client(), + ), + ], + ids=["standard", "custom http client"], + ) + def test_base_url_no_trailing_slash(self, client: PostGrid) -> None: + request = client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + json_data={"foo": "bar"}, + ), + ) + assert request.url == "http://localhost:5000/custom/path/foo" + + @pytest.mark.parametrize( + "client", + [ + PostGrid( + base_url="http://localhost:5000/custom/path/", + address_verification_api_key=address_verification_api_key, + print_mail_api_key=print_mail_api_key, + _strict_response_validation=True, + ), + PostGrid( + base_url="http://localhost:5000/custom/path/", + address_verification_api_key=address_verification_api_key, + print_mail_api_key=print_mail_api_key, + _strict_response_validation=True, + http_client=httpx.Client(), + ), + ], + ids=["standard", "custom http client"], + ) + def test_absolute_request_url(self, client: PostGrid) -> None: + request = client._build_request( + FinalRequestOptions( + method="post", + url="https://myapi.com/foo", + json_data={"foo": "bar"}, + ), + ) + assert request.url == "https://myapi.com/foo" + + def test_copied_client_does_not_close_http(self) -> None: + client = PostGrid( + base_url=base_url, + address_verification_api_key=address_verification_api_key, + print_mail_api_key=print_mail_api_key, + _strict_response_validation=True, + ) + assert not client.is_closed() + + copied = client.copy() + assert copied is not client + + del copied + + assert not client.is_closed() + + def test_client_context_manager(self) -> None: + client = PostGrid( + base_url=base_url, + address_verification_api_key=address_verification_api_key, + print_mail_api_key=print_mail_api_key, + _strict_response_validation=True, + ) + with client as c2: + assert c2 is client + assert not c2.is_closed() + assert not client.is_closed() + assert client.is_closed() + + @pytest.mark.respx(base_url=base_url) + def test_client_response_validation_error(self, respx_mock: MockRouter) -> None: + class Model(BaseModel): + foo: str + + respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": {"invalid": True}})) + + with pytest.raises(APIResponseValidationError) as exc: + self.client.get("/foo", cast_to=Model) + + assert isinstance(exc.value.__cause__, ValidationError) + + def test_client_max_retries_validation(self) -> None: + with pytest.raises(TypeError, match=r"max_retries cannot be None"): + PostGrid( + base_url=base_url, + address_verification_api_key=address_verification_api_key, + print_mail_api_key=print_mail_api_key, + _strict_response_validation=True, + max_retries=cast(Any, None), + ) + + @pytest.mark.respx(base_url=base_url) + def test_received_text_for_expected_json(self, respx_mock: MockRouter) -> None: + class Model(BaseModel): + name: str + + respx_mock.get("/foo").mock(return_value=httpx.Response(200, text="my-custom-format")) + + strict_client = PostGrid( + base_url=base_url, + address_verification_api_key=address_verification_api_key, + print_mail_api_key=print_mail_api_key, + _strict_response_validation=True, + ) + + with pytest.raises(APIResponseValidationError): + strict_client.get("/foo", cast_to=Model) + + client = PostGrid( + base_url=base_url, + address_verification_api_key=address_verification_api_key, + print_mail_api_key=print_mail_api_key, + _strict_response_validation=False, + ) + + response = client.get("/foo", cast_to=Model) + assert isinstance(response, str) # type: ignore[unreachable] + + @pytest.mark.parametrize( + "remaining_retries,retry_after,timeout", + [ + [3, "20", 20], + [3, "0", 0.5], + [3, "-10", 0.5], + [3, "60", 60], + [3, "61", 0.5], + [3, "Fri, 29 Sep 2023 16:26:57 GMT", 20], + [3, "Fri, 29 Sep 2023 16:26:37 GMT", 0.5], + [3, "Fri, 29 Sep 2023 16:26:27 GMT", 0.5], + [3, "Fri, 29 Sep 2023 16:27:37 GMT", 60], + [3, "Fri, 29 Sep 2023 16:27:38 GMT", 0.5], + [3, "99999999999999999999999999999999999", 0.5], + [3, "Zun, 29 Sep 2023 16:26:27 GMT", 0.5], + [3, "", 0.5], + [2, "", 0.5 * 2.0], + [1, "", 0.5 * 4.0], + [-1100, "", 8], # test large number potentially overflowing + ], + ) + @mock.patch("time.time", mock.MagicMock(return_value=1696004797)) + def test_parse_retry_after_header(self, remaining_retries: int, retry_after: str, timeout: float) -> None: + client = PostGrid( + base_url=base_url, + address_verification_api_key=address_verification_api_key, + print_mail_api_key=print_mail_api_key, + _strict_response_validation=True, + ) + + headers = httpx.Headers({"retry-after": retry_after}) + options = FinalRequestOptions(method="get", url="/foo", max_retries=3) + calculated = client._calculate_retry_timeout(remaining_retries, options, headers) + assert calculated == pytest.approx(timeout, 0.5 * 0.875) # pyright: ignore[reportUnknownMemberType] + + @mock.patch("postgrid._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) + @pytest.mark.respx(base_url=base_url) + def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter) -> None: + respx_mock.post("/v1/addver/verifications").mock(side_effect=httpx.TimeoutException("Test timeout error")) + + with pytest.raises(APITimeoutError): + self.client.post( + "/v1/addver/verifications", + body=cast(object, maybe_transform(dict(address="address"), StandardFreeformAddressInput)), + cast_to=httpx.Response, + options={"headers": {RAW_RESPONSE_HEADER: "stream"}}, + ) + + assert _get_open_connections(self.client) == 0 + + @mock.patch("postgrid._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) + @pytest.mark.respx(base_url=base_url) + def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter) -> None: + respx_mock.post("/v1/addver/verifications").mock(return_value=httpx.Response(500)) + + with pytest.raises(APIStatusError): + self.client.post( + "/v1/addver/verifications", + body=cast(object, maybe_transform(dict(address="address"), StandardFreeformAddressInput)), + cast_to=httpx.Response, + options={"headers": {RAW_RESPONSE_HEADER: "stream"}}, + ) + + assert _get_open_connections(self.client) == 0 + + @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) + @mock.patch("postgrid._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) + @pytest.mark.respx(base_url=base_url) + @pytest.mark.parametrize("failure_mode", ["status", "exception"]) + def test_retries_taken( + self, + client: PostGrid, + failures_before_success: int, + failure_mode: Literal["status", "exception"], + respx_mock: MockRouter, + ) -> None: + client = client.with_options(max_retries=4) + + nb_retries = 0 + + def retry_handler(_request: httpx.Request) -> httpx.Response: + nonlocal nb_retries + if nb_retries < failures_before_success: + nb_retries += 1 + if failure_mode == "exception": + raise RuntimeError("oops") + return httpx.Response(500) + return httpx.Response(200) + + respx_mock.post("/v1/addver/verifications").mock(side_effect=retry_handler) + + response = client.address_verification.with_raw_response.verify(address="address") + + assert response.retries_taken == failures_before_success + assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success + + @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) + @mock.patch("postgrid._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) + @pytest.mark.respx(base_url=base_url) + def test_omit_retry_count_header( + self, client: PostGrid, failures_before_success: int, respx_mock: MockRouter + ) -> None: + client = client.with_options(max_retries=4) + + nb_retries = 0 + + def retry_handler(_request: httpx.Request) -> httpx.Response: + nonlocal nb_retries + if nb_retries < failures_before_success: + nb_retries += 1 + return httpx.Response(500) + return httpx.Response(200) + + respx_mock.post("/v1/addver/verifications").mock(side_effect=retry_handler) + + response = client.address_verification.with_raw_response.verify( + address="address", extra_headers={"x-stainless-retry-count": Omit()} + ) + + assert len(response.http_request.headers.get_list("x-stainless-retry-count")) == 0 + + @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) + @mock.patch("postgrid._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) + @pytest.mark.respx(base_url=base_url) + def test_overwrite_retry_count_header( + self, client: PostGrid, failures_before_success: int, respx_mock: MockRouter + ) -> None: + client = client.with_options(max_retries=4) + + nb_retries = 0 + + def retry_handler(_request: httpx.Request) -> httpx.Response: + nonlocal nb_retries + if nb_retries < failures_before_success: + nb_retries += 1 + return httpx.Response(500) + return httpx.Response(200) + + respx_mock.post("/v1/addver/verifications").mock(side_effect=retry_handler) + + response = client.address_verification.with_raw_response.verify( + address="address", extra_headers={"x-stainless-retry-count": "42"} + ) + + assert response.http_request.headers.get("x-stainless-retry-count") == "42" + + +class TestAsyncPostGrid: + client = AsyncPostGrid( + base_url=base_url, + address_verification_api_key=address_verification_api_key, + print_mail_api_key=print_mail_api_key, + _strict_response_validation=True, + ) + + @pytest.mark.respx(base_url=base_url) + @pytest.mark.asyncio + async def test_raw_response(self, respx_mock: MockRouter) -> None: + respx_mock.post("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + + response = await self.client.post("/foo", cast_to=httpx.Response) + assert response.status_code == 200 + assert isinstance(response, httpx.Response) + assert response.json() == {"foo": "bar"} + + @pytest.mark.respx(base_url=base_url) + @pytest.mark.asyncio + async def test_raw_response_for_binary(self, respx_mock: MockRouter) -> None: + respx_mock.post("/foo").mock( + return_value=httpx.Response(200, headers={"Content-Type": "application/binary"}, content='{"foo": "bar"}') + ) + + response = await self.client.post("/foo", cast_to=httpx.Response) + assert response.status_code == 200 + assert isinstance(response, httpx.Response) + assert response.json() == {"foo": "bar"} + + def test_copy(self) -> None: + copied = self.client.copy() + assert id(copied) != id(self.client) + + copied = self.client.copy(address_verification_api_key="another My Address Verification API Key") + assert copied.address_verification_api_key == "another My Address Verification API Key" + assert self.client.address_verification_api_key == "My Address Verification API Key" + + copied = self.client.copy(print_mail_api_key="another My Print Mail API Key") + assert copied.print_mail_api_key == "another My Print Mail API Key" + assert self.client.print_mail_api_key == "My Print Mail API Key" + + def test_copy_default_options(self) -> None: + # options that have a default are overridden correctly + copied = self.client.copy(max_retries=7) + assert copied.max_retries == 7 + assert self.client.max_retries == 2 + + copied2 = copied.copy(max_retries=6) + assert copied2.max_retries == 6 + assert copied.max_retries == 7 + + # timeout + assert isinstance(self.client.timeout, httpx.Timeout) + copied = self.client.copy(timeout=None) + assert copied.timeout is None + assert isinstance(self.client.timeout, httpx.Timeout) + + def test_copy_default_headers(self) -> None: + client = AsyncPostGrid( + base_url=base_url, + address_verification_api_key=address_verification_api_key, + print_mail_api_key=print_mail_api_key, + _strict_response_validation=True, + default_headers={"X-Foo": "bar"}, + ) + assert client.default_headers["X-Foo"] == "bar" + + # does not override the already given value when not specified + copied = client.copy() + assert copied.default_headers["X-Foo"] == "bar" + + # merges already given headers + copied = client.copy(default_headers={"X-Bar": "stainless"}) + assert copied.default_headers["X-Foo"] == "bar" + assert copied.default_headers["X-Bar"] == "stainless" + + # uses new values for any already given headers + copied = client.copy(default_headers={"X-Foo": "stainless"}) + assert copied.default_headers["X-Foo"] == "stainless" + + # set_default_headers + + # completely overrides already set values + copied = client.copy(set_default_headers={}) + assert copied.default_headers.get("X-Foo") is None + + copied = client.copy(set_default_headers={"X-Bar": "Robert"}) + assert copied.default_headers["X-Bar"] == "Robert" + + with pytest.raises( + ValueError, + match="`default_headers` and `set_default_headers` arguments are mutually exclusive", + ): + client.copy(set_default_headers={}, default_headers={"X-Foo": "Bar"}) + + def test_copy_default_query(self) -> None: + client = AsyncPostGrid( + base_url=base_url, + address_verification_api_key=address_verification_api_key, + print_mail_api_key=print_mail_api_key, + _strict_response_validation=True, + default_query={"foo": "bar"}, + ) + assert _get_params(client)["foo"] == "bar" + + # does not override the already given value when not specified + copied = client.copy() + assert _get_params(copied)["foo"] == "bar" + + # merges already given params + copied = client.copy(default_query={"bar": "stainless"}) + params = _get_params(copied) + assert params["foo"] == "bar" + assert params["bar"] == "stainless" + + # uses new values for any already given headers + copied = client.copy(default_query={"foo": "stainless"}) + assert _get_params(copied)["foo"] == "stainless" + + # set_default_query + + # completely overrides already set values + copied = client.copy(set_default_query={}) + assert _get_params(copied) == {} + + copied = client.copy(set_default_query={"bar": "Robert"}) + assert _get_params(copied)["bar"] == "Robert" + + with pytest.raises( + ValueError, + # TODO: update + match="`default_query` and `set_default_query` arguments are mutually exclusive", + ): + client.copy(set_default_query={}, default_query={"foo": "Bar"}) + + def test_copy_signature(self) -> None: + # ensure the same parameters that can be passed to the client are defined in the `.copy()` method + init_signature = inspect.signature( + # mypy doesn't like that we access the `__init__` property. + self.client.__init__, # type: ignore[misc] + ) + copy_signature = inspect.signature(self.client.copy) + exclude_params = {"transport", "proxies", "_strict_response_validation"} + + for name in init_signature.parameters.keys(): + if name in exclude_params: + continue + + copy_param = copy_signature.parameters.get(name) + assert copy_param is not None, f"copy() signature is missing the {name} param" + + def test_copy_build_request(self) -> None: + options = FinalRequestOptions(method="get", url="/foo") + + def build_request(options: FinalRequestOptions) -> None: + client = self.client.copy() + client._build_request(options) + + # ensure that the machinery is warmed up before tracing starts. + build_request(options) + gc.collect() + + tracemalloc.start(1000) + + snapshot_before = tracemalloc.take_snapshot() + + ITERATIONS = 10 + for _ in range(ITERATIONS): + build_request(options) + + gc.collect() + snapshot_after = tracemalloc.take_snapshot() + + tracemalloc.stop() + + def add_leak(leaks: list[tracemalloc.StatisticDiff], diff: tracemalloc.StatisticDiff) -> None: + if diff.count == 0: + # Avoid false positives by considering only leaks (i.e. allocations that persist). + return + + if diff.count % ITERATIONS != 0: + # Avoid false positives by considering only leaks that appear per iteration. + return + + for frame in diff.traceback: + if any( + frame.filename.endswith(fragment) + for fragment in [ + # to_raw_response_wrapper leaks through the @functools.wraps() decorator. + # + # removing the decorator fixes the leak for reasons we don't understand. + "postgrid/_legacy_response.py", + "postgrid/_response.py", + # pydantic.BaseModel.model_dump || pydantic.BaseModel.dict leak memory for some reason. + "postgrid/_compat.py", + # Standard library leaks we don't care about. + "/logging/__init__.py", + ] + ): + return + + leaks.append(diff) + + leaks: list[tracemalloc.StatisticDiff] = [] + for diff in snapshot_after.compare_to(snapshot_before, "traceback"): + add_leak(leaks, diff) + if leaks: + for leak in leaks: + print("MEMORY LEAK:", leak) + for frame in leak.traceback: + print(frame) + raise AssertionError() + + async def test_request_timeout(self) -> None: + request = self.client._build_request(FinalRequestOptions(method="get", url="/foo")) + timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore + assert timeout == DEFAULT_TIMEOUT + + request = self.client._build_request( + FinalRequestOptions(method="get", url="/foo", timeout=httpx.Timeout(100.0)) + ) + timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore + assert timeout == httpx.Timeout(100.0) + + async def test_client_timeout_option(self) -> None: + client = AsyncPostGrid( + base_url=base_url, + address_verification_api_key=address_verification_api_key, + print_mail_api_key=print_mail_api_key, + _strict_response_validation=True, + timeout=httpx.Timeout(0), + ) + + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore + assert timeout == httpx.Timeout(0) + + async def test_http_client_timeout_option(self) -> None: + # custom timeout given to the httpx client should be used + async with httpx.AsyncClient(timeout=None) as http_client: + client = AsyncPostGrid( + base_url=base_url, + address_verification_api_key=address_verification_api_key, + print_mail_api_key=print_mail_api_key, + _strict_response_validation=True, + http_client=http_client, + ) + + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore + assert timeout == httpx.Timeout(None) + + # no timeout given to the httpx client should not use the httpx default + async with httpx.AsyncClient() as http_client: + client = AsyncPostGrid( + base_url=base_url, + address_verification_api_key=address_verification_api_key, + print_mail_api_key=print_mail_api_key, + _strict_response_validation=True, + http_client=http_client, + ) + + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore + assert timeout == DEFAULT_TIMEOUT + + # explicitly passing the default timeout currently results in it being ignored + async with httpx.AsyncClient(timeout=HTTPX_DEFAULT_TIMEOUT) as http_client: + client = AsyncPostGrid( + base_url=base_url, + address_verification_api_key=address_verification_api_key, + print_mail_api_key=print_mail_api_key, + _strict_response_validation=True, + http_client=http_client, + ) + + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore + assert timeout == DEFAULT_TIMEOUT # our default + + def test_invalid_http_client(self) -> None: + with pytest.raises(TypeError, match="Invalid `http_client` arg"): + with httpx.Client() as http_client: + AsyncPostGrid( + base_url=base_url, + address_verification_api_key=address_verification_api_key, + print_mail_api_key=print_mail_api_key, + _strict_response_validation=True, + http_client=cast(Any, http_client), + ) + + def test_default_headers_option(self) -> None: + client = AsyncPostGrid( + base_url=base_url, + address_verification_api_key=address_verification_api_key, + print_mail_api_key=print_mail_api_key, + _strict_response_validation=True, + default_headers={"X-Foo": "bar"}, + ) + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + assert request.headers.get("x-foo") == "bar" + assert request.headers.get("x-stainless-lang") == "python" + + client2 = AsyncPostGrid( + base_url=base_url, + address_verification_api_key=address_verification_api_key, + print_mail_api_key=print_mail_api_key, + _strict_response_validation=True, + default_headers={ + "X-Foo": "stainless", + "X-Stainless-Lang": "my-overriding-header", + }, + ) + request = client2._build_request(FinalRequestOptions(method="get", url="/foo")) + assert request.headers.get("x-foo") == "stainless" + assert request.headers.get("x-stainless-lang") == "my-overriding-header" + + def test_validate_headers(self) -> None: + client = AsyncPostGrid( + base_url=base_url, + address_verification_api_key=address_verification_api_key, + print_mail_api_key=print_mail_api_key, + _strict_response_validation=True, + ) + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + assert request.headers.get("X-API-Key") == address_verification_api_key + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + assert request.headers.get("X-API-Key") == print_mail_api_key + + with update_env( + **{ + "POSTGRID_ADDRESS_VERIFICATION_API_KEY": Omit(), + "POSTGRID_PRINT_MAIL_API_KEY": Omit(), + } + ): + client2 = AsyncPostGrid( + base_url=base_url, + address_verification_api_key=None, + print_mail_api_key=None, + _strict_response_validation=True, + ) + + with pytest.raises( + TypeError, + match="Could not resolve authentication method. Expected either address_verification_api_key or print_mail_api_key to be set. Or for one of the `X-API-Key` or `X-API-Key` headers to be explicitly omitted", + ): + client2._build_request(FinalRequestOptions(method="get", url="/foo")) + + request2 = client2._build_request(FinalRequestOptions(method="get", url="/foo", headers={"X-API-Key": Omit()})) + assert request2.headers.get("X-API-Key") is None + request2 = client2._build_request(FinalRequestOptions(method="get", url="/foo", headers={"X-API-Key": Omit()})) + assert request2.headers.get("X-API-Key") is None + + def test_default_query_option(self) -> None: + client = AsyncPostGrid( + base_url=base_url, + address_verification_api_key=address_verification_api_key, + print_mail_api_key=print_mail_api_key, + _strict_response_validation=True, + default_query={"query_param": "bar"}, + ) + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + url = httpx.URL(request.url) + assert dict(url.params) == {"query_param": "bar"} + + request = client._build_request( + FinalRequestOptions( + method="get", + url="/foo", + params={"foo": "baz", "query_param": "overridden"}, + ) + ) + url = httpx.URL(request.url) + assert dict(url.params) == {"foo": "baz", "query_param": "overridden"} + + def test_request_extra_json(self) -> None: + request = self.client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + json_data={"foo": "bar"}, + extra_json={"baz": False}, + ), + ) + data = json.loads(request.content.decode("utf-8")) + assert data == {"foo": "bar", "baz": False} + + request = self.client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + extra_json={"baz": False}, + ), + ) + data = json.loads(request.content.decode("utf-8")) + assert data == {"baz": False} + + # `extra_json` takes priority over `json_data` when keys clash + request = self.client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + json_data={"foo": "bar", "baz": True}, + extra_json={"baz": None}, + ), + ) + data = json.loads(request.content.decode("utf-8")) + assert data == {"foo": "bar", "baz": None} + + def test_request_extra_headers(self) -> None: + request = self.client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + **make_request_options(extra_headers={"X-Foo": "Foo"}), + ), + ) + assert request.headers.get("X-Foo") == "Foo" + + # `extra_headers` takes priority over `default_headers` when keys clash + request = self.client.with_options(default_headers={"X-Bar": "true"})._build_request( + FinalRequestOptions( + method="post", + url="/foo", + **make_request_options( + extra_headers={"X-Bar": "false"}, + ), + ), + ) + assert request.headers.get("X-Bar") == "false" + + def test_request_extra_query(self) -> None: + request = self.client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + **make_request_options( + extra_query={"my_query_param": "Foo"}, + ), + ), + ) + params = dict(request.url.params) + assert params == {"my_query_param": "Foo"} + + # if both `query` and `extra_query` are given, they are merged + request = self.client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + **make_request_options( + query={"bar": "1"}, + extra_query={"foo": "2"}, + ), + ), + ) + params = dict(request.url.params) + assert params == {"bar": "1", "foo": "2"} + + # `extra_query` takes priority over `query` when keys clash + request = self.client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + **make_request_options( + query={"foo": "1"}, + extra_query={"foo": "2"}, + ), + ), + ) + params = dict(request.url.params) + assert params == {"foo": "2"} + + def test_multipart_repeating_array(self, async_client: AsyncPostGrid) -> None: + request = async_client._build_request( + FinalRequestOptions.construct( + method="get", + url="/foo", + headers={"Content-Type": "multipart/form-data; boundary=6b7ba517decee4a450543ea6ae821c82"}, + json_data={"array": ["foo", "bar"]}, + files=[("foo.txt", b"hello world")], + ) + ) + + assert request.read().split(b"\r\n") == [ + b"--6b7ba517decee4a450543ea6ae821c82", + b'Content-Disposition: form-data; name="array[]"', + b"", + b"foo", + b"--6b7ba517decee4a450543ea6ae821c82", + b'Content-Disposition: form-data; name="array[]"', + b"", + b"bar", + b"--6b7ba517decee4a450543ea6ae821c82", + b'Content-Disposition: form-data; name="foo.txt"; filename="upload"', + b"Content-Type: application/octet-stream", + b"", + b"hello world", + b"--6b7ba517decee4a450543ea6ae821c82--", + b"", + ] + + @pytest.mark.respx(base_url=base_url) + async def test_basic_union_response(self, respx_mock: MockRouter) -> None: + class Model1(BaseModel): + name: str + + class Model2(BaseModel): + foo: str + + respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + + response = await self.client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) + assert isinstance(response, Model2) + assert response.foo == "bar" + + @pytest.mark.respx(base_url=base_url) + async def test_union_response_different_types(self, respx_mock: MockRouter) -> None: + """Union of objects with the same field name using a different type""" + + class Model1(BaseModel): + foo: int + + class Model2(BaseModel): + foo: str + + respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + + response = await self.client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) + assert isinstance(response, Model2) + assert response.foo == "bar" + + respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": 1})) + + response = await self.client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) + assert isinstance(response, Model1) + assert response.foo == 1 + + @pytest.mark.respx(base_url=base_url) + async def test_non_application_json_content_type_for_json_data(self, respx_mock: MockRouter) -> None: + """ + Response that sets Content-Type to something other than application/json but returns json data + """ + + class Model(BaseModel): + foo: int + + respx_mock.get("/foo").mock( + return_value=httpx.Response( + 200, + content=json.dumps({"foo": 2}), + headers={"Content-Type": "application/text"}, + ) + ) + + response = await self.client.get("/foo", cast_to=Model) + assert isinstance(response, Model) + assert response.foo == 2 + + def test_base_url_setter(self) -> None: + client = AsyncPostGrid( + base_url="https://example.com/from_init", + address_verification_api_key=address_verification_api_key, + print_mail_api_key=print_mail_api_key, + _strict_response_validation=True, + ) + assert client.base_url == "https://example.com/from_init/" + + client.base_url = "https://example.com/from_setter" # type: ignore[assignment] + + assert client.base_url == "https://example.com/from_setter/" + + def test_base_url_env(self) -> None: + with update_env(POST_GRID_BASE_URL="http://localhost:5000/from/env"): + client = AsyncPostGrid( + address_verification_api_key=address_verification_api_key, + print_mail_api_key=print_mail_api_key, + _strict_response_validation=True, + ) + assert client.base_url == "http://localhost:5000/from/env/" + + @pytest.mark.parametrize( + "client", + [ + AsyncPostGrid( + base_url="http://localhost:5000/custom/path/", + address_verification_api_key=address_verification_api_key, + print_mail_api_key=print_mail_api_key, + _strict_response_validation=True, + ), + AsyncPostGrid( + base_url="http://localhost:5000/custom/path/", + address_verification_api_key=address_verification_api_key, + print_mail_api_key=print_mail_api_key, + _strict_response_validation=True, + http_client=httpx.AsyncClient(), + ), + ], + ids=["standard", "custom http client"], + ) + def test_base_url_trailing_slash(self, client: AsyncPostGrid) -> None: + request = client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + json_data={"foo": "bar"}, + ), + ) + assert request.url == "http://localhost:5000/custom/path/foo" + + @pytest.mark.parametrize( + "client", + [ + AsyncPostGrid( + base_url="http://localhost:5000/custom/path/", + address_verification_api_key=address_verification_api_key, + print_mail_api_key=print_mail_api_key, + _strict_response_validation=True, + ), + AsyncPostGrid( + base_url="http://localhost:5000/custom/path/", + address_verification_api_key=address_verification_api_key, + print_mail_api_key=print_mail_api_key, + _strict_response_validation=True, + http_client=httpx.AsyncClient(), + ), + ], + ids=["standard", "custom http client"], + ) + def test_base_url_no_trailing_slash(self, client: AsyncPostGrid) -> None: + request = client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + json_data={"foo": "bar"}, + ), + ) + assert request.url == "http://localhost:5000/custom/path/foo" + + @pytest.mark.parametrize( + "client", + [ + AsyncPostGrid( + base_url="http://localhost:5000/custom/path/", + address_verification_api_key=address_verification_api_key, + print_mail_api_key=print_mail_api_key, + _strict_response_validation=True, + ), + AsyncPostGrid( + base_url="http://localhost:5000/custom/path/", + address_verification_api_key=address_verification_api_key, + print_mail_api_key=print_mail_api_key, + _strict_response_validation=True, + http_client=httpx.AsyncClient(), + ), + ], + ids=["standard", "custom http client"], + ) + def test_absolute_request_url(self, client: AsyncPostGrid) -> None: + request = client._build_request( + FinalRequestOptions( + method="post", + url="https://myapi.com/foo", + json_data={"foo": "bar"}, + ), + ) + assert request.url == "https://myapi.com/foo" + + async def test_copied_client_does_not_close_http(self) -> None: + client = AsyncPostGrid( + base_url=base_url, + address_verification_api_key=address_verification_api_key, + print_mail_api_key=print_mail_api_key, + _strict_response_validation=True, + ) + assert not client.is_closed() + + copied = client.copy() + assert copied is not client + + del copied + + await asyncio.sleep(0.2) + assert not client.is_closed() + + async def test_client_context_manager(self) -> None: + client = AsyncPostGrid( + base_url=base_url, + address_verification_api_key=address_verification_api_key, + print_mail_api_key=print_mail_api_key, + _strict_response_validation=True, + ) + async with client as c2: + assert c2 is client + assert not c2.is_closed() + assert not client.is_closed() + assert client.is_closed() + + @pytest.mark.respx(base_url=base_url) + @pytest.mark.asyncio + async def test_client_response_validation_error(self, respx_mock: MockRouter) -> None: + class Model(BaseModel): + foo: str + + respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": {"invalid": True}})) + + with pytest.raises(APIResponseValidationError) as exc: + await self.client.get("/foo", cast_to=Model) + + assert isinstance(exc.value.__cause__, ValidationError) + + async def test_client_max_retries_validation(self) -> None: + with pytest.raises(TypeError, match=r"max_retries cannot be None"): + AsyncPostGrid( + base_url=base_url, + address_verification_api_key=address_verification_api_key, + print_mail_api_key=print_mail_api_key, + _strict_response_validation=True, + max_retries=cast(Any, None), + ) + + @pytest.mark.respx(base_url=base_url) + @pytest.mark.asyncio + async def test_received_text_for_expected_json(self, respx_mock: MockRouter) -> None: + class Model(BaseModel): + name: str + + respx_mock.get("/foo").mock(return_value=httpx.Response(200, text="my-custom-format")) + + strict_client = AsyncPostGrid( + base_url=base_url, + address_verification_api_key=address_verification_api_key, + print_mail_api_key=print_mail_api_key, + _strict_response_validation=True, + ) + + with pytest.raises(APIResponseValidationError): + await strict_client.get("/foo", cast_to=Model) + + client = AsyncPostGrid( + base_url=base_url, + address_verification_api_key=address_verification_api_key, + print_mail_api_key=print_mail_api_key, + _strict_response_validation=False, + ) + + response = await client.get("/foo", cast_to=Model) + assert isinstance(response, str) # type: ignore[unreachable] + + @pytest.mark.parametrize( + "remaining_retries,retry_after,timeout", + [ + [3, "20", 20], + [3, "0", 0.5], + [3, "-10", 0.5], + [3, "60", 60], + [3, "61", 0.5], + [3, "Fri, 29 Sep 2023 16:26:57 GMT", 20], + [3, "Fri, 29 Sep 2023 16:26:37 GMT", 0.5], + [3, "Fri, 29 Sep 2023 16:26:27 GMT", 0.5], + [3, "Fri, 29 Sep 2023 16:27:37 GMT", 60], + [3, "Fri, 29 Sep 2023 16:27:38 GMT", 0.5], + [3, "99999999999999999999999999999999999", 0.5], + [3, "Zun, 29 Sep 2023 16:26:27 GMT", 0.5], + [3, "", 0.5], + [2, "", 0.5 * 2.0], + [1, "", 0.5 * 4.0], + [-1100, "", 8], # test large number potentially overflowing + ], + ) + @mock.patch("time.time", mock.MagicMock(return_value=1696004797)) + @pytest.mark.asyncio + async def test_parse_retry_after_header(self, remaining_retries: int, retry_after: str, timeout: float) -> None: + client = AsyncPostGrid( + base_url=base_url, + address_verification_api_key=address_verification_api_key, + print_mail_api_key=print_mail_api_key, + _strict_response_validation=True, + ) + + headers = httpx.Headers({"retry-after": retry_after}) + options = FinalRequestOptions(method="get", url="/foo", max_retries=3) + calculated = client._calculate_retry_timeout(remaining_retries, options, headers) + assert calculated == pytest.approx(timeout, 0.5 * 0.875) # pyright: ignore[reportUnknownMemberType] + + @mock.patch("postgrid._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) + @pytest.mark.respx(base_url=base_url) + async def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter) -> None: + respx_mock.post("/v1/addver/verifications").mock(side_effect=httpx.TimeoutException("Test timeout error")) + + with pytest.raises(APITimeoutError): + await self.client.post( + "/v1/addver/verifications", + body=cast(object, maybe_transform(dict(address="address"), StandardFreeformAddressInput)), + cast_to=httpx.Response, + options={"headers": {RAW_RESPONSE_HEADER: "stream"}}, + ) + + assert _get_open_connections(self.client) == 0 + + @mock.patch("postgrid._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) + @pytest.mark.respx(base_url=base_url) + async def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter) -> None: + respx_mock.post("/v1/addver/verifications").mock(return_value=httpx.Response(500)) + + with pytest.raises(APIStatusError): + await self.client.post( + "/v1/addver/verifications", + body=cast(object, maybe_transform(dict(address="address"), StandardFreeformAddressInput)), + cast_to=httpx.Response, + options={"headers": {RAW_RESPONSE_HEADER: "stream"}}, + ) + + assert _get_open_connections(self.client) == 0 + + @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) + @mock.patch("postgrid._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) + @pytest.mark.respx(base_url=base_url) + @pytest.mark.asyncio + @pytest.mark.parametrize("failure_mode", ["status", "exception"]) + async def test_retries_taken( + self, + async_client: AsyncPostGrid, + failures_before_success: int, + failure_mode: Literal["status", "exception"], + respx_mock: MockRouter, + ) -> None: + client = async_client.with_options(max_retries=4) + + nb_retries = 0 + + def retry_handler(_request: httpx.Request) -> httpx.Response: + nonlocal nb_retries + if nb_retries < failures_before_success: + nb_retries += 1 + if failure_mode == "exception": + raise RuntimeError("oops") + return httpx.Response(500) + return httpx.Response(200) + + respx_mock.post("/v1/addver/verifications").mock(side_effect=retry_handler) + + response = await client.address_verification.with_raw_response.verify(address="address") + + assert response.retries_taken == failures_before_success + assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success + + @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) + @mock.patch("postgrid._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) + @pytest.mark.respx(base_url=base_url) + @pytest.mark.asyncio + async def test_omit_retry_count_header( + self, async_client: AsyncPostGrid, failures_before_success: int, respx_mock: MockRouter + ) -> None: + client = async_client.with_options(max_retries=4) + + nb_retries = 0 + + def retry_handler(_request: httpx.Request) -> httpx.Response: + nonlocal nb_retries + if nb_retries < failures_before_success: + nb_retries += 1 + return httpx.Response(500) + return httpx.Response(200) + + respx_mock.post("/v1/addver/verifications").mock(side_effect=retry_handler) + + response = await client.address_verification.with_raw_response.verify( + address="address", extra_headers={"x-stainless-retry-count": Omit()} + ) + + assert len(response.http_request.headers.get_list("x-stainless-retry-count")) == 0 + + @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) + @mock.patch("postgrid._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) + @pytest.mark.respx(base_url=base_url) + @pytest.mark.asyncio + async def test_overwrite_retry_count_header( + self, async_client: AsyncPostGrid, failures_before_success: int, respx_mock: MockRouter + ) -> None: + client = async_client.with_options(max_retries=4) + + nb_retries = 0 + + def retry_handler(_request: httpx.Request) -> httpx.Response: + nonlocal nb_retries + if nb_retries < failures_before_success: + nb_retries += 1 + return httpx.Response(500) + return httpx.Response(200) + + respx_mock.post("/v1/addver/verifications").mock(side_effect=retry_handler) + + response = await client.address_verification.with_raw_response.verify( + address="address", extra_headers={"x-stainless-retry-count": "42"} + ) + + assert response.http_request.headers.get("x-stainless-retry-count") == "42" + + def test_get_platform(self) -> None: + # A previous implementation of asyncify could leave threads unterminated when + # used with nest_asyncio. + # + # Since nest_asyncio.apply() is global and cannot be un-applied, this + # test is run in a separate process to avoid affecting other tests. + test_code = dedent(""" + import asyncio + import nest_asyncio + import threading + + from postgrid._utils import asyncify + from postgrid._base_client import get_platform + + async def test_main() -> None: + result = await asyncify(get_platform)() + print(result) + for thread in threading.enumerate(): + print(thread.name) + + nest_asyncio.apply() + asyncio.run(test_main()) + """) + with subprocess.Popen( + [sys.executable, "-c", test_code], + text=True, + ) as process: + timeout = 10 # seconds + + start_time = time.monotonic() + while True: + return_code = process.poll() + if return_code is not None: + if return_code != 0: + raise AssertionError("calling get_platform using asyncify resulted in a non-zero exit code") + + # success + break + + if time.monotonic() - start_time > timeout: + process.kill() + raise AssertionError("calling get_platform using asyncify resulted in a hung process") + + time.sleep(0.1) diff --git a/tests/test_contact.py b/tests/test_contact.py deleted file mode 100644 index 8804a47..0000000 --- a/tests/test_contact.py +++ /dev/null @@ -1,54 +0,0 @@ -import postgrid -import os -import uuid - - -def setup_module(): - postgrid.pm_key = os.environ.get("PM_API_KEY") - - -def create_test_contact(idempotency_key=None): - return postgrid.Contact.create( - first_name="Test", - last_name="Contact", - company_name="Test Company", - address_line1="20 Bay St, Toronto, ON M9V 4V1", - country_code="CA", - job_title="Test", - metadata={"test": [10, 20]}, - idempotency_key=idempotency_key, - ) - - -def test_create(): - contact = create_test_contact() - - assert isinstance(contact, postgrid.Contact) - assert contact.company_name == "Test Company" - - -def test_idempotency(): - idempotency_key = str(uuid.uuid4()) - - contact = create_test_contact(idempotency_key=idempotency_key) - contact_duplicate = create_test_contact(idempotency_key=idempotency_key) - - # The second call should not result in a new contact being created - assert contact.id == contact_duplicate.id - - -def test_list(): - contact = create_test_contact() - list_ = postgrid.Contact.list() - - assert list_.total_count >= 1 - assert list_.skip == 0 - assert isinstance(list_.data[0], postgrid.Contact) - assert list_.data[0].company_name == contact.company_name - - -def test_delete(): - contact = create_test_contact() - res = postgrid.Contact.delete(contact.id) - - assert isinstance(res, postgrid.Contact) diff --git a/tests/test_deepcopy.py b/tests/test_deepcopy.py new file mode 100644 index 0000000..c6a9dd0 --- /dev/null +++ b/tests/test_deepcopy.py @@ -0,0 +1,58 @@ +from postgrid._utils import deepcopy_minimal + + +def assert_different_identities(obj1: object, obj2: object) -> None: + assert obj1 == obj2 + assert id(obj1) != id(obj2) + + +def test_simple_dict() -> None: + obj1 = {"foo": "bar"} + obj2 = deepcopy_minimal(obj1) + assert_different_identities(obj1, obj2) + + +def test_nested_dict() -> None: + obj1 = {"foo": {"bar": True}} + obj2 = deepcopy_minimal(obj1) + assert_different_identities(obj1, obj2) + assert_different_identities(obj1["foo"], obj2["foo"]) + + +def test_complex_nested_dict() -> None: + obj1 = {"foo": {"bar": [{"hello": "world"}]}} + obj2 = deepcopy_minimal(obj1) + assert_different_identities(obj1, obj2) + assert_different_identities(obj1["foo"], obj2["foo"]) + assert_different_identities(obj1["foo"]["bar"], obj2["foo"]["bar"]) + assert_different_identities(obj1["foo"]["bar"][0], obj2["foo"]["bar"][0]) + + +def test_simple_list() -> None: + obj1 = ["a", "b", "c"] + obj2 = deepcopy_minimal(obj1) + assert_different_identities(obj1, obj2) + + +def test_nested_list() -> None: + obj1 = ["a", [1, 2, 3]] + obj2 = deepcopy_minimal(obj1) + assert_different_identities(obj1, obj2) + assert_different_identities(obj1[1], obj2[1]) + + +class MyObject: ... + + +def test_ignores_other_types() -> None: + # custom classes + my_obj = MyObject() + obj1 = {"foo": my_obj} + obj2 = deepcopy_minimal(obj1) + assert_different_identities(obj1, obj2) + assert obj1["foo"] is my_obj + + # tuples + obj3 = ("a", "b") + obj4 = deepcopy_minimal(obj3) + assert obj3 is obj4 diff --git a/tests/test_extract_files.py b/tests/test_extract_files.py new file mode 100644 index 0000000..4b8b1b9 --- /dev/null +++ b/tests/test_extract_files.py @@ -0,0 +1,64 @@ +from __future__ import annotations + +from typing import Sequence + +import pytest + +from postgrid._types import FileTypes +from postgrid._utils import extract_files + + +def test_removes_files_from_input() -> None: + query = {"foo": "bar"} + assert extract_files(query, paths=[]) == [] + assert query == {"foo": "bar"} + + query2 = {"foo": b"Bar", "hello": "world"} + assert extract_files(query2, paths=[["foo"]]) == [("foo", b"Bar")] + assert query2 == {"hello": "world"} + + query3 = {"foo": {"foo": {"bar": b"Bar"}}, "hello": "world"} + assert extract_files(query3, paths=[["foo", "foo", "bar"]]) == [("foo[foo][bar]", b"Bar")] + assert query3 == {"foo": {"foo": {}}, "hello": "world"} + + query4 = {"foo": {"bar": b"Bar", "baz": "foo"}, "hello": "world"} + assert extract_files(query4, paths=[["foo", "bar"]]) == [("foo[bar]", b"Bar")] + assert query4 == {"hello": "world", "foo": {"baz": "foo"}} + + +def test_multiple_files() -> None: + query = {"documents": [{"file": b"My first file"}, {"file": b"My second file"}]} + assert extract_files(query, paths=[["documents", "", "file"]]) == [ + ("documents[][file]", b"My first file"), + ("documents[][file]", b"My second file"), + ] + assert query == {"documents": [{}, {}]} + + +@pytest.mark.parametrize( + "query,paths,expected", + [ + [ + {"foo": {"bar": "baz"}}, + [["foo", "", "bar"]], + [], + ], + [ + {"foo": ["bar", "baz"]}, + [["foo", "bar"]], + [], + ], + [ + {"foo": {"bar": "baz"}}, + [["foo", "foo"]], + [], + ], + ], + ids=["dict expecting array", "array expecting dict", "unknown keys"], +) +def test_ignores_incorrect_paths( + query: dict[str, object], + paths: Sequence[Sequence[str]], + expected: list[tuple[str, FileTypes]], +) -> None: + assert extract_files(query, paths=paths) == expected diff --git a/tests/test_files.py b/tests/test_files.py new file mode 100644 index 0000000..d2a877a --- /dev/null +++ b/tests/test_files.py @@ -0,0 +1,51 @@ +from pathlib import Path + +import anyio +import pytest +from dirty_equals import IsDict, IsList, IsBytes, IsTuple + +from postgrid._files import to_httpx_files, async_to_httpx_files + +readme_path = Path(__file__).parent.parent.joinpath("README.md") + + +def test_pathlib_includes_file_name() -> None: + result = to_httpx_files({"file": readme_path}) + print(result) + assert result == IsDict({"file": IsTuple("README.md", IsBytes())}) + + +def test_tuple_input() -> None: + result = to_httpx_files([("file", readme_path)]) + print(result) + assert result == IsList(IsTuple("file", IsTuple("README.md", IsBytes()))) + + +@pytest.mark.asyncio +async def test_async_pathlib_includes_file_name() -> None: + result = await async_to_httpx_files({"file": readme_path}) + print(result) + assert result == IsDict({"file": IsTuple("README.md", IsBytes())}) + + +@pytest.mark.asyncio +async def test_async_supports_anyio_path() -> None: + result = await async_to_httpx_files({"file": anyio.Path(readme_path)}) + print(result) + assert result == IsDict({"file": IsTuple("README.md", IsBytes())}) + + +@pytest.mark.asyncio +async def test_async_tuple_input() -> None: + result = await async_to_httpx_files([("file", readme_path)]) + print(result) + assert result == IsList(IsTuple("file", IsTuple("README.md", IsBytes()))) + + +def test_string_not_allowed() -> None: + with pytest.raises(TypeError, match="Expected file types input to be a FileContent type or to be a tuple"): + to_httpx_files( + { + "file": "foo", # type: ignore + } + ) diff --git a/tests/test_letter.py b/tests/test_letter.py deleted file mode 100644 index 57968e9..0000000 --- a/tests/test_letter.py +++ /dev/null @@ -1,89 +0,0 @@ -import postgrid -import os -import time - - -def setup_module(): - postgrid.pm_key = os.environ.get("PM_API_KEY") - - -def create_test_letter(): - return postgrid.Letter.create( - to={ - "first_name": "Test", - "last_name": "Contact", - "company_name": "Test Company", - "address_line1": "20 Bay St, Toronto, ON M9V 4V1", - "country_code": "CA", - "job_title": "Test", - "metadata": {"test": [10, 20]}, - }, - from_={ - "company_name": "PostGrid", - "address_line1": "20 Bay St, Toronto, ON M9V 4V1", - "country_code": "CA", - }, - html="Hello, {{to.firstName}}", - double_sided=True, - color=True, - merge_variables={"testCamel": 1, "test_snake": 1}, - ) - - -def test_create(): - letter = create_test_letter() - - assert isinstance(letter, postgrid.Letter) - assert isinstance(letter.html, str) - assert letter.double_sided - assert letter.color - assert letter.merge_variables.get("testCamel") == "1" - assert letter.merge_variables.get("test_snake") == "1" - - -def test_preview_generated(): - letter = create_test_letter() - - assert isinstance(letter, postgrid.Letter) - - time.sleep(5) - - letter = postgrid.Letter.retrieve(letter.id) - - assert isinstance(letter.url, str) - - -def test_list(): - letter = create_test_letter() - list_ = postgrid.Letter.list() - - assert list_.total_count >= 1 - assert list_.skip == 0 - assert isinstance(list_.data[0], postgrid.Letter) - assert list_.data[0].html == letter.html - - -def test_list_search(): - letter = create_test_letter() - list_ = postgrid.Letter.list(search=f'{{"_id": "{letter.id}"}}') - - assert list_.total_count >= 1 - assert list_.skip == 0 - assert isinstance(list_.data[0], postgrid.Letter) - assert list_.data[0].html == letter.html - - -def test_delete(): - letter = create_test_letter() - res = postgrid.Letter.delete(letter.id) - - assert isinstance(res, postgrid.Letter) - - -def test_delete_with_note(): - letter = create_test_letter() - note = "cancelled due to issues test" - res = postgrid.Letter.delete_with_note(letter.id, note) - - assert isinstance(res, postgrid.Letter) - assert res.cancellation["note"] == note diff --git a/tests/test_models.py b/tests/test_models.py new file mode 100644 index 0000000..e570758 --- /dev/null +++ b/tests/test_models.py @@ -0,0 +1,856 @@ +import json +from typing import Any, Dict, List, Union, Optional, cast +from datetime import datetime, timezone +from typing_extensions import Literal, Annotated, TypeAliasType + +import pytest +import pydantic +from pydantic import Field + +from postgrid._utils import PropertyInfo +from postgrid._compat import PYDANTIC_V2, parse_obj, model_dump, model_json +from postgrid._models import BaseModel, construct_type + + +class BasicModel(BaseModel): + foo: str + + +@pytest.mark.parametrize("value", ["hello", 1], ids=["correct type", "mismatched"]) +def test_basic(value: object) -> None: + m = BasicModel.construct(foo=value) + assert m.foo == value + + +def test_directly_nested_model() -> None: + class NestedModel(BaseModel): + nested: BasicModel + + m = NestedModel.construct(nested={"foo": "Foo!"}) + assert m.nested.foo == "Foo!" + + # mismatched types + m = NestedModel.construct(nested="hello!") + assert cast(Any, m.nested) == "hello!" + + +def test_optional_nested_model() -> None: + class NestedModel(BaseModel): + nested: Optional[BasicModel] + + m1 = NestedModel.construct(nested=None) + assert m1.nested is None + + m2 = NestedModel.construct(nested={"foo": "bar"}) + assert m2.nested is not None + assert m2.nested.foo == "bar" + + # mismatched types + m3 = NestedModel.construct(nested={"foo"}) + assert isinstance(cast(Any, m3.nested), set) + assert cast(Any, m3.nested) == {"foo"} + + +def test_list_nested_model() -> None: + class NestedModel(BaseModel): + nested: List[BasicModel] + + m = NestedModel.construct(nested=[{"foo": "bar"}, {"foo": "2"}]) + assert m.nested is not None + assert isinstance(m.nested, list) + assert len(m.nested) == 2 + assert m.nested[0].foo == "bar" + assert m.nested[1].foo == "2" + + # mismatched types + m = NestedModel.construct(nested=True) + assert cast(Any, m.nested) is True + + m = NestedModel.construct(nested=[False]) + assert cast(Any, m.nested) == [False] + + +def test_optional_list_nested_model() -> None: + class NestedModel(BaseModel): + nested: Optional[List[BasicModel]] + + m1 = NestedModel.construct(nested=[{"foo": "bar"}, {"foo": "2"}]) + assert m1.nested is not None + assert isinstance(m1.nested, list) + assert len(m1.nested) == 2 + assert m1.nested[0].foo == "bar" + assert m1.nested[1].foo == "2" + + m2 = NestedModel.construct(nested=None) + assert m2.nested is None + + # mismatched types + m3 = NestedModel.construct(nested={1}) + assert cast(Any, m3.nested) == {1} + + m4 = NestedModel.construct(nested=[False]) + assert cast(Any, m4.nested) == [False] + + +def test_list_optional_items_nested_model() -> None: + class NestedModel(BaseModel): + nested: List[Optional[BasicModel]] + + m = NestedModel.construct(nested=[None, {"foo": "bar"}]) + assert m.nested is not None + assert isinstance(m.nested, list) + assert len(m.nested) == 2 + assert m.nested[0] is None + assert m.nested[1] is not None + assert m.nested[1].foo == "bar" + + # mismatched types + m3 = NestedModel.construct(nested="foo") + assert cast(Any, m3.nested) == "foo" + + m4 = NestedModel.construct(nested=[False]) + assert cast(Any, m4.nested) == [False] + + +def test_list_mismatched_type() -> None: + class NestedModel(BaseModel): + nested: List[str] + + m = NestedModel.construct(nested=False) + assert cast(Any, m.nested) is False + + +def test_raw_dictionary() -> None: + class NestedModel(BaseModel): + nested: Dict[str, str] + + m = NestedModel.construct(nested={"hello": "world"}) + assert m.nested == {"hello": "world"} + + # mismatched types + m = NestedModel.construct(nested=False) + assert cast(Any, m.nested) is False + + +def test_nested_dictionary_model() -> None: + class NestedModel(BaseModel): + nested: Dict[str, BasicModel] + + m = NestedModel.construct(nested={"hello": {"foo": "bar"}}) + assert isinstance(m.nested, dict) + assert m.nested["hello"].foo == "bar" + + # mismatched types + m = NestedModel.construct(nested={"hello": False}) + assert cast(Any, m.nested["hello"]) is False + + +def test_unknown_fields() -> None: + m1 = BasicModel.construct(foo="foo", unknown=1) + assert m1.foo == "foo" + assert cast(Any, m1).unknown == 1 + + m2 = BasicModel.construct(foo="foo", unknown={"foo_bar": True}) + assert m2.foo == "foo" + assert cast(Any, m2).unknown == {"foo_bar": True} + + assert model_dump(m2) == {"foo": "foo", "unknown": {"foo_bar": True}} + + +def test_strict_validation_unknown_fields() -> None: + class Model(BaseModel): + foo: str + + model = parse_obj(Model, dict(foo="hello!", user="Robert")) + assert model.foo == "hello!" + assert cast(Any, model).user == "Robert" + + assert model_dump(model) == {"foo": "hello!", "user": "Robert"} + + +def test_aliases() -> None: + class Model(BaseModel): + my_field: int = Field(alias="myField") + + m = Model.construct(myField=1) + assert m.my_field == 1 + + # mismatched types + m = Model.construct(myField={"hello": False}) + assert cast(Any, m.my_field) == {"hello": False} + + +def test_repr() -> None: + model = BasicModel(foo="bar") + assert str(model) == "BasicModel(foo='bar')" + assert repr(model) == "BasicModel(foo='bar')" + + +def test_repr_nested_model() -> None: + class Child(BaseModel): + name: str + age: int + + class Parent(BaseModel): + name: str + child: Child + + model = Parent(name="Robert", child=Child(name="Foo", age=5)) + assert str(model) == "Parent(name='Robert', child=Child(name='Foo', age=5))" + assert repr(model) == "Parent(name='Robert', child=Child(name='Foo', age=5))" + + +def test_optional_list() -> None: + class Submodel(BaseModel): + name: str + + class Model(BaseModel): + items: Optional[List[Submodel]] + + m = Model.construct(items=None) + assert m.items is None + + m = Model.construct(items=[]) + assert m.items == [] + + m = Model.construct(items=[{"name": "Robert"}]) + assert m.items is not None + assert len(m.items) == 1 + assert m.items[0].name == "Robert" + + +def test_nested_union_of_models() -> None: + class Submodel1(BaseModel): + bar: bool + + class Submodel2(BaseModel): + thing: str + + class Model(BaseModel): + foo: Union[Submodel1, Submodel2] + + m = Model.construct(foo={"thing": "hello"}) + assert isinstance(m.foo, Submodel2) + assert m.foo.thing == "hello" + + +def test_nested_union_of_mixed_types() -> None: + class Submodel1(BaseModel): + bar: bool + + class Model(BaseModel): + foo: Union[Submodel1, Literal[True], Literal["CARD_HOLDER"]] + + m = Model.construct(foo=True) + assert m.foo is True + + m = Model.construct(foo="CARD_HOLDER") + assert m.foo == "CARD_HOLDER" + + m = Model.construct(foo={"bar": False}) + assert isinstance(m.foo, Submodel1) + assert m.foo.bar is False + + +def test_nested_union_multiple_variants() -> None: + class Submodel1(BaseModel): + bar: bool + + class Submodel2(BaseModel): + thing: str + + class Submodel3(BaseModel): + foo: int + + class Model(BaseModel): + foo: Union[Submodel1, Submodel2, None, Submodel3] + + m = Model.construct(foo={"thing": "hello"}) + assert isinstance(m.foo, Submodel2) + assert m.foo.thing == "hello" + + m = Model.construct(foo=None) + assert m.foo is None + + m = Model.construct() + assert m.foo is None + + m = Model.construct(foo={"foo": "1"}) + assert isinstance(m.foo, Submodel3) + assert m.foo.foo == 1 + + +def test_nested_union_invalid_data() -> None: + class Submodel1(BaseModel): + level: int + + class Submodel2(BaseModel): + name: str + + class Model(BaseModel): + foo: Union[Submodel1, Submodel2] + + m = Model.construct(foo=True) + assert cast(bool, m.foo) is True + + m = Model.construct(foo={"name": 3}) + if PYDANTIC_V2: + assert isinstance(m.foo, Submodel1) + assert m.foo.name == 3 # type: ignore + else: + assert isinstance(m.foo, Submodel2) + assert m.foo.name == "3" + + +def test_list_of_unions() -> None: + class Submodel1(BaseModel): + level: int + + class Submodel2(BaseModel): + name: str + + class Model(BaseModel): + items: List[Union[Submodel1, Submodel2]] + + m = Model.construct(items=[{"level": 1}, {"name": "Robert"}]) + assert len(m.items) == 2 + assert isinstance(m.items[0], Submodel1) + assert m.items[0].level == 1 + assert isinstance(m.items[1], Submodel2) + assert m.items[1].name == "Robert" + + m = Model.construct(items=[{"level": -1}, 156]) + assert len(m.items) == 2 + assert isinstance(m.items[0], Submodel1) + assert m.items[0].level == -1 + assert cast(Any, m.items[1]) == 156 + + +def test_union_of_lists() -> None: + class SubModel1(BaseModel): + level: int + + class SubModel2(BaseModel): + name: str + + class Model(BaseModel): + items: Union[List[SubModel1], List[SubModel2]] + + # with one valid entry + m = Model.construct(items=[{"name": "Robert"}]) + assert len(m.items) == 1 + assert isinstance(m.items[0], SubModel2) + assert m.items[0].name == "Robert" + + # with two entries pointing to different types + m = Model.construct(items=[{"level": 1}, {"name": "Robert"}]) + assert len(m.items) == 2 + assert isinstance(m.items[0], SubModel1) + assert m.items[0].level == 1 + assert isinstance(m.items[1], SubModel1) + assert cast(Any, m.items[1]).name == "Robert" + + # with two entries pointing to *completely* different types + m = Model.construct(items=[{"level": -1}, 156]) + assert len(m.items) == 2 + assert isinstance(m.items[0], SubModel1) + assert m.items[0].level == -1 + assert cast(Any, m.items[1]) == 156 + + +def test_dict_of_union() -> None: + class SubModel1(BaseModel): + name: str + + class SubModel2(BaseModel): + foo: str + + class Model(BaseModel): + data: Dict[str, Union[SubModel1, SubModel2]] + + m = Model.construct(data={"hello": {"name": "there"}, "foo": {"foo": "bar"}}) + assert len(list(m.data.keys())) == 2 + assert isinstance(m.data["hello"], SubModel1) + assert m.data["hello"].name == "there" + assert isinstance(m.data["foo"], SubModel2) + assert m.data["foo"].foo == "bar" + + # TODO: test mismatched type + + +def test_double_nested_union() -> None: + class SubModel1(BaseModel): + name: str + + class SubModel2(BaseModel): + bar: str + + class Model(BaseModel): + data: Dict[str, List[Union[SubModel1, SubModel2]]] + + m = Model.construct(data={"foo": [{"bar": "baz"}, {"name": "Robert"}]}) + assert len(m.data["foo"]) == 2 + + entry1 = m.data["foo"][0] + assert isinstance(entry1, SubModel2) + assert entry1.bar == "baz" + + entry2 = m.data["foo"][1] + assert isinstance(entry2, SubModel1) + assert entry2.name == "Robert" + + # TODO: test mismatched type + + +def test_union_of_dict() -> None: + class SubModel1(BaseModel): + name: str + + class SubModel2(BaseModel): + foo: str + + class Model(BaseModel): + data: Union[Dict[str, SubModel1], Dict[str, SubModel2]] + + m = Model.construct(data={"hello": {"name": "there"}, "foo": {"foo": "bar"}}) + assert len(list(m.data.keys())) == 2 + assert isinstance(m.data["hello"], SubModel1) + assert m.data["hello"].name == "there" + assert isinstance(m.data["foo"], SubModel1) + assert cast(Any, m.data["foo"]).foo == "bar" + + +def test_iso8601_datetime() -> None: + class Model(BaseModel): + created_at: datetime + + expected = datetime(2019, 12, 27, 18, 11, 19, 117000, tzinfo=timezone.utc) + + if PYDANTIC_V2: + expected_json = '{"created_at":"2019-12-27T18:11:19.117000Z"}' + else: + expected_json = '{"created_at": "2019-12-27T18:11:19.117000+00:00"}' + + model = Model.construct(created_at="2019-12-27T18:11:19.117Z") + assert model.created_at == expected + assert model_json(model) == expected_json + + model = parse_obj(Model, dict(created_at="2019-12-27T18:11:19.117Z")) + assert model.created_at == expected + assert model_json(model) == expected_json + + +def test_does_not_coerce_int() -> None: + class Model(BaseModel): + bar: int + + assert Model.construct(bar=1).bar == 1 + assert Model.construct(bar=10.9).bar == 10.9 + assert Model.construct(bar="19").bar == "19" # type: ignore[comparison-overlap] + assert Model.construct(bar=False).bar is False + + +def test_int_to_float_safe_conversion() -> None: + class Model(BaseModel): + float_field: float + + m = Model.construct(float_field=10) + assert m.float_field == 10.0 + assert isinstance(m.float_field, float) + + m = Model.construct(float_field=10.12) + assert m.float_field == 10.12 + assert isinstance(m.float_field, float) + + # number too big + m = Model.construct(float_field=2**53 + 1) + assert m.float_field == 2**53 + 1 + assert isinstance(m.float_field, int) + + +def test_deprecated_alias() -> None: + class Model(BaseModel): + resource_id: str = Field(alias="model_id") + + @property + def model_id(self) -> str: + return self.resource_id + + m = Model.construct(model_id="id") + assert m.model_id == "id" + assert m.resource_id == "id" + assert m.resource_id is m.model_id + + m = parse_obj(Model, {"model_id": "id"}) + assert m.model_id == "id" + assert m.resource_id == "id" + assert m.resource_id is m.model_id + + +def test_omitted_fields() -> None: + class Model(BaseModel): + resource_id: Optional[str] = None + + m = Model.construct() + assert "resource_id" not in m.model_fields_set + + m = Model.construct(resource_id=None) + assert "resource_id" in m.model_fields_set + + m = Model.construct(resource_id="foo") + assert "resource_id" in m.model_fields_set + + +def test_to_dict() -> None: + class Model(BaseModel): + foo: Optional[str] = Field(alias="FOO", default=None) + + m = Model(FOO="hello") + assert m.to_dict() == {"FOO": "hello"} + assert m.to_dict(use_api_names=False) == {"foo": "hello"} + + m2 = Model() + assert m2.to_dict() == {} + assert m2.to_dict(exclude_unset=False) == {"FOO": None} + assert m2.to_dict(exclude_unset=False, exclude_none=True) == {} + assert m2.to_dict(exclude_unset=False, exclude_defaults=True) == {} + + m3 = Model(FOO=None) + assert m3.to_dict() == {"FOO": None} + assert m3.to_dict(exclude_none=True) == {} + assert m3.to_dict(exclude_defaults=True) == {} + + class Model2(BaseModel): + created_at: datetime + + time_str = "2024-03-21T11:39:01.275859" + m4 = Model2.construct(created_at=time_str) + assert m4.to_dict(mode="python") == {"created_at": datetime.fromisoformat(time_str)} + assert m4.to_dict(mode="json") == {"created_at": time_str} + + if not PYDANTIC_V2: + with pytest.raises(ValueError, match="warnings is only supported in Pydantic v2"): + m.to_dict(warnings=False) + + +def test_forwards_compat_model_dump_method() -> None: + class Model(BaseModel): + foo: Optional[str] = Field(alias="FOO", default=None) + + m = Model(FOO="hello") + assert m.model_dump() == {"foo": "hello"} + assert m.model_dump(include={"bar"}) == {} + assert m.model_dump(exclude={"foo"}) == {} + assert m.model_dump(by_alias=True) == {"FOO": "hello"} + + m2 = Model() + assert m2.model_dump() == {"foo": None} + assert m2.model_dump(exclude_unset=True) == {} + assert m2.model_dump(exclude_none=True) == {} + assert m2.model_dump(exclude_defaults=True) == {} + + m3 = Model(FOO=None) + assert m3.model_dump() == {"foo": None} + assert m3.model_dump(exclude_none=True) == {} + + if not PYDANTIC_V2: + with pytest.raises(ValueError, match="round_trip is only supported in Pydantic v2"): + m.model_dump(round_trip=True) + + with pytest.raises(ValueError, match="warnings is only supported in Pydantic v2"): + m.model_dump(warnings=False) + + +def test_compat_method_no_error_for_warnings() -> None: + class Model(BaseModel): + foo: Optional[str] + + m = Model(foo="hello") + assert isinstance(model_dump(m, warnings=False), dict) + + +def test_to_json() -> None: + class Model(BaseModel): + foo: Optional[str] = Field(alias="FOO", default=None) + + m = Model(FOO="hello") + assert json.loads(m.to_json()) == {"FOO": "hello"} + assert json.loads(m.to_json(use_api_names=False)) == {"foo": "hello"} + + if PYDANTIC_V2: + assert m.to_json(indent=None) == '{"FOO":"hello"}' + else: + assert m.to_json(indent=None) == '{"FOO": "hello"}' + + m2 = Model() + assert json.loads(m2.to_json()) == {} + assert json.loads(m2.to_json(exclude_unset=False)) == {"FOO": None} + assert json.loads(m2.to_json(exclude_unset=False, exclude_none=True)) == {} + assert json.loads(m2.to_json(exclude_unset=False, exclude_defaults=True)) == {} + + m3 = Model(FOO=None) + assert json.loads(m3.to_json()) == {"FOO": None} + assert json.loads(m3.to_json(exclude_none=True)) == {} + + if not PYDANTIC_V2: + with pytest.raises(ValueError, match="warnings is only supported in Pydantic v2"): + m.to_json(warnings=False) + + +def test_forwards_compat_model_dump_json_method() -> None: + class Model(BaseModel): + foo: Optional[str] = Field(alias="FOO", default=None) + + m = Model(FOO="hello") + assert json.loads(m.model_dump_json()) == {"foo": "hello"} + assert json.loads(m.model_dump_json(include={"bar"})) == {} + assert json.loads(m.model_dump_json(include={"foo"})) == {"foo": "hello"} + assert json.loads(m.model_dump_json(by_alias=True)) == {"FOO": "hello"} + + assert m.model_dump_json(indent=2) == '{\n "foo": "hello"\n}' + + m2 = Model() + assert json.loads(m2.model_dump_json()) == {"foo": None} + assert json.loads(m2.model_dump_json(exclude_unset=True)) == {} + assert json.loads(m2.model_dump_json(exclude_none=True)) == {} + assert json.loads(m2.model_dump_json(exclude_defaults=True)) == {} + + m3 = Model(FOO=None) + assert json.loads(m3.model_dump_json()) == {"foo": None} + assert json.loads(m3.model_dump_json(exclude_none=True)) == {} + + if not PYDANTIC_V2: + with pytest.raises(ValueError, match="round_trip is only supported in Pydantic v2"): + m.model_dump_json(round_trip=True) + + with pytest.raises(ValueError, match="warnings is only supported in Pydantic v2"): + m.model_dump_json(warnings=False) + + +def test_type_compat() -> None: + # our model type can be assigned to Pydantic's model type + + def takes_pydantic(model: pydantic.BaseModel) -> None: # noqa: ARG001 + ... + + class OurModel(BaseModel): + foo: Optional[str] = None + + takes_pydantic(OurModel()) + + +def test_annotated_types() -> None: + class Model(BaseModel): + value: str + + m = construct_type( + value={"value": "foo"}, + type_=cast(Any, Annotated[Model, "random metadata"]), + ) + assert isinstance(m, Model) + assert m.value == "foo" + + +def test_discriminated_unions_invalid_data() -> None: + class A(BaseModel): + type: Literal["a"] + + data: str + + class B(BaseModel): + type: Literal["b"] + + data: int + + m = construct_type( + value={"type": "b", "data": "foo"}, + type_=cast(Any, Annotated[Union[A, B], PropertyInfo(discriminator="type")]), + ) + assert isinstance(m, B) + assert m.type == "b" + assert m.data == "foo" # type: ignore[comparison-overlap] + + m = construct_type( + value={"type": "a", "data": 100}, + type_=cast(Any, Annotated[Union[A, B], PropertyInfo(discriminator="type")]), + ) + assert isinstance(m, A) + assert m.type == "a" + if PYDANTIC_V2: + assert m.data == 100 # type: ignore[comparison-overlap] + else: + # pydantic v1 automatically converts inputs to strings + # if the expected type is a str + assert m.data == "100" + + +def test_discriminated_unions_unknown_variant() -> None: + class A(BaseModel): + type: Literal["a"] + + data: str + + class B(BaseModel): + type: Literal["b"] + + data: int + + m = construct_type( + value={"type": "c", "data": None, "new_thing": "bar"}, + type_=cast(Any, Annotated[Union[A, B], PropertyInfo(discriminator="type")]), + ) + + # just chooses the first variant + assert isinstance(m, A) + assert m.type == "c" # type: ignore[comparison-overlap] + assert m.data == None # type: ignore[unreachable] + assert m.new_thing == "bar" + + +def test_discriminated_unions_invalid_data_nested_unions() -> None: + class A(BaseModel): + type: Literal["a"] + + data: str + + class B(BaseModel): + type: Literal["b"] + + data: int + + class C(BaseModel): + type: Literal["c"] + + data: bool + + m = construct_type( + value={"type": "b", "data": "foo"}, + type_=cast(Any, Annotated[Union[Union[A, B], C], PropertyInfo(discriminator="type")]), + ) + assert isinstance(m, B) + assert m.type == "b" + assert m.data == "foo" # type: ignore[comparison-overlap] + + m = construct_type( + value={"type": "c", "data": "foo"}, + type_=cast(Any, Annotated[Union[Union[A, B], C], PropertyInfo(discriminator="type")]), + ) + assert isinstance(m, C) + assert m.type == "c" + assert m.data == "foo" # type: ignore[comparison-overlap] + + +def test_discriminated_unions_with_aliases_invalid_data() -> None: + class A(BaseModel): + foo_type: Literal["a"] = Field(alias="type") + + data: str + + class B(BaseModel): + foo_type: Literal["b"] = Field(alias="type") + + data: int + + m = construct_type( + value={"type": "b", "data": "foo"}, + type_=cast(Any, Annotated[Union[A, B], PropertyInfo(discriminator="foo_type")]), + ) + assert isinstance(m, B) + assert m.foo_type == "b" + assert m.data == "foo" # type: ignore[comparison-overlap] + + m = construct_type( + value={"type": "a", "data": 100}, + type_=cast(Any, Annotated[Union[A, B], PropertyInfo(discriminator="foo_type")]), + ) + assert isinstance(m, A) + assert m.foo_type == "a" + if PYDANTIC_V2: + assert m.data == 100 # type: ignore[comparison-overlap] + else: + # pydantic v1 automatically converts inputs to strings + # if the expected type is a str + assert m.data == "100" + + +def test_discriminated_unions_overlapping_discriminators_invalid_data() -> None: + class A(BaseModel): + type: Literal["a"] + + data: bool + + class B(BaseModel): + type: Literal["a"] + + data: int + + m = construct_type( + value={"type": "a", "data": "foo"}, + type_=cast(Any, Annotated[Union[A, B], PropertyInfo(discriminator="type")]), + ) + assert isinstance(m, B) + assert m.type == "a" + assert m.data == "foo" # type: ignore[comparison-overlap] + + +def test_discriminated_unions_invalid_data_uses_cache() -> None: + class A(BaseModel): + type: Literal["a"] + + data: str + + class B(BaseModel): + type: Literal["b"] + + data: int + + UnionType = cast(Any, Union[A, B]) + + assert not hasattr(UnionType, "__discriminator__") + + m = construct_type( + value={"type": "b", "data": "foo"}, type_=cast(Any, Annotated[UnionType, PropertyInfo(discriminator="type")]) + ) + assert isinstance(m, B) + assert m.type == "b" + assert m.data == "foo" # type: ignore[comparison-overlap] + + discriminator = UnionType.__discriminator__ + assert discriminator is not None + + m = construct_type( + value={"type": "b", "data": "foo"}, type_=cast(Any, Annotated[UnionType, PropertyInfo(discriminator="type")]) + ) + assert isinstance(m, B) + assert m.type == "b" + assert m.data == "foo" # type: ignore[comparison-overlap] + + # if the discriminator details object stays the same between invocations then + # we hit the cache + assert UnionType.__discriminator__ is discriminator + + +@pytest.mark.skipif(not PYDANTIC_V2, reason="TypeAliasType is not supported in Pydantic v1") +def test_type_alias_type() -> None: + Alias = TypeAliasType("Alias", str) + + class Model(BaseModel): + alias: Alias + union: Union[int, Alias] + + m = construct_type(value={"alias": "foo", "union": "bar"}, type_=Model) + assert isinstance(m, Model) + assert isinstance(m.alias, str) + assert m.alias == "foo" + assert isinstance(m.union, str) + assert m.union == "bar" + + +@pytest.mark.skipif(not PYDANTIC_V2, reason="TypeAliasType is not supported in Pydantic v1") +def test_field_named_cls() -> None: + class Model(BaseModel): + cls: str + + m = construct_type(value={"cls": "foo"}, type_=Model) + assert isinstance(m, Model) + assert isinstance(m.cls, str) diff --git a/tests/test_postcard.py b/tests/test_postcard.py deleted file mode 100644 index 5c11662..0000000 --- a/tests/test_postcard.py +++ /dev/null @@ -1,62 +0,0 @@ -import postgrid -import os - - -def setup_module(): - postgrid.pm_key = os.environ.get("PM_API_KEY") - - -def create_test_postcard(): - return postgrid.Postcard.create( - to={ - "first_name": "Test", - "last_name": "Contact", - "company_name": "Test Company", - "address_line1": "20 Bay St, Toronto, ON M9V 4V1", - "country_code": "CA", - "job_title": "Test", - "metadata": {"test": [10, 20]}, - }, - from_={ - "company_name": "PostGrid", - "address_line1": "20 Bay St, Toronto, ON M9V 4V1", - "country_code": "CA", - }, - frontHTML="Hello, {{to.firstName}}", - backHTML="Goodbye, {{to.firstName}}", - size="6x4", - ) - - -def test_create(): - res = create_test_postcard() - - assert isinstance(res, postgrid.Postcard) - assert isinstance(res.front_html, str) - assert isinstance(res.back_html, str) - - -def test_list(): - postcard = create_test_postcard() - list_ = postgrid.Postcard.list() - - assert list_.total_count >= 1 - assert list_.skip == 0 - assert isinstance(list_.data[0], postgrid.Postcard) - assert list_.data[0].front_html == postcard.front_html - - -def test_delete(): - postcard = create_test_postcard() - res = postgrid.Postcard.delete(postcard.id) - - assert isinstance(res, postgrid.Postcard) - - -def test_delete_with_note(): - postcard = create_test_postcard() - note = "cancelled due to issues test" - res = postgrid.Postcard.delete_with_note(postcard.id, note) - - assert isinstance(res, postgrid.Postcard) - assert res.cancellation["note"] == note diff --git a/tests/test_qs.py b/tests/test_qs.py new file mode 100644 index 0000000..d4a1886 --- /dev/null +++ b/tests/test_qs.py @@ -0,0 +1,78 @@ +from typing import Any, cast +from functools import partial +from urllib.parse import unquote + +import pytest + +from postgrid._qs import Querystring, stringify + + +def test_empty() -> None: + assert stringify({}) == "" + assert stringify({"a": {}}) == "" + assert stringify({"a": {"b": {"c": {}}}}) == "" + + +def test_basic() -> None: + assert stringify({"a": 1}) == "a=1" + assert stringify({"a": "b"}) == "a=b" + assert stringify({"a": True}) == "a=true" + assert stringify({"a": False}) == "a=false" + assert stringify({"a": 1.23456}) == "a=1.23456" + assert stringify({"a": None}) == "" + + +@pytest.mark.parametrize("method", ["class", "function"]) +def test_nested_dotted(method: str) -> None: + if method == "class": + serialise = Querystring(nested_format="dots").stringify + else: + serialise = partial(stringify, nested_format="dots") + + assert unquote(serialise({"a": {"b": "c"}})) == "a.b=c" + assert unquote(serialise({"a": {"b": "c", "d": "e", "f": "g"}})) == "a.b=c&a.d=e&a.f=g" + assert unquote(serialise({"a": {"b": {"c": {"d": "e"}}}})) == "a.b.c.d=e" + assert unquote(serialise({"a": {"b": True}})) == "a.b=true" + + +def test_nested_brackets() -> None: + assert unquote(stringify({"a": {"b": "c"}})) == "a[b]=c" + assert unquote(stringify({"a": {"b": "c", "d": "e", "f": "g"}})) == "a[b]=c&a[d]=e&a[f]=g" + assert unquote(stringify({"a": {"b": {"c": {"d": "e"}}}})) == "a[b][c][d]=e" + assert unquote(stringify({"a": {"b": True}})) == "a[b]=true" + + +@pytest.mark.parametrize("method", ["class", "function"]) +def test_array_comma(method: str) -> None: + if method == "class": + serialise = Querystring(array_format="comma").stringify + else: + serialise = partial(stringify, array_format="comma") + + assert unquote(serialise({"in": ["foo", "bar"]})) == "in=foo,bar" + assert unquote(serialise({"a": {"b": [True, False]}})) == "a[b]=true,false" + assert unquote(serialise({"a": {"b": [True, False, None, True]}})) == "a[b]=true,false,true" + + +def test_array_repeat() -> None: + assert unquote(stringify({"in": ["foo", "bar"]})) == "in=foo&in=bar" + assert unquote(stringify({"a": {"b": [True, False]}})) == "a[b]=true&a[b]=false" + assert unquote(stringify({"a": {"b": [True, False, None, True]}})) == "a[b]=true&a[b]=false&a[b]=true" + assert unquote(stringify({"in": ["foo", {"b": {"c": ["d", "e"]}}]})) == "in=foo&in[b][c]=d&in[b][c]=e" + + +@pytest.mark.parametrize("method", ["class", "function"]) +def test_array_brackets(method: str) -> None: + if method == "class": + serialise = Querystring(array_format="brackets").stringify + else: + serialise = partial(stringify, array_format="brackets") + + assert unquote(serialise({"in": ["foo", "bar"]})) == "in[]=foo&in[]=bar" + assert unquote(serialise({"a": {"b": [True, False]}})) == "a[b][]=true&a[b][]=false" + assert unquote(serialise({"a": {"b": [True, False, None, True]}})) == "a[b][]=true&a[b][]=false&a[b][]=true" + + +def test_unknown_array_format() -> None: + with pytest.raises(NotImplementedError, match="Unknown array_format value: foo, choose from comma, repeat"): + stringify({"a": ["foo", "bar"]}, array_format=cast(Any, "foo")) diff --git a/tests/test_required_args.py b/tests/test_required_args.py new file mode 100644 index 0000000..71e58ae --- /dev/null +++ b/tests/test_required_args.py @@ -0,0 +1,111 @@ +from __future__ import annotations + +import pytest + +from postgrid._utils import required_args + + +def test_too_many_positional_params() -> None: + @required_args(["a"]) + def foo(a: str | None = None) -> str | None: + return a + + with pytest.raises(TypeError, match=r"foo\(\) takes 1 argument\(s\) but 2 were given"): + foo("a", "b") # type: ignore + + +def test_positional_param() -> None: + @required_args(["a"]) + def foo(a: str | None = None) -> str | None: + return a + + assert foo("a") == "a" + assert foo(None) is None + assert foo(a="b") == "b" + + with pytest.raises(TypeError, match="Missing required argument: 'a'"): + foo() + + +def test_keyword_only_param() -> None: + @required_args(["a"]) + def foo(*, a: str | None = None) -> str | None: + return a + + assert foo(a="a") == "a" + assert foo(a=None) is None + assert foo(a="b") == "b" + + with pytest.raises(TypeError, match="Missing required argument: 'a'"): + foo() + + +def test_multiple_params() -> None: + @required_args(["a", "b", "c"]) + def foo(a: str = "", *, b: str = "", c: str = "") -> str | None: + return f"{a} {b} {c}" + + assert foo(a="a", b="b", c="c") == "a b c" + + error_message = r"Missing required arguments.*" + + with pytest.raises(TypeError, match=error_message): + foo() + + with pytest.raises(TypeError, match=error_message): + foo(a="a") + + with pytest.raises(TypeError, match=error_message): + foo(b="b") + + with pytest.raises(TypeError, match=error_message): + foo(c="c") + + with pytest.raises(TypeError, match=r"Missing required argument: 'a'"): + foo(b="a", c="c") + + with pytest.raises(TypeError, match=r"Missing required argument: 'b'"): + foo("a", c="c") + + +def test_multiple_variants() -> None: + @required_args(["a"], ["b"]) + def foo(*, a: str | None = None, b: str | None = None) -> str | None: + return a if a is not None else b + + assert foo(a="foo") == "foo" + assert foo(b="bar") == "bar" + assert foo(a=None) is None + assert foo(b=None) is None + + # TODO: this error message could probably be improved + with pytest.raises( + TypeError, + match=r"Missing required arguments; Expected either \('a'\) or \('b'\) arguments to be given", + ): + foo() + + +def test_multiple_params_multiple_variants() -> None: + @required_args(["a", "b"], ["c"]) + def foo(*, a: str | None = None, b: str | None = None, c: str | None = None) -> str | None: + if a is not None: + return a + if b is not None: + return b + return c + + error_message = r"Missing required arguments; Expected either \('a' and 'b'\) or \('c'\) arguments to be given" + + with pytest.raises(TypeError, match=error_message): + foo(a="foo") + + with pytest.raises(TypeError, match=error_message): + foo(b="bar") + + with pytest.raises(TypeError, match=error_message): + foo() + + assert foo(a=None, b="bar") == "bar" + assert foo(c=None) is None + assert foo(c="foo") == "foo" diff --git a/tests/test_response.py b/tests/test_response.py new file mode 100644 index 0000000..c5c8527 --- /dev/null +++ b/tests/test_response.py @@ -0,0 +1,277 @@ +import json +from typing import Any, List, Union, cast +from typing_extensions import Annotated + +import httpx +import pytest +import pydantic + +from postgrid import PostGrid, BaseModel, AsyncPostGrid +from postgrid._response import ( + APIResponse, + BaseAPIResponse, + AsyncAPIResponse, + BinaryAPIResponse, + AsyncBinaryAPIResponse, + extract_response_type, +) +from postgrid._streaming import Stream +from postgrid._base_client import FinalRequestOptions + + +class ConcreteBaseAPIResponse(APIResponse[bytes]): ... + + +class ConcreteAPIResponse(APIResponse[List[str]]): ... + + +class ConcreteAsyncAPIResponse(APIResponse[httpx.Response]): ... + + +def test_extract_response_type_direct_classes() -> None: + assert extract_response_type(BaseAPIResponse[str]) == str + assert extract_response_type(APIResponse[str]) == str + assert extract_response_type(AsyncAPIResponse[str]) == str + + +def test_extract_response_type_direct_class_missing_type_arg() -> None: + with pytest.raises( + RuntimeError, + match="Expected type to have a type argument at index 0 but it did not", + ): + extract_response_type(AsyncAPIResponse) + + +def test_extract_response_type_concrete_subclasses() -> None: + assert extract_response_type(ConcreteBaseAPIResponse) == bytes + assert extract_response_type(ConcreteAPIResponse) == List[str] + assert extract_response_type(ConcreteAsyncAPIResponse) == httpx.Response + + +def test_extract_response_type_binary_response() -> None: + assert extract_response_type(BinaryAPIResponse) == bytes + assert extract_response_type(AsyncBinaryAPIResponse) == bytes + + +class PydanticModel(pydantic.BaseModel): ... + + +def test_response_parse_mismatched_basemodel(client: PostGrid) -> None: + response = APIResponse( + raw=httpx.Response(200, content=b"foo"), + client=client, + stream=False, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + with pytest.raises( + TypeError, + match="Pydantic models must subclass our base model type, e.g. `from postgrid import BaseModel`", + ): + response.parse(to=PydanticModel) + + +@pytest.mark.asyncio +async def test_async_response_parse_mismatched_basemodel(async_client: AsyncPostGrid) -> None: + response = AsyncAPIResponse( + raw=httpx.Response(200, content=b"foo"), + client=async_client, + stream=False, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + with pytest.raises( + TypeError, + match="Pydantic models must subclass our base model type, e.g. `from postgrid import BaseModel`", + ): + await response.parse(to=PydanticModel) + + +def test_response_parse_custom_stream(client: PostGrid) -> None: + response = APIResponse( + raw=httpx.Response(200, content=b"foo"), + client=client, + stream=True, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + stream = response.parse(to=Stream[int]) + assert stream._cast_to == int + + +@pytest.mark.asyncio +async def test_async_response_parse_custom_stream(async_client: AsyncPostGrid) -> None: + response = AsyncAPIResponse( + raw=httpx.Response(200, content=b"foo"), + client=async_client, + stream=True, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + stream = await response.parse(to=Stream[int]) + assert stream._cast_to == int + + +class CustomModel(BaseModel): + foo: str + bar: int + + +def test_response_parse_custom_model(client: PostGrid) -> None: + response = APIResponse( + raw=httpx.Response(200, content=json.dumps({"foo": "hello!", "bar": 2})), + client=client, + stream=False, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + obj = response.parse(to=CustomModel) + assert obj.foo == "hello!" + assert obj.bar == 2 + + +@pytest.mark.asyncio +async def test_async_response_parse_custom_model(async_client: AsyncPostGrid) -> None: + response = AsyncAPIResponse( + raw=httpx.Response(200, content=json.dumps({"foo": "hello!", "bar": 2})), + client=async_client, + stream=False, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + obj = await response.parse(to=CustomModel) + assert obj.foo == "hello!" + assert obj.bar == 2 + + +def test_response_parse_annotated_type(client: PostGrid) -> None: + response = APIResponse( + raw=httpx.Response(200, content=json.dumps({"foo": "hello!", "bar": 2})), + client=client, + stream=False, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + obj = response.parse( + to=cast("type[CustomModel]", Annotated[CustomModel, "random metadata"]), + ) + assert obj.foo == "hello!" + assert obj.bar == 2 + + +async def test_async_response_parse_annotated_type(async_client: AsyncPostGrid) -> None: + response = AsyncAPIResponse( + raw=httpx.Response(200, content=json.dumps({"foo": "hello!", "bar": 2})), + client=async_client, + stream=False, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + obj = await response.parse( + to=cast("type[CustomModel]", Annotated[CustomModel, "random metadata"]), + ) + assert obj.foo == "hello!" + assert obj.bar == 2 + + +@pytest.mark.parametrize( + "content, expected", + [ + ("false", False), + ("true", True), + ("False", False), + ("True", True), + ("TrUe", True), + ("FalSe", False), + ], +) +def test_response_parse_bool(client: PostGrid, content: str, expected: bool) -> None: + response = APIResponse( + raw=httpx.Response(200, content=content), + client=client, + stream=False, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + result = response.parse(to=bool) + assert result is expected + + +@pytest.mark.parametrize( + "content, expected", + [ + ("false", False), + ("true", True), + ("False", False), + ("True", True), + ("TrUe", True), + ("FalSe", False), + ], +) +async def test_async_response_parse_bool(client: AsyncPostGrid, content: str, expected: bool) -> None: + response = AsyncAPIResponse( + raw=httpx.Response(200, content=content), + client=client, + stream=False, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + result = await response.parse(to=bool) + assert result is expected + + +class OtherModel(BaseModel): + a: str + + +@pytest.mark.parametrize("client", [False], indirect=True) # loose validation +def test_response_parse_expect_model_union_non_json_content(client: PostGrid) -> None: + response = APIResponse( + raw=httpx.Response(200, content=b"foo", headers={"Content-Type": "application/text"}), + client=client, + stream=False, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + obj = response.parse(to=cast(Any, Union[CustomModel, OtherModel])) + assert isinstance(obj, str) + assert obj == "foo" + + +@pytest.mark.asyncio +@pytest.mark.parametrize("async_client", [False], indirect=True) # loose validation +async def test_async_response_parse_expect_model_union_non_json_content(async_client: AsyncPostGrid) -> None: + response = AsyncAPIResponse( + raw=httpx.Response(200, content=b"foo", headers={"Content-Type": "application/text"}), + client=async_client, + stream=False, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + obj = await response.parse(to=cast(Any, Union[CustomModel, OtherModel])) + assert isinstance(obj, str) + assert obj == "foo" diff --git a/tests/test_return_envelopes.py b/tests/test_return_envelopes.py deleted file mode 100644 index ba9bd5d..0000000 --- a/tests/test_return_envelopes.py +++ /dev/null @@ -1,74 +0,0 @@ -import postgrid -import os -import random - - -def setup_module(): - postgrid.pm_key = os.environ.get("PM_API_KEY") - - -def create_test_return_envelope(retrieve_existing): - try: - return postgrid.ReturnEnvelope.create( - to={ - "first_name": "Test", - "last_name": "Contact", - "company_name": "Test Company", - "address_line1": "20 Bay St, Toronto, ON M9V 4V1", - "country_code": "CA", - "job_title": "Test", - "metadata": {"dedup": 0 if retrieve_existing else random.random()}, - }, - description="SDK Test", - ) - except postgrid.PMError: - # Ideally we would only hit this codepath if we're retrieving an existing return envelope - assert retrieve_existing - - return postgrid.ReturnEnvelope.list().data[0] - - -def test_create(): - res = create_test_return_envelope(False) - - assert isinstance(res, postgrid.ReturnEnvelope) - assert res.available == 0 - - -def test_create_order(): - res = create_test_return_envelope(True) - order_res = postgrid.ReturnEnvelopeOrder.create(res.id, 10000) - - assert isinstance(order_res, postgrid.ReturnEnvelopeOrder) - assert order_res.quantity_ordered == 10000 - - -def test_list_orders(): - res = create_test_return_envelope(True) - order_res = postgrid.ReturnEnvelopeOrder.create(res.id, 10000) - orders = postgrid.ReturnEnvelopeOrder.list(res.id) - - assert isinstance(orders, postgrid.List) - assert len(orders.data) >= 1 - - -def test_fill_order(): - res = create_test_return_envelope(True) - order_res = postgrid.ReturnEnvelopeOrder.create(res.id, 10000) - - order = postgrid.ReturnEnvelopeOrder.fill(res.id, order_res.id) - res = postgrid.ReturnEnvelope.retrieve(res.id) - - assert isinstance(order, postgrid.ReturnEnvelopeOrder) - assert order.status == "filled" - assert res.available >= 10000 - - -def test_cancel_order(): - res = create_test_return_envelope(True) - order_res = postgrid.ReturnEnvelopeOrder.create(res.id, 10000) - - order = postgrid.ReturnEnvelopeOrder.delete(res.id, order_res.id) - - assert isinstance(order, postgrid.ReturnEnvelopeOrder) - assert order.status == "cancelled" diff --git a/tests/test_streaming.py b/tests/test_streaming.py new file mode 100644 index 0000000..e78ee3d --- /dev/null +++ b/tests/test_streaming.py @@ -0,0 +1,248 @@ +from __future__ import annotations + +from typing import Iterator, AsyncIterator + +import httpx +import pytest + +from postgrid import PostGrid, AsyncPostGrid +from postgrid._streaming import Stream, AsyncStream, ServerSentEvent + + +@pytest.mark.asyncio +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +async def test_basic(sync: bool, client: PostGrid, async_client: AsyncPostGrid) -> None: + def body() -> Iterator[bytes]: + yield b"event: completion\n" + yield b'data: {"foo":true}\n' + yield b"\n" + + iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) + + sse = await iter_next(iterator) + assert sse.event == "completion" + assert sse.json() == {"foo": True} + + await assert_empty_iter(iterator) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +async def test_data_missing_event(sync: bool, client: PostGrid, async_client: AsyncPostGrid) -> None: + def body() -> Iterator[bytes]: + yield b'data: {"foo":true}\n' + yield b"\n" + + iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) + + sse = await iter_next(iterator) + assert sse.event is None + assert sse.json() == {"foo": True} + + await assert_empty_iter(iterator) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +async def test_event_missing_data(sync: bool, client: PostGrid, async_client: AsyncPostGrid) -> None: + def body() -> Iterator[bytes]: + yield b"event: ping\n" + yield b"\n" + + iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) + + sse = await iter_next(iterator) + assert sse.event == "ping" + assert sse.data == "" + + await assert_empty_iter(iterator) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +async def test_multiple_events(sync: bool, client: PostGrid, async_client: AsyncPostGrid) -> None: + def body() -> Iterator[bytes]: + yield b"event: ping\n" + yield b"\n" + yield b"event: completion\n" + yield b"\n" + + iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) + + sse = await iter_next(iterator) + assert sse.event == "ping" + assert sse.data == "" + + sse = await iter_next(iterator) + assert sse.event == "completion" + assert sse.data == "" + + await assert_empty_iter(iterator) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +async def test_multiple_events_with_data(sync: bool, client: PostGrid, async_client: AsyncPostGrid) -> None: + def body() -> Iterator[bytes]: + yield b"event: ping\n" + yield b'data: {"foo":true}\n' + yield b"\n" + yield b"event: completion\n" + yield b'data: {"bar":false}\n' + yield b"\n" + + iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) + + sse = await iter_next(iterator) + assert sse.event == "ping" + assert sse.json() == {"foo": True} + + sse = await iter_next(iterator) + assert sse.event == "completion" + assert sse.json() == {"bar": False} + + await assert_empty_iter(iterator) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +async def test_multiple_data_lines_with_empty_line(sync: bool, client: PostGrid, async_client: AsyncPostGrid) -> None: + def body() -> Iterator[bytes]: + yield b"event: ping\n" + yield b"data: {\n" + yield b'data: "foo":\n' + yield b"data: \n" + yield b"data:\n" + yield b"data: true}\n" + yield b"\n\n" + + iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) + + sse = await iter_next(iterator) + assert sse.event == "ping" + assert sse.json() == {"foo": True} + assert sse.data == '{\n"foo":\n\n\ntrue}' + + await assert_empty_iter(iterator) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +async def test_data_json_escaped_double_new_line(sync: bool, client: PostGrid, async_client: AsyncPostGrid) -> None: + def body() -> Iterator[bytes]: + yield b"event: ping\n" + yield b'data: {"foo": "my long\\n\\ncontent"}' + yield b"\n\n" + + iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) + + sse = await iter_next(iterator) + assert sse.event == "ping" + assert sse.json() == {"foo": "my long\n\ncontent"} + + await assert_empty_iter(iterator) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +async def test_multiple_data_lines(sync: bool, client: PostGrid, async_client: AsyncPostGrid) -> None: + def body() -> Iterator[bytes]: + yield b"event: ping\n" + yield b"data: {\n" + yield b'data: "foo":\n' + yield b"data: true}\n" + yield b"\n\n" + + iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) + + sse = await iter_next(iterator) + assert sse.event == "ping" + assert sse.json() == {"foo": True} + + await assert_empty_iter(iterator) + + +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +async def test_special_new_line_character( + sync: bool, + client: PostGrid, + async_client: AsyncPostGrid, +) -> None: + def body() -> Iterator[bytes]: + yield b'data: {"content":" culpa"}\n' + yield b"\n" + yield b'data: {"content":" \xe2\x80\xa8"}\n' + yield b"\n" + yield b'data: {"content":"foo"}\n' + yield b"\n" + + iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) + + sse = await iter_next(iterator) + assert sse.event is None + assert sse.json() == {"content": " culpa"} + + sse = await iter_next(iterator) + assert sse.event is None + assert sse.json() == {"content": " 
"} + + sse = await iter_next(iterator) + assert sse.event is None + assert sse.json() == {"content": "foo"} + + await assert_empty_iter(iterator) + + +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +async def test_multi_byte_character_multiple_chunks( + sync: bool, + client: PostGrid, + async_client: AsyncPostGrid, +) -> None: + def body() -> Iterator[bytes]: + yield b'data: {"content":"' + # bytes taken from the string 'известни' and arbitrarily split + # so that some multi-byte characters span multiple chunks + yield b"\xd0" + yield b"\xb8\xd0\xb7\xd0" + yield b"\xb2\xd0\xb5\xd1\x81\xd1\x82\xd0\xbd\xd0\xb8" + yield b'"}\n' + yield b"\n" + + iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) + + sse = await iter_next(iterator) + assert sse.event is None + assert sse.json() == {"content": "известни"} + + +async def to_aiter(iter: Iterator[bytes]) -> AsyncIterator[bytes]: + for chunk in iter: + yield chunk + + +async def iter_next(iter: Iterator[ServerSentEvent] | AsyncIterator[ServerSentEvent]) -> ServerSentEvent: + if isinstance(iter, AsyncIterator): + return await iter.__anext__() + + return next(iter) + + +async def assert_empty_iter(iter: Iterator[ServerSentEvent] | AsyncIterator[ServerSentEvent]) -> None: + with pytest.raises((StopAsyncIteration, RuntimeError)): + await iter_next(iter) + + +def make_event_iterator( + content: Iterator[bytes], + *, + sync: bool, + client: PostGrid, + async_client: AsyncPostGrid, +) -> Iterator[ServerSentEvent] | AsyncIterator[ServerSentEvent]: + if sync: + return Stream(cast_to=object, client=client, response=httpx.Response(200, content=content))._iter_events() + + return AsyncStream( + cast_to=object, client=async_client, response=httpx.Response(200, content=to_aiter(content)) + )._iter_events() diff --git a/tests/test_template.py b/tests/test_template.py deleted file mode 100644 index 14d6e0c..0000000 --- a/tests/test_template.py +++ /dev/null @@ -1,65 +0,0 @@ -import postgrid -import os - - -def setup_module(): - postgrid.pm_key = os.environ.get("PM_API_KEY") - - -def create_test_template(): - return postgrid.Template.create( - html="test template, hello {{to.firstName}}!", - description="Test", - metadata={"test": [10, 20]}, - ) - - -def test_create(): - template = create_test_template() - - assert isinstance(template, postgrid.Template) - assert isinstance(template.html, str) - assert isinstance(template.description, str) - assert template.html == "test template, hello {{to.firstName}}!" - assert template.description == "Test" - - -def test_retrieve(): - template = create_test_template() - assert isinstance(template, postgrid.Template) - - template = postgrid.Template.retrieve(template.id) - assert isinstance(template, postgrid.Template) - - -def test_list(): - template = create_test_template() - list_ = postgrid.Template.list() - - assert list_.total_count >= 1 - assert list_.skip == 0 - assert isinstance(list_.data[0], postgrid.Template) - assert list_.data[0].html == template.html - assert list_.data[0].description == template.description - - -def test_update(): - template = create_test_template() - assert isinstance(template, postgrid.Template) - - template = postgrid.Template.update( - template.id, html="updated template", description="updated description" - ) - assert isinstance(template, postgrid.Template) - assert template.html == "updated template" - assert template.description == "updated description" - - -def test_delete(): - template = create_test_template() - assert isinstance(template, postgrid.Template) - - res = postgrid.Template.delete(template.id) - - assert isinstance(res, postgrid.Template) - assert res.deleted == True diff --git a/tests/test_transform.py b/tests/test_transform.py new file mode 100644 index 0000000..7dde06d --- /dev/null +++ b/tests/test_transform.py @@ -0,0 +1,434 @@ +from __future__ import annotations + +import io +import pathlib +from typing import Any, Dict, List, Union, TypeVar, Iterable, Optional, cast +from datetime import date, datetime +from typing_extensions import Required, Annotated, TypedDict + +import pytest + +from postgrid._types import Base64FileInput +from postgrid._utils import ( + PropertyInfo, + transform as _transform, + parse_datetime, + async_transform as _async_transform, +) +from postgrid._compat import PYDANTIC_V2 +from postgrid._models import BaseModel + +_T = TypeVar("_T") + +SAMPLE_FILE_PATH = pathlib.Path(__file__).parent.joinpath("sample_file.txt") + + +async def transform( + data: _T, + expected_type: object, + use_async: bool, +) -> _T: + if use_async: + return await _async_transform(data, expected_type=expected_type) + + return _transform(data, expected_type=expected_type) + + +parametrize = pytest.mark.parametrize("use_async", [False, True], ids=["sync", "async"]) + + +class Foo1(TypedDict): + foo_bar: Annotated[str, PropertyInfo(alias="fooBar")] + + +@parametrize +@pytest.mark.asyncio +async def test_top_level_alias(use_async: bool) -> None: + assert await transform({"foo_bar": "hello"}, expected_type=Foo1, use_async=use_async) == {"fooBar": "hello"} + + +class Foo2(TypedDict): + bar: Bar2 + + +class Bar2(TypedDict): + this_thing: Annotated[int, PropertyInfo(alias="this__thing")] + baz: Annotated[Baz2, PropertyInfo(alias="Baz")] + + +class Baz2(TypedDict): + my_baz: Annotated[str, PropertyInfo(alias="myBaz")] + + +@parametrize +@pytest.mark.asyncio +async def test_recursive_typeddict(use_async: bool) -> None: + assert await transform({"bar": {"this_thing": 1}}, Foo2, use_async) == {"bar": {"this__thing": 1}} + assert await transform({"bar": {"baz": {"my_baz": "foo"}}}, Foo2, use_async) == {"bar": {"Baz": {"myBaz": "foo"}}} + + +class Foo3(TypedDict): + things: List[Bar3] + + +class Bar3(TypedDict): + my_field: Annotated[str, PropertyInfo(alias="myField")] + + +@parametrize +@pytest.mark.asyncio +async def test_list_of_typeddict(use_async: bool) -> None: + result = await transform({"things": [{"my_field": "foo"}, {"my_field": "foo2"}]}, Foo3, use_async) + assert result == {"things": [{"myField": "foo"}, {"myField": "foo2"}]} + + +class Foo4(TypedDict): + foo: Union[Bar4, Baz4] + + +class Bar4(TypedDict): + foo_bar: Annotated[str, PropertyInfo(alias="fooBar")] + + +class Baz4(TypedDict): + foo_baz: Annotated[str, PropertyInfo(alias="fooBaz")] + + +@parametrize +@pytest.mark.asyncio +async def test_union_of_typeddict(use_async: bool) -> None: + assert await transform({"foo": {"foo_bar": "bar"}}, Foo4, use_async) == {"foo": {"fooBar": "bar"}} + assert await transform({"foo": {"foo_baz": "baz"}}, Foo4, use_async) == {"foo": {"fooBaz": "baz"}} + assert await transform({"foo": {"foo_baz": "baz", "foo_bar": "bar"}}, Foo4, use_async) == { + "foo": {"fooBaz": "baz", "fooBar": "bar"} + } + + +class Foo5(TypedDict): + foo: Annotated[Union[Bar4, List[Baz4]], PropertyInfo(alias="FOO")] + + +class Bar5(TypedDict): + foo_bar: Annotated[str, PropertyInfo(alias="fooBar")] + + +class Baz5(TypedDict): + foo_baz: Annotated[str, PropertyInfo(alias="fooBaz")] + + +@parametrize +@pytest.mark.asyncio +async def test_union_of_list(use_async: bool) -> None: + assert await transform({"foo": {"foo_bar": "bar"}}, Foo5, use_async) == {"FOO": {"fooBar": "bar"}} + assert await transform( + { + "foo": [ + {"foo_baz": "baz"}, + {"foo_baz": "baz"}, + ] + }, + Foo5, + use_async, + ) == {"FOO": [{"fooBaz": "baz"}, {"fooBaz": "baz"}]} + + +class Foo6(TypedDict): + bar: Annotated[str, PropertyInfo(alias="Bar")] + + +@parametrize +@pytest.mark.asyncio +async def test_includes_unknown_keys(use_async: bool) -> None: + assert await transform({"bar": "bar", "baz_": {"FOO": 1}}, Foo6, use_async) == { + "Bar": "bar", + "baz_": {"FOO": 1}, + } + + +class Foo7(TypedDict): + bar: Annotated[List[Bar7], PropertyInfo(alias="bAr")] + foo: Bar7 + + +class Bar7(TypedDict): + foo: str + + +@parametrize +@pytest.mark.asyncio +async def test_ignores_invalid_input(use_async: bool) -> None: + assert await transform({"bar": ""}, Foo7, use_async) == {"bAr": ""} + assert await transform({"foo": ""}, Foo7, use_async) == {"foo": ""} + + +class DatetimeDict(TypedDict, total=False): + foo: Annotated[datetime, PropertyInfo(format="iso8601")] + + bar: Annotated[Optional[datetime], PropertyInfo(format="iso8601")] + + required: Required[Annotated[Optional[datetime], PropertyInfo(format="iso8601")]] + + list_: Required[Annotated[Optional[List[datetime]], PropertyInfo(format="iso8601")]] + + union: Annotated[Union[int, datetime], PropertyInfo(format="iso8601")] + + +class DateDict(TypedDict, total=False): + foo: Annotated[date, PropertyInfo(format="iso8601")] + + +class DatetimeModel(BaseModel): + foo: datetime + + +class DateModel(BaseModel): + foo: Optional[date] + + +@parametrize +@pytest.mark.asyncio +async def test_iso8601_format(use_async: bool) -> None: + dt = datetime.fromisoformat("2023-02-23T14:16:36.337692+00:00") + tz = "Z" if PYDANTIC_V2 else "+00:00" + assert await transform({"foo": dt}, DatetimeDict, use_async) == {"foo": "2023-02-23T14:16:36.337692+00:00"} # type: ignore[comparison-overlap] + assert await transform(DatetimeModel(foo=dt), Any, use_async) == {"foo": "2023-02-23T14:16:36.337692" + tz} # type: ignore[comparison-overlap] + + dt = dt.replace(tzinfo=None) + assert await transform({"foo": dt}, DatetimeDict, use_async) == {"foo": "2023-02-23T14:16:36.337692"} # type: ignore[comparison-overlap] + assert await transform(DatetimeModel(foo=dt), Any, use_async) == {"foo": "2023-02-23T14:16:36.337692"} # type: ignore[comparison-overlap] + + assert await transform({"foo": None}, DateDict, use_async) == {"foo": None} # type: ignore[comparison-overlap] + assert await transform(DateModel(foo=None), Any, use_async) == {"foo": None} # type: ignore + assert await transform({"foo": date.fromisoformat("2023-02-23")}, DateDict, use_async) == {"foo": "2023-02-23"} # type: ignore[comparison-overlap] + assert await transform(DateModel(foo=date.fromisoformat("2023-02-23")), DateDict, use_async) == { + "foo": "2023-02-23" + } # type: ignore[comparison-overlap] + + +@parametrize +@pytest.mark.asyncio +async def test_optional_iso8601_format(use_async: bool) -> None: + dt = datetime.fromisoformat("2023-02-23T14:16:36.337692+00:00") + assert await transform({"bar": dt}, DatetimeDict, use_async) == {"bar": "2023-02-23T14:16:36.337692+00:00"} # type: ignore[comparison-overlap] + + assert await transform({"bar": None}, DatetimeDict, use_async) == {"bar": None} + + +@parametrize +@pytest.mark.asyncio +async def test_required_iso8601_format(use_async: bool) -> None: + dt = datetime.fromisoformat("2023-02-23T14:16:36.337692+00:00") + assert await transform({"required": dt}, DatetimeDict, use_async) == { + "required": "2023-02-23T14:16:36.337692+00:00" + } # type: ignore[comparison-overlap] + + assert await transform({"required": None}, DatetimeDict, use_async) == {"required": None} + + +@parametrize +@pytest.mark.asyncio +async def test_union_datetime(use_async: bool) -> None: + dt = datetime.fromisoformat("2023-02-23T14:16:36.337692+00:00") + assert await transform({"union": dt}, DatetimeDict, use_async) == { # type: ignore[comparison-overlap] + "union": "2023-02-23T14:16:36.337692+00:00" + } + + assert await transform({"union": "foo"}, DatetimeDict, use_async) == {"union": "foo"} + + +@parametrize +@pytest.mark.asyncio +async def test_nested_list_iso6801_format(use_async: bool) -> None: + dt1 = datetime.fromisoformat("2023-02-23T14:16:36.337692+00:00") + dt2 = parse_datetime("2022-01-15T06:34:23Z") + assert await transform({"list_": [dt1, dt2]}, DatetimeDict, use_async) == { # type: ignore[comparison-overlap] + "list_": ["2023-02-23T14:16:36.337692+00:00", "2022-01-15T06:34:23+00:00"] + } + + +@parametrize +@pytest.mark.asyncio +async def test_datetime_custom_format(use_async: bool) -> None: + dt = parse_datetime("2022-01-15T06:34:23Z") + + result = await transform(dt, Annotated[datetime, PropertyInfo(format="custom", format_template="%H")], use_async) + assert result == "06" # type: ignore[comparison-overlap] + + +class DateDictWithRequiredAlias(TypedDict, total=False): + required_prop: Required[Annotated[date, PropertyInfo(format="iso8601", alias="prop")]] + + +@parametrize +@pytest.mark.asyncio +async def test_datetime_with_alias(use_async: bool) -> None: + assert await transform({"required_prop": None}, DateDictWithRequiredAlias, use_async) == {"prop": None} # type: ignore[comparison-overlap] + assert await transform( + {"required_prop": date.fromisoformat("2023-02-23")}, DateDictWithRequiredAlias, use_async + ) == {"prop": "2023-02-23"} # type: ignore[comparison-overlap] + + +class MyModel(BaseModel): + foo: str + + +@parametrize +@pytest.mark.asyncio +async def test_pydantic_model_to_dictionary(use_async: bool) -> None: + assert cast(Any, await transform(MyModel(foo="hi!"), Any, use_async)) == {"foo": "hi!"} + assert cast(Any, await transform(MyModel.construct(foo="hi!"), Any, use_async)) == {"foo": "hi!"} + + +@parametrize +@pytest.mark.asyncio +async def test_pydantic_empty_model(use_async: bool) -> None: + assert cast(Any, await transform(MyModel.construct(), Any, use_async)) == {} + + +@parametrize +@pytest.mark.asyncio +async def test_pydantic_unknown_field(use_async: bool) -> None: + assert cast(Any, await transform(MyModel.construct(my_untyped_field=True), Any, use_async)) == { + "my_untyped_field": True + } + + +@parametrize +@pytest.mark.asyncio +async def test_pydantic_mismatched_types(use_async: bool) -> None: + model = MyModel.construct(foo=True) + if PYDANTIC_V2: + with pytest.warns(UserWarning): + params = await transform(model, Any, use_async) + else: + params = await transform(model, Any, use_async) + assert cast(Any, params) == {"foo": True} + + +@parametrize +@pytest.mark.asyncio +async def test_pydantic_mismatched_object_type(use_async: bool) -> None: + model = MyModel.construct(foo=MyModel.construct(hello="world")) + if PYDANTIC_V2: + with pytest.warns(UserWarning): + params = await transform(model, Any, use_async) + else: + params = await transform(model, Any, use_async) + assert cast(Any, params) == {"foo": {"hello": "world"}} + + +class ModelNestedObjects(BaseModel): + nested: MyModel + + +@parametrize +@pytest.mark.asyncio +async def test_pydantic_nested_objects(use_async: bool) -> None: + model = ModelNestedObjects.construct(nested={"foo": "stainless"}) + assert isinstance(model.nested, MyModel) + assert cast(Any, await transform(model, Any, use_async)) == {"nested": {"foo": "stainless"}} + + +class ModelWithDefaultField(BaseModel): + foo: str + with_none_default: Union[str, None] = None + with_str_default: str = "foo" + + +@parametrize +@pytest.mark.asyncio +async def test_pydantic_default_field(use_async: bool) -> None: + # should be excluded when defaults are used + model = ModelWithDefaultField.construct() + assert model.with_none_default is None + assert model.with_str_default == "foo" + assert cast(Any, await transform(model, Any, use_async)) == {} + + # should be included when the default value is explicitly given + model = ModelWithDefaultField.construct(with_none_default=None, with_str_default="foo") + assert model.with_none_default is None + assert model.with_str_default == "foo" + assert cast(Any, await transform(model, Any, use_async)) == {"with_none_default": None, "with_str_default": "foo"} + + # should be included when a non-default value is explicitly given + model = ModelWithDefaultField.construct(with_none_default="bar", with_str_default="baz") + assert model.with_none_default == "bar" + assert model.with_str_default == "baz" + assert cast(Any, await transform(model, Any, use_async)) == {"with_none_default": "bar", "with_str_default": "baz"} + + +class TypedDictIterableUnion(TypedDict): + foo: Annotated[Union[Bar8, Iterable[Baz8]], PropertyInfo(alias="FOO")] + + +class Bar8(TypedDict): + foo_bar: Annotated[str, PropertyInfo(alias="fooBar")] + + +class Baz8(TypedDict): + foo_baz: Annotated[str, PropertyInfo(alias="fooBaz")] + + +@parametrize +@pytest.mark.asyncio +async def test_iterable_of_dictionaries(use_async: bool) -> None: + assert await transform({"foo": [{"foo_baz": "bar"}]}, TypedDictIterableUnion, use_async) == { + "FOO": [{"fooBaz": "bar"}] + } + assert cast(Any, await transform({"foo": ({"foo_baz": "bar"},)}, TypedDictIterableUnion, use_async)) == { + "FOO": [{"fooBaz": "bar"}] + } + + def my_iter() -> Iterable[Baz8]: + yield {"foo_baz": "hello"} + yield {"foo_baz": "world"} + + assert await transform({"foo": my_iter()}, TypedDictIterableUnion, use_async) == { + "FOO": [{"fooBaz": "hello"}, {"fooBaz": "world"}] + } + + +@parametrize +@pytest.mark.asyncio +async def test_dictionary_items(use_async: bool) -> None: + class DictItems(TypedDict): + foo_baz: Annotated[str, PropertyInfo(alias="fooBaz")] + + assert await transform({"foo": {"foo_baz": "bar"}}, Dict[str, DictItems], use_async) == {"foo": {"fooBaz": "bar"}} + + +class TypedDictIterableUnionStr(TypedDict): + foo: Annotated[Union[str, Iterable[Baz8]], PropertyInfo(alias="FOO")] + + +@parametrize +@pytest.mark.asyncio +async def test_iterable_union_str(use_async: bool) -> None: + assert await transform({"foo": "bar"}, TypedDictIterableUnionStr, use_async) == {"FOO": "bar"} + assert cast(Any, await transform(iter([{"foo_baz": "bar"}]), Union[str, Iterable[Baz8]], use_async)) == [ + {"fooBaz": "bar"} + ] + + +class TypedDictBase64Input(TypedDict): + foo: Annotated[Union[str, Base64FileInput], PropertyInfo(format="base64")] + + +@parametrize +@pytest.mark.asyncio +async def test_base64_file_input(use_async: bool) -> None: + # strings are left as-is + assert await transform({"foo": "bar"}, TypedDictBase64Input, use_async) == {"foo": "bar"} + + # pathlib.Path is automatically converted to base64 + assert await transform({"foo": SAMPLE_FILE_PATH}, TypedDictBase64Input, use_async) == { + "foo": "SGVsbG8sIHdvcmxkIQo=" + } # type: ignore[comparison-overlap] + + # io instances are automatically converted to base64 + assert await transform({"foo": io.StringIO("Hello, world!")}, TypedDictBase64Input, use_async) == { + "foo": "SGVsbG8sIHdvcmxkIQ==" + } # type: ignore[comparison-overlap] + assert await transform({"foo": io.BytesIO(b"Hello, world!")}, TypedDictBase64Input, use_async) == { + "foo": "SGVsbG8sIHdvcmxkIQ==" + } # type: ignore[comparison-overlap] diff --git a/tests/test_util.py b/tests/test_util.py deleted file mode 100644 index f4ca90c..0000000 --- a/tests/test_util.py +++ /dev/null @@ -1,27 +0,0 @@ -import postgrid - - -def test_camel_to_snake(): - assert postgrid._camel_to_snake("frontHTML") == "front_html" - assert postgrid._camel_to_snake("backHTML") == "back_html" - assert postgrid._camel_to_snake("letterID") == "letter_id" - assert postgrid._camel_to_snake("thisIsACamelStr") == "this_is_acamel_str" - - -def test_preserve_vars_pm_convert_json_value(): - letter = postgrid._pm_convert_json_value( - { - "object": "letter", - "to": {"addressLine1": "Test", "countryCode": "Test"}, - "from": {"addressLine1": "Test", "countryCode": "Test"}, - "html": "Test", - "doubleSided": True, - "mergeVariables": { - "testCamel": 1, - }, - "metadata": {"testCamel": 1}, - } - ) - - assert letter.merge_variables["testCamel"] == 1 - assert letter.metadata["testCamel"] == 1 diff --git a/tests/test_utils/test_proxy.py b/tests/test_utils/test_proxy.py new file mode 100644 index 0000000..39187d5 --- /dev/null +++ b/tests/test_utils/test_proxy.py @@ -0,0 +1,23 @@ +import operator +from typing import Any +from typing_extensions import override + +from postgrid._utils import LazyProxy + + +class RecursiveLazyProxy(LazyProxy[Any]): + @override + def __load__(self) -> Any: + return self + + def __call__(self, *_args: Any, **_kwds: Any) -> Any: + raise RuntimeError("This should never be called!") + + +def test_recursive_proxy() -> None: + proxy = RecursiveLazyProxy() + assert repr(proxy) == "RecursiveLazyProxy" + assert str(proxy) == "RecursiveLazyProxy" + assert dir(proxy) == [] + assert type(proxy).__name__ == "RecursiveLazyProxy" + assert type(operator.attrgetter("name.foo.bar.baz")(proxy)).__name__ == "RecursiveLazyProxy" diff --git a/tests/test_utils/test_typing.py b/tests/test_utils/test_typing.py new file mode 100644 index 0000000..c2431f6 --- /dev/null +++ b/tests/test_utils/test_typing.py @@ -0,0 +1,73 @@ +from __future__ import annotations + +from typing import Generic, TypeVar, cast + +from postgrid._utils import extract_type_var_from_base + +_T = TypeVar("_T") +_T2 = TypeVar("_T2") +_T3 = TypeVar("_T3") + + +class BaseGeneric(Generic[_T]): ... + + +class SubclassGeneric(BaseGeneric[_T]): ... + + +class BaseGenericMultipleTypeArgs(Generic[_T, _T2, _T3]): ... + + +class SubclassGenericMultipleTypeArgs(BaseGenericMultipleTypeArgs[_T, _T2, _T3]): ... + + +class SubclassDifferentOrderGenericMultipleTypeArgs(BaseGenericMultipleTypeArgs[_T2, _T, _T3]): ... + + +def test_extract_type_var() -> None: + assert ( + extract_type_var_from_base( + BaseGeneric[int], + index=0, + generic_bases=cast("tuple[type, ...]", (BaseGeneric,)), + ) + == int + ) + + +def test_extract_type_var_generic_subclass() -> None: + assert ( + extract_type_var_from_base( + SubclassGeneric[int], + index=0, + generic_bases=cast("tuple[type, ...]", (BaseGeneric,)), + ) + == int + ) + + +def test_extract_type_var_multiple() -> None: + typ = BaseGenericMultipleTypeArgs[int, str, None] + + generic_bases = cast("tuple[type, ...]", (BaseGenericMultipleTypeArgs,)) + assert extract_type_var_from_base(typ, index=0, generic_bases=generic_bases) == int + assert extract_type_var_from_base(typ, index=1, generic_bases=generic_bases) == str + assert extract_type_var_from_base(typ, index=2, generic_bases=generic_bases) == type(None) + + +def test_extract_type_var_generic_subclass_multiple() -> None: + typ = SubclassGenericMultipleTypeArgs[int, str, None] + + generic_bases = cast("tuple[type, ...]", (BaseGenericMultipleTypeArgs,)) + assert extract_type_var_from_base(typ, index=0, generic_bases=generic_bases) == int + assert extract_type_var_from_base(typ, index=1, generic_bases=generic_bases) == str + assert extract_type_var_from_base(typ, index=2, generic_bases=generic_bases) == type(None) + + +def test_extract_type_var_generic_subclass_different_ordering_multiple() -> None: + typ = SubclassDifferentOrderGenericMultipleTypeArgs[int, str, None] + + generic_bases = cast("tuple[type, ...]", (BaseGenericMultipleTypeArgs,)) + assert extract_type_var_from_base(typ, index=0, generic_bases=generic_bases) == int + assert extract_type_var_from_base(typ, index=1, generic_bases=generic_bases) == str + assert extract_type_var_from_base(typ, index=2, generic_bases=generic_bases) == type(None) diff --git a/tests/test_webhook.py b/tests/test_webhook.py deleted file mode 100644 index d10cd0d..0000000 --- a/tests/test_webhook.py +++ /dev/null @@ -1,30 +0,0 @@ -import postgrid -import os - -TEST_PAYLOAD = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0eXBlIjoicG9zdGNhcmQudXBkYXRlZCIsImRhdGEiOnsiaWQiOiJwb3N0Y2FyZF9pQjNMbVp4MVF2bXFvUmYyc3BrUVR4IiwibGl2ZSI6ZmFsc2UsImJhY2tIVE1MIjoiR29vZGJ5ZSwge3t0by5maXJzdE5hbWV9fSIsImZyb20iOnsiaWQiOiJjb250YWN0X3M4Z3I1eHVSTnllcHVNRkpHcUVBaEciLCJhZGRyZXNzTGluZTEiOiIyMCBCQVkgU1QiLCJhZGRyZXNzTGluZTIiOiIiLCJhZGRyZXNzU3RhdHVzIjoidmVyaWZpZWQiLCJjaXR5IjoiVE9ST05UTyIsImNvbXBhbnlOYW1lIjoiUG9zdEdyaWQiLCJjb3VudHJ5Q29kZSI6IkNBIiwicG9zdGFsT3JaaXAiOiJNOVYgNFYxIiwicHJvdmluY2VPclN0YXRlIjoiT04ifSwiZnJvbnRIVE1MIjoiSGVsbG8sIHt7dG8uZmlyc3ROYW1lfX0iLCJwYWdlQ291bnQiOjIsInJlbmRlcmVkUERGUzNLZXkiOiJ0ZXN0L3Bvc3RjYXJkX2lCM0xtWngxUXZtcW9SZjJzcGtRVHgucGRmIiwic2VuZERhdGUiOiIyMDIyLTAzLTI0VDE1OjQwOjM3LjYzMVoiLCJzaXplIjoiNng0Iiwic3RhdHVzIjoiY2FuY2VsbGVkIiwidG8iOnsiaWQiOiJjb250YWN0X2duNG1YRms4c3B4Wmt1U1JLV3dYeW4iLCJhZGRyZXNzTGluZTEiOiIyMCBCQVkgU1QiLCJhZGRyZXNzTGluZTIiOiIiLCJhZGRyZXNzU3RhdHVzIjoidmVyaWZpZWQiLCJjaXR5IjoiVE9ST05UTyIsImNvbXBhbnlOYW1lIjoiVGVzdCBDb21wYW55IiwiY291bnRyeUNvZGUiOiJDQSIsImZpcnN0TmFtZSI6IlRlc3QiLCJqb2JUaXRsZSI6IlRlc3QiLCJsYXN0TmFtZSI6IkNvbnRhY3QiLCJtZXRhZGF0YSI6eyJ0ZXN0IjpbIjEwIiwiMjAiXX0sInBvc3RhbE9yWmlwIjoiTTlWIDRWMSIsInByb3ZpbmNlT3JTdGF0ZSI6Ik9OIn0sInVzZXIiOiJ1c2VyX2NWWWZKRnpFejQ3YkRnaHVUakJXaXMiLCJjcmVhdGVkQXQiOiIyMDIyLTAzLTI0VDE1OjQwOjM3LjYzNFoiLCJ1cGRhdGVkQXQiOiIyMDIyLTAzLTI0VDE1OjQwOjQwLjIwNFoifSwiaWF0IjoxNjQ4MTM2NDQwfQ.O4O499k17BzPJ1X69ULT9ZIlWi2EnCNgR7P_JNpZzyI" -TEST_SECRET = "webhook_secret_7UPMB4r2Dkwa5KfnrUNrrv" -TEST_PAYLOAD_EVENT_TYPE = "postcard.updated" - - -def setup_module(): - postgrid.pm_key = os.environ.get("PM_API_KEY") - - -def test_construct_event(): - event = postgrid.Webhook.construct_event(TEST_PAYLOAD, TEST_SECRET) - - assert isinstance(event, postgrid.WebhookEvent) - assert event.type == TEST_PAYLOAD_EVENT_TYPE - assert isinstance(event.data, postgrid.Postcard) - - -def create_test_webhook(): - return postgrid.Webhook.create( - enabled_events=["letter.created", "postcard.updated"], - url="https://path-to-your-webhook-listener.com", - ) - - -def test_create(): - webhook = create_test_webhook() - assert isinstance(webhook, postgrid.Webhook) diff --git a/tests/utils.py b/tests/utils.py new file mode 100644 index 0000000..51557a9 --- /dev/null +++ b/tests/utils.py @@ -0,0 +1,159 @@ +from __future__ import annotations + +import os +import inspect +import traceback +import contextlib +from typing import Any, TypeVar, Iterator, cast +from datetime import date, datetime +from typing_extensions import Literal, get_args, get_origin, assert_type + +from postgrid._types import Omit, NoneType +from postgrid._utils import ( + is_dict, + is_list, + is_list_type, + is_union_type, + extract_type_arg, + is_annotated_type, + is_type_alias_type, +) +from postgrid._compat import PYDANTIC_V2, field_outer_type, get_model_fields +from postgrid._models import BaseModel + +BaseModelT = TypeVar("BaseModelT", bound=BaseModel) + + +def assert_matches_model(model: type[BaseModelT], value: BaseModelT, *, path: list[str]) -> bool: + for name, field in get_model_fields(model).items(): + field_value = getattr(value, name) + if PYDANTIC_V2: + allow_none = False + else: + # in v1 nullability was structured differently + # https://docs.pydantic.dev/2.0/migration/#required-optional-and-nullable-fields + allow_none = getattr(field, "allow_none", False) + + assert_matches_type( + field_outer_type(field), + field_value, + path=[*path, name], + allow_none=allow_none, + ) + + return True + + +# Note: the `path` argument is only used to improve error messages when `--showlocals` is used +def assert_matches_type( + type_: Any, + value: object, + *, + path: list[str], + allow_none: bool = False, +) -> None: + if is_type_alias_type(type_): + type_ = type_.__value__ + + # unwrap `Annotated[T, ...]` -> `T` + if is_annotated_type(type_): + type_ = extract_type_arg(type_, 0) + + if allow_none and value is None: + return + + if type_ is None or type_ is NoneType: + assert value is None + return + + origin = get_origin(type_) or type_ + + if is_list_type(type_): + return _assert_list_type(type_, value) + + if origin == str: + assert isinstance(value, str) + elif origin == int: + assert isinstance(value, int) + elif origin == bool: + assert isinstance(value, bool) + elif origin == float: + assert isinstance(value, float) + elif origin == bytes: + assert isinstance(value, bytes) + elif origin == datetime: + assert isinstance(value, datetime) + elif origin == date: + assert isinstance(value, date) + elif origin == object: + # nothing to do here, the expected type is unknown + pass + elif origin == Literal: + assert value in get_args(type_) + elif origin == dict: + assert is_dict(value) + + args = get_args(type_) + key_type = args[0] + items_type = args[1] + + for key, item in value.items(): + assert_matches_type(key_type, key, path=[*path, ""]) + assert_matches_type(items_type, item, path=[*path, ""]) + elif is_union_type(type_): + variants = get_args(type_) + + try: + none_index = variants.index(type(None)) + except ValueError: + pass + else: + # special case Optional[T] for better error messages + if len(variants) == 2: + if value is None: + # valid + return + + return assert_matches_type(type_=variants[not none_index], value=value, path=path) + + for i, variant in enumerate(variants): + try: + assert_matches_type(variant, value, path=[*path, f"variant {i}"]) + return + except AssertionError: + traceback.print_exc() + continue + + raise AssertionError("Did not match any variants") + elif issubclass(origin, BaseModel): + assert isinstance(value, type_) + assert assert_matches_model(type_, cast(Any, value), path=path) + elif inspect.isclass(origin) and origin.__name__ == "HttpxBinaryResponseContent": + assert value.__class__.__name__ == "HttpxBinaryResponseContent" + else: + assert None, f"Unhandled field type: {type_}" + + +def _assert_list_type(type_: type[object], value: object) -> None: + assert is_list(value) + + inner_type = get_args(type_)[0] + for entry in value: + assert_type(inner_type, entry) # type: ignore + + +@contextlib.contextmanager +def update_env(**new_env: str | Omit) -> Iterator[None]: + old = os.environ.copy() + + try: + for name, value in new_env.items(): + if isinstance(value, Omit): + os.environ.pop(name, None) + else: + os.environ[name] = value + + yield None + finally: + os.environ.clear() + os.environ.update(old) From 277f25a1fa957073e355fba73bfa783b85adb2ad Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 12 Nov 2025 00:42:27 +0000 Subject: [PATCH 02/13] chore: update SDK settings --- .github/workflows/publish-pypi.yml | 31 +++++++++ .github/workflows/release-doctor.yml | 21 ++++++ .release-please-manifest.json | 3 + CONTRIBUTING.md | 4 +- README.md | 10 +-- bin/check-release-environment | 21 ++++++ pyproject.toml | 6 +- release-please-config.json | 66 +++++++++++++++++++ src/postgrid/_version.py | 2 +- .../resources/address_verification.py | 8 +-- .../resources/intl_address_verification.py | 8 +-- .../resources/print_mail/bank_accounts.py | 8 +-- src/postgrid/resources/print_mail/boxes.py | 8 +-- .../resources/print_mail/campaigns.py | 8 +-- src/postgrid/resources/print_mail/cheques.py | 8 +-- src/postgrid/resources/print_mail/contacts.py | 8 +-- src/postgrid/resources/print_mail/letters.py | 8 +-- .../print_mail/mailing_list_imports.py | 8 +-- .../resources/print_mail/mailing_lists.py | 8 +-- .../print_mail/order_profiles/cheques.py | 8 +-- .../print_mail/order_profiles/letters.py | 8 +-- .../order_profiles/order_profiles.py | 8 +-- .../print_mail/order_profiles/postcards.py | 8 +-- .../print_mail/order_profiles/self_mailers.py | 8 +-- .../resources/print_mail/postcards.py | 8 +-- .../resources/print_mail/print_mail.py | 8 +-- .../resources/print_mail/reports/exports.py | 8 +-- .../resources/print_mail/reports/reports.py | 8 +-- .../resources/print_mail/reports/samples.py | 8 +-- .../resources/print_mail/self_mailers.py | 8 +-- .../resources/print_mail/sub_organizations.py | 8 +-- .../resources/print_mail/templates.py | 8 +-- 32 files changed, 245 insertions(+), 103 deletions(-) create mode 100644 .github/workflows/publish-pypi.yml create mode 100644 .github/workflows/release-doctor.yml create mode 100644 .release-please-manifest.json create mode 100644 bin/check-release-environment create mode 100644 release-please-config.json diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml new file mode 100644 index 0000000..5c5902c --- /dev/null +++ b/.github/workflows/publish-pypi.yml @@ -0,0 +1,31 @@ +# This workflow is triggered when a GitHub release is created. +# It can also be run manually to re-publish to PyPI in case it failed for some reason. +# You can run this workflow by navigating to https://www.github.com/postgrid/postgrid-python/actions/workflows/publish-pypi.yml +name: Publish PyPI +on: + workflow_dispatch: + + release: + types: [published] + +jobs: + publish: + name: publish + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Install Rye + run: | + curl -sSf https://rye.astral.sh/get | bash + echo "$HOME/.rye/shims" >> $GITHUB_PATH + env: + RYE_VERSION: '0.35.0' + RYE_INSTALL_OPTION: '--yes' + + - name: Publish to PyPI + run: | + bash ./bin/publish-pypi + env: + PYPI_TOKEN: ${{ secrets.POST_GRID_PYPI_TOKEN || secrets.PYPI_TOKEN }} diff --git a/.github/workflows/release-doctor.yml b/.github/workflows/release-doctor.yml new file mode 100644 index 0000000..f8cb264 --- /dev/null +++ b/.github/workflows/release-doctor.yml @@ -0,0 +1,21 @@ +name: Release Doctor +on: + pull_request: + branches: + - main + workflow_dispatch: + +jobs: + release_doctor: + name: release doctor + runs-on: ubuntu-latest + if: github.repository == 'postgrid/postgrid-python' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || startsWith(github.head_ref, 'release-please') || github.head_ref == 'next') + + steps: + - uses: actions/checkout@v4 + + - name: Check release environment + run: | + bash ./bin/check-release-environment + env: + PYPI_TOKEN: ${{ secrets.POST_GRID_PYPI_TOKEN || secrets.PYPI_TOKEN }} diff --git a/.release-please-manifest.json b/.release-please-manifest.json new file mode 100644 index 0000000..c476280 --- /dev/null +++ b/.release-please-manifest.json @@ -0,0 +1,3 @@ +{ + ".": "0.0.1-alpha.0" +} \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 29357c9..c86a673 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -63,7 +63,7 @@ If you’d like to use the repository from source, you can either install from g To install via git: ```sh -$ pip install git+ssh://git@github.com/stainless-sdks/postgrid-python.git +$ pip install git+ssh://git@github.com/postgrid/postgrid-python.git ``` Alternatively, you can build from source and install the wheel file: @@ -121,7 +121,7 @@ the changes aren't made through the automated pipeline, you may want to make rel ### Publish with a GitHub workflow -You can release to package managers by using [the `Publish PyPI` GitHub action](https://www.github.com/stainless-sdks/postgrid-python/actions/workflows/publish-pypi.yml). This requires a setup organization or repository secret to be set up. +You can release to package managers by using [the `Publish PyPI` GitHub action](https://www.github.com/postgrid/postgrid-python/actions/workflows/publish-pypi.yml). This requires a setup organization or repository secret to be set up. ### Publish manually diff --git a/README.md b/README.md index cdc910e..9a0b918 100644 --- a/README.md +++ b/README.md @@ -13,8 +13,8 @@ The REST API documentation can be found on [docs.postgrid.com](https://docs.post ## Installation ```sh -# install from this staging repo -pip install git+ssh://git@github.com/stainless-sdks/postgrid-python.git +# install from the production repo +pip install git+ssh://git@github.com/postgrid/postgrid-python.git ``` > [!NOTE] @@ -299,9 +299,9 @@ address_verification = response.parse() # get the object that `address_verifica print(address_verification.data) ``` -These methods return an [`APIResponse`](https://github.com/stainless-sdks/postgrid-python/tree/main/src/postgrid/_response.py) object. +These methods return an [`APIResponse`](https://github.com/postgrid/postgrid-python/tree/main/src/postgrid/_response.py) object. -The async client returns an [`AsyncAPIResponse`](https://github.com/stainless-sdks/postgrid-python/tree/main/src/postgrid/_response.py) with the same structure, the only difference being `await`able methods for reading the response content. +The async client returns an [`AsyncAPIResponse`](https://github.com/postgrid/postgrid-python/tree/main/src/postgrid/_response.py) with the same structure, the only difference being `await`able methods for reading the response content. #### `.with_streaming_response` @@ -407,7 +407,7 @@ This package generally follows [SemVer](https://semver.org/spec/v2.0.0.html) con We take backwards-compatibility seriously and work hard to ensure you can rely on a smooth upgrade experience. -We are keen for your feedback; please open an [issue](https://www.github.com/stainless-sdks/postgrid-python/issues) with questions, bugs, or suggestions. +We are keen for your feedback; please open an [issue](https://www.github.com/postgrid/postgrid-python/issues) with questions, bugs, or suggestions. ### Determining the installed version diff --git a/bin/check-release-environment b/bin/check-release-environment new file mode 100644 index 0000000..210603f --- /dev/null +++ b/bin/check-release-environment @@ -0,0 +1,21 @@ +#!/usr/bin/env bash + +errors=() + +if [ -z "${PYPI_TOKEN}" ]; then + errors+=("The POST_GRID_PYPI_TOKEN secret has not been set. Please set it in either this repository's secrets or your organization secrets.") +fi + +lenErrors=${#errors[@]} + +if [[ lenErrors -gt 0 ]]; then + echo -e "Found the following errors in the release environment:\n" + + for error in "${errors[@]}"; do + echo -e "- $error\n" + done + + exit 1 +fi + +echo "The environment is ready to push releases!" diff --git a/pyproject.toml b/pyproject.toml index 31a2aac..da3b4dc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,8 +34,8 @@ classifiers = [ ] [project.urls] -Homepage = "https://github.com/stainless-sdks/postgrid-python" -Repository = "https://github.com/stainless-sdks/postgrid-python" +Homepage = "https://github.com/postgrid/postgrid-python" +Repository = "https://github.com/postgrid/postgrid-python" @@ -122,7 +122,7 @@ path = "README.md" [[tool.hatch.metadata.hooks.fancy-pypi-readme.substitutions]] # replace relative links with absolute links pattern = '\[(.+?)\]\(((?!https?://)\S+?)\)' -replacement = '[\1](https://github.com/stainless-sdks/postgrid-python/tree/main/\g<2>)' +replacement = '[\1](https://github.com/postgrid/postgrid-python/tree/main/\g<2>)' [tool.pytest.ini_options] testpaths = ["tests"] diff --git a/release-please-config.json b/release-please-config.json new file mode 100644 index 0000000..dae828c --- /dev/null +++ b/release-please-config.json @@ -0,0 +1,66 @@ +{ + "packages": { + ".": {} + }, + "$schema": "https://raw.githubusercontent.com/stainless-api/release-please/main/schemas/config.json", + "include-v-in-tag": true, + "include-component-in-tag": false, + "versioning": "prerelease", + "prerelease": true, + "bump-minor-pre-major": true, + "bump-patch-for-minor-pre-major": false, + "pull-request-header": "Automated Release PR", + "pull-request-title-pattern": "release: ${version}", + "changelog-sections": [ + { + "type": "feat", + "section": "Features" + }, + { + "type": "fix", + "section": "Bug Fixes" + }, + { + "type": "perf", + "section": "Performance Improvements" + }, + { + "type": "revert", + "section": "Reverts" + }, + { + "type": "chore", + "section": "Chores" + }, + { + "type": "docs", + "section": "Documentation" + }, + { + "type": "style", + "section": "Styles" + }, + { + "type": "refactor", + "section": "Refactors" + }, + { + "type": "test", + "section": "Tests", + "hidden": true + }, + { + "type": "build", + "section": "Build System" + }, + { + "type": "ci", + "section": "Continuous Integration", + "hidden": true + } + ], + "release-type": "python", + "extra-files": [ + "src/postgrid/_version.py" + ] +} \ No newline at end of file diff --git a/src/postgrid/_version.py b/src/postgrid/_version.py index fc064f0..153b599 100644 --- a/src/postgrid/_version.py +++ b/src/postgrid/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "postgrid" -__version__ = "0.0.1-alpha.0" +__version__ = "0.0.1-alpha.0" # x-release-please-version diff --git a/src/postgrid/resources/address_verification.py b/src/postgrid/resources/address_verification.py index a7892b6..ea4cabf 100644 --- a/src/postgrid/resources/address_verification.py +++ b/src/postgrid/resources/address_verification.py @@ -34,7 +34,7 @@ def with_raw_response(self) -> AddressVerificationResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/postgrid/postgrid-python#accessing-raw-response-data-eg-headers """ return AddressVerificationResourceWithRawResponse(self) @@ -43,7 +43,7 @@ def with_streaming_response(self) -> AddressVerificationResourceWithStreamingRes """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + For more information, see https://www.github.com/postgrid/postgrid-python#with_streaming_response """ return AddressVerificationResourceWithStreamingResponse(self) @@ -172,7 +172,7 @@ def with_raw_response(self) -> AsyncAddressVerificationResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/postgrid/postgrid-python#accessing-raw-response-data-eg-headers """ return AsyncAddressVerificationResourceWithRawResponse(self) @@ -181,7 +181,7 @@ def with_streaming_response(self) -> AsyncAddressVerificationResourceWithStreami """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + For more information, see https://www.github.com/postgrid/postgrid-python#with_streaming_response """ return AsyncAddressVerificationResourceWithStreamingResponse(self) diff --git a/src/postgrid/resources/intl_address_verification.py b/src/postgrid/resources/intl_address_verification.py index b281dff..c4ffe63 100644 --- a/src/postgrid/resources/intl_address_verification.py +++ b/src/postgrid/resources/intl_address_verification.py @@ -34,7 +34,7 @@ def with_raw_response(self) -> IntlAddressVerificationResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/postgrid/postgrid-python#accessing-raw-response-data-eg-headers """ return IntlAddressVerificationResourceWithRawResponse(self) @@ -43,7 +43,7 @@ def with_streaming_response(self) -> IntlAddressVerificationResourceWithStreamin """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + For more information, see https://www.github.com/postgrid/postgrid-python#with_streaming_response """ return IntlAddressVerificationResourceWithStreamingResponse(self) @@ -162,7 +162,7 @@ def with_raw_response(self) -> AsyncIntlAddressVerificationResourceWithRawRespon This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/postgrid/postgrid-python#accessing-raw-response-data-eg-headers """ return AsyncIntlAddressVerificationResourceWithRawResponse(self) @@ -171,7 +171,7 @@ def with_streaming_response(self) -> AsyncIntlAddressVerificationResourceWithStr """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + For more information, see https://www.github.com/postgrid/postgrid-python#with_streaming_response """ return AsyncIntlAddressVerificationResourceWithStreamingResponse(self) diff --git a/src/postgrid/resources/print_mail/bank_accounts.py b/src/postgrid/resources/print_mail/bank_accounts.py index 981c838..2ca8c66 100644 --- a/src/postgrid/resources/print_mail/bank_accounts.py +++ b/src/postgrid/resources/print_mail/bank_accounts.py @@ -45,7 +45,7 @@ def with_raw_response(self) -> BankAccountsResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/postgrid/postgrid-python#accessing-raw-response-data-eg-headers """ return BankAccountsResourceWithRawResponse(self) @@ -54,7 +54,7 @@ def with_streaming_response(self) -> BankAccountsResourceWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + For more information, see https://www.github.com/postgrid/postgrid-python#with_streaming_response """ return BankAccountsResourceWithStreamingResponse(self) @@ -449,7 +449,7 @@ def with_raw_response(self) -> AsyncBankAccountsResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/postgrid/postgrid-python#accessing-raw-response-data-eg-headers """ return AsyncBankAccountsResourceWithRawResponse(self) @@ -458,7 +458,7 @@ def with_streaming_response(self) -> AsyncBankAccountsResourceWithStreamingRespo """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + For more information, see https://www.github.com/postgrid/postgrid-python#with_streaming_response """ return AsyncBankAccountsResourceWithStreamingResponse(self) diff --git a/src/postgrid/resources/print_mail/boxes.py b/src/postgrid/resources/print_mail/boxes.py index dfbcadb..1ed5234 100644 --- a/src/postgrid/resources/print_mail/boxes.py +++ b/src/postgrid/resources/print_mail/boxes.py @@ -36,7 +36,7 @@ def with_raw_response(self) -> BoxesResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/postgrid/postgrid-python#accessing-raw-response-data-eg-headers """ return BoxesResourceWithRawResponse(self) @@ -45,7 +45,7 @@ def with_streaming_response(self) -> BoxesResourceWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + For more information, see https://www.github.com/postgrid/postgrid-python#with_streaming_response """ return BoxesResourceWithStreamingResponse(self) @@ -268,7 +268,7 @@ def with_raw_response(self) -> AsyncBoxesResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/postgrid/postgrid-python#accessing-raw-response-data-eg-headers """ return AsyncBoxesResourceWithRawResponse(self) @@ -277,7 +277,7 @@ def with_streaming_response(self) -> AsyncBoxesResourceWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + For more information, see https://www.github.com/postgrid/postgrid-python#with_streaming_response """ return AsyncBoxesResourceWithStreamingResponse(self) diff --git a/src/postgrid/resources/print_mail/campaigns.py b/src/postgrid/resources/print_mail/campaigns.py index ca89d8c..81bc744 100644 --- a/src/postgrid/resources/print_mail/campaigns.py +++ b/src/postgrid/resources/print_mail/campaigns.py @@ -42,7 +42,7 @@ def with_raw_response(self) -> CampaignsResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/postgrid/postgrid-python#accessing-raw-response-data-eg-headers """ return CampaignsResourceWithRawResponse(self) @@ -51,7 +51,7 @@ def with_streaming_response(self) -> CampaignsResourceWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + For more information, see https://www.github.com/postgrid/postgrid-python#with_streaming_response """ return CampaignsResourceWithStreamingResponse(self) @@ -388,7 +388,7 @@ def with_raw_response(self) -> AsyncCampaignsResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/postgrid/postgrid-python#accessing-raw-response-data-eg-headers """ return AsyncCampaignsResourceWithRawResponse(self) @@ -397,7 +397,7 @@ def with_streaming_response(self) -> AsyncCampaignsResourceWithStreamingResponse """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + For more information, see https://www.github.com/postgrid/postgrid-python#with_streaming_response """ return AsyncCampaignsResourceWithStreamingResponse(self) diff --git a/src/postgrid/resources/print_mail/cheques.py b/src/postgrid/resources/print_mail/cheques.py index 6dd5f30..a090568 100644 --- a/src/postgrid/resources/print_mail/cheques.py +++ b/src/postgrid/resources/print_mail/cheques.py @@ -40,7 +40,7 @@ def with_raw_response(self) -> ChequesResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/postgrid/postgrid-python#accessing-raw-response-data-eg-headers """ return ChequesResourceWithRawResponse(self) @@ -49,7 +49,7 @@ def with_streaming_response(self) -> ChequesResourceWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + For more information, see https://www.github.com/postgrid/postgrid-python#with_streaming_response """ return ChequesResourceWithStreamingResponse(self) @@ -406,7 +406,7 @@ def with_raw_response(self) -> AsyncChequesResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/postgrid/postgrid-python#accessing-raw-response-data-eg-headers """ return AsyncChequesResourceWithRawResponse(self) @@ -415,7 +415,7 @@ def with_streaming_response(self) -> AsyncChequesResourceWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + For more information, see https://www.github.com/postgrid/postgrid-python#with_streaming_response """ return AsyncChequesResourceWithStreamingResponse(self) diff --git a/src/postgrid/resources/print_mail/contacts.py b/src/postgrid/resources/print_mail/contacts.py index 8559434..ed02081 100644 --- a/src/postgrid/resources/print_mail/contacts.py +++ b/src/postgrid/resources/print_mail/contacts.py @@ -37,7 +37,7 @@ def with_raw_response(self) -> ContactsResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/postgrid/postgrid-python#accessing-raw-response-data-eg-headers """ return ContactsResourceWithRawResponse(self) @@ -46,7 +46,7 @@ def with_streaming_response(self) -> ContactsResourceWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + For more information, see https://www.github.com/postgrid/postgrid-python#with_streaming_response """ return ContactsResourceWithStreamingResponse(self) @@ -407,7 +407,7 @@ def with_raw_response(self) -> AsyncContactsResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/postgrid/postgrid-python#accessing-raw-response-data-eg-headers """ return AsyncContactsResourceWithRawResponse(self) @@ -416,7 +416,7 @@ def with_streaming_response(self) -> AsyncContactsResourceWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + For more information, see https://www.github.com/postgrid/postgrid-python#with_streaming_response """ return AsyncContactsResourceWithStreamingResponse(self) diff --git a/src/postgrid/resources/print_mail/letters.py b/src/postgrid/resources/print_mail/letters.py index 853c817..50e037d 100644 --- a/src/postgrid/resources/print_mail/letters.py +++ b/src/postgrid/resources/print_mail/letters.py @@ -49,7 +49,7 @@ def with_raw_response(self) -> LettersResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/postgrid/postgrid-python#accessing-raw-response-data-eg-headers """ return LettersResourceWithRawResponse(self) @@ -58,7 +58,7 @@ def with_streaming_response(self) -> LettersResourceWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + For more information, see https://www.github.com/postgrid/postgrid-python#with_streaming_response """ return LettersResourceWithStreamingResponse(self) @@ -512,7 +512,7 @@ def with_raw_response(self) -> AsyncLettersResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/postgrid/postgrid-python#accessing-raw-response-data-eg-headers """ return AsyncLettersResourceWithRawResponse(self) @@ -521,7 +521,7 @@ def with_streaming_response(self) -> AsyncLettersResourceWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + For more information, see https://www.github.com/postgrid/postgrid-python#with_streaming_response """ return AsyncLettersResourceWithStreamingResponse(self) diff --git a/src/postgrid/resources/print_mail/mailing_list_imports.py b/src/postgrid/resources/print_mail/mailing_list_imports.py index da8c926..3ae7b44 100644 --- a/src/postgrid/resources/print_mail/mailing_list_imports.py +++ b/src/postgrid/resources/print_mail/mailing_list_imports.py @@ -42,7 +42,7 @@ def with_raw_response(self) -> MailingListImportsResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/postgrid/postgrid-python#accessing-raw-response-data-eg-headers """ return MailingListImportsResourceWithRawResponse(self) @@ -51,7 +51,7 @@ def with_streaming_response(self) -> MailingListImportsResourceWithStreamingResp """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + For more information, see https://www.github.com/postgrid/postgrid-python#with_streaming_response """ return MailingListImportsResourceWithStreamingResponse(self) @@ -308,7 +308,7 @@ def with_raw_response(self) -> AsyncMailingListImportsResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/postgrid/postgrid-python#accessing-raw-response-data-eg-headers """ return AsyncMailingListImportsResourceWithRawResponse(self) @@ -317,7 +317,7 @@ def with_streaming_response(self) -> AsyncMailingListImportsResourceWithStreamin """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + For more information, see https://www.github.com/postgrid/postgrid-python#with_streaming_response """ return AsyncMailingListImportsResourceWithStreamingResponse(self) diff --git a/src/postgrid/resources/print_mail/mailing_lists.py b/src/postgrid/resources/print_mail/mailing_lists.py index ded09ca..555563e 100644 --- a/src/postgrid/resources/print_mail/mailing_lists.py +++ b/src/postgrid/resources/print_mail/mailing_lists.py @@ -42,7 +42,7 @@ def with_raw_response(self) -> MailingListsResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/postgrid/postgrid-python#accessing-raw-response-data-eg-headers """ return MailingListsResourceWithRawResponse(self) @@ -51,7 +51,7 @@ def with_streaming_response(self) -> MailingListsResourceWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + For more information, see https://www.github.com/postgrid/postgrid-python#with_streaming_response """ return MailingListsResourceWithStreamingResponse(self) @@ -348,7 +348,7 @@ def with_raw_response(self) -> AsyncMailingListsResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/postgrid/postgrid-python#accessing-raw-response-data-eg-headers """ return AsyncMailingListsResourceWithRawResponse(self) @@ -357,7 +357,7 @@ def with_streaming_response(self) -> AsyncMailingListsResourceWithStreamingRespo """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + For more information, see https://www.github.com/postgrid/postgrid-python#with_streaming_response """ return AsyncMailingListsResourceWithStreamingResponse(self) diff --git a/src/postgrid/resources/print_mail/order_profiles/cheques.py b/src/postgrid/resources/print_mail/order_profiles/cheques.py index 64961f6..d7d46ba 100644 --- a/src/postgrid/resources/print_mail/order_profiles/cheques.py +++ b/src/postgrid/resources/print_mail/order_profiles/cheques.py @@ -53,7 +53,7 @@ def with_raw_response(self) -> ChequesResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/postgrid/postgrid-python#accessing-raw-response-data-eg-headers """ return ChequesResourceWithRawResponse(self) @@ -62,7 +62,7 @@ def with_streaming_response(self) -> ChequesResourceWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + For more information, see https://www.github.com/postgrid/postgrid-python#with_streaming_response """ return ChequesResourceWithStreamingResponse(self) @@ -395,7 +395,7 @@ def with_raw_response(self) -> AsyncChequesResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/postgrid/postgrid-python#accessing-raw-response-data-eg-headers """ return AsyncChequesResourceWithRawResponse(self) @@ -404,7 +404,7 @@ def with_streaming_response(self) -> AsyncChequesResourceWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + For more information, see https://www.github.com/postgrid/postgrid-python#with_streaming_response """ return AsyncChequesResourceWithStreamingResponse(self) diff --git a/src/postgrid/resources/print_mail/order_profiles/letters.py b/src/postgrid/resources/print_mail/order_profiles/letters.py index 62999ee..8b64b27 100644 --- a/src/postgrid/resources/print_mail/order_profiles/letters.py +++ b/src/postgrid/resources/print_mail/order_profiles/letters.py @@ -46,7 +46,7 @@ def with_raw_response(self) -> LettersResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/postgrid/postgrid-python#accessing-raw-response-data-eg-headers """ return LettersResourceWithRawResponse(self) @@ -55,7 +55,7 @@ def with_streaming_response(self) -> LettersResourceWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + For more information, see https://www.github.com/postgrid/postgrid-python#with_streaming_response """ return LettersResourceWithStreamingResponse(self) @@ -396,7 +396,7 @@ def with_raw_response(self) -> AsyncLettersResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/postgrid/postgrid-python#accessing-raw-response-data-eg-headers """ return AsyncLettersResourceWithRawResponse(self) @@ -405,7 +405,7 @@ def with_streaming_response(self) -> AsyncLettersResourceWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + For more information, see https://www.github.com/postgrid/postgrid-python#with_streaming_response """ return AsyncLettersResourceWithStreamingResponse(self) diff --git a/src/postgrid/resources/print_mail/order_profiles/order_profiles.py b/src/postgrid/resources/print_mail/order_profiles/order_profiles.py index 8f339b4..fe05144 100644 --- a/src/postgrid/resources/print_mail/order_profiles/order_profiles.py +++ b/src/postgrid/resources/print_mail/order_profiles/order_profiles.py @@ -63,7 +63,7 @@ def with_raw_response(self) -> OrderProfilesResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/postgrid/postgrid-python#accessing-raw-response-data-eg-headers """ return OrderProfilesResourceWithRawResponse(self) @@ -72,7 +72,7 @@ def with_streaming_response(self) -> OrderProfilesResourceWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + For more information, see https://www.github.com/postgrid/postgrid-python#with_streaming_response """ return OrderProfilesResourceWithStreamingResponse(self) @@ -100,7 +100,7 @@ def with_raw_response(self) -> AsyncOrderProfilesResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/postgrid/postgrid-python#accessing-raw-response-data-eg-headers """ return AsyncOrderProfilesResourceWithRawResponse(self) @@ -109,7 +109,7 @@ def with_streaming_response(self) -> AsyncOrderProfilesResourceWithStreamingResp """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + For more information, see https://www.github.com/postgrid/postgrid-python#with_streaming_response """ return AsyncOrderProfilesResourceWithStreamingResponse(self) diff --git a/src/postgrid/resources/print_mail/order_profiles/postcards.py b/src/postgrid/resources/print_mail/order_profiles/postcards.py index 0908fa7..57b1a99 100644 --- a/src/postgrid/resources/print_mail/order_profiles/postcards.py +++ b/src/postgrid/resources/print_mail/order_profiles/postcards.py @@ -44,7 +44,7 @@ def with_raw_response(self) -> PostcardsResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/postgrid/postgrid-python#accessing-raw-response-data-eg-headers """ return PostcardsResourceWithRawResponse(self) @@ -53,7 +53,7 @@ def with_streaming_response(self) -> PostcardsResourceWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + For more information, see https://www.github.com/postgrid/postgrid-python#with_streaming_response """ return PostcardsResourceWithStreamingResponse(self) @@ -345,7 +345,7 @@ def with_raw_response(self) -> AsyncPostcardsResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/postgrid/postgrid-python#accessing-raw-response-data-eg-headers """ return AsyncPostcardsResourceWithRawResponse(self) @@ -354,7 +354,7 @@ def with_streaming_response(self) -> AsyncPostcardsResourceWithStreamingResponse """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + For more information, see https://www.github.com/postgrid/postgrid-python#with_streaming_response """ return AsyncPostcardsResourceWithStreamingResponse(self) diff --git a/src/postgrid/resources/print_mail/order_profiles/self_mailers.py b/src/postgrid/resources/print_mail/order_profiles/self_mailers.py index 8cd1a21..70b6cb9 100644 --- a/src/postgrid/resources/print_mail/order_profiles/self_mailers.py +++ b/src/postgrid/resources/print_mail/order_profiles/self_mailers.py @@ -44,7 +44,7 @@ def with_raw_response(self) -> SelfMailersResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/postgrid/postgrid-python#accessing-raw-response-data-eg-headers """ return SelfMailersResourceWithRawResponse(self) @@ -53,7 +53,7 @@ def with_streaming_response(self) -> SelfMailersResourceWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + For more information, see https://www.github.com/postgrid/postgrid-python#with_streaming_response """ return SelfMailersResourceWithStreamingResponse(self) @@ -347,7 +347,7 @@ def with_raw_response(self) -> AsyncSelfMailersResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/postgrid/postgrid-python#accessing-raw-response-data-eg-headers """ return AsyncSelfMailersResourceWithRawResponse(self) @@ -356,7 +356,7 @@ def with_streaming_response(self) -> AsyncSelfMailersResourceWithStreamingRespon """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + For more information, see https://www.github.com/postgrid/postgrid-python#with_streaming_response """ return AsyncSelfMailersResourceWithStreamingResponse(self) diff --git a/src/postgrid/resources/print_mail/postcards.py b/src/postgrid/resources/print_mail/postcards.py index 950196d..6d7c4ef 100644 --- a/src/postgrid/resources/print_mail/postcards.py +++ b/src/postgrid/resources/print_mail/postcards.py @@ -48,7 +48,7 @@ def with_raw_response(self) -> PostcardsResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/postgrid/postgrid-python#accessing-raw-response-data-eg-headers """ return PostcardsResourceWithRawResponse(self) @@ -57,7 +57,7 @@ def with_streaming_response(self) -> PostcardsResourceWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + For more information, see https://www.github.com/postgrid/postgrid-python#with_streaming_response """ return PostcardsResourceWithStreamingResponse(self) @@ -534,7 +534,7 @@ def with_raw_response(self) -> AsyncPostcardsResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/postgrid/postgrid-python#accessing-raw-response-data-eg-headers """ return AsyncPostcardsResourceWithRawResponse(self) @@ -543,7 +543,7 @@ def with_streaming_response(self) -> AsyncPostcardsResourceWithStreamingResponse """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + For more information, see https://www.github.com/postgrid/postgrid-python#with_streaming_response """ return AsyncPostcardsResourceWithStreamingResponse(self) diff --git a/src/postgrid/resources/print_mail/print_mail.py b/src/postgrid/resources/print_mail/print_mail.py index 9d926fe..b9559ba 100644 --- a/src/postgrid/resources/print_mail/print_mail.py +++ b/src/postgrid/resources/print_mail/print_mail.py @@ -183,7 +183,7 @@ def with_raw_response(self) -> PrintMailResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/postgrid/postgrid-python#accessing-raw-response-data-eg-headers """ return PrintMailResourceWithRawResponse(self) @@ -192,7 +192,7 @@ def with_streaming_response(self) -> PrintMailResourceWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + For more information, see https://www.github.com/postgrid/postgrid-python#with_streaming_response """ return PrintMailResourceWithStreamingResponse(self) @@ -260,7 +260,7 @@ def with_raw_response(self) -> AsyncPrintMailResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/postgrid/postgrid-python#accessing-raw-response-data-eg-headers """ return AsyncPrintMailResourceWithRawResponse(self) @@ -269,7 +269,7 @@ def with_streaming_response(self) -> AsyncPrintMailResourceWithStreamingResponse """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + For more information, see https://www.github.com/postgrid/postgrid-python#with_streaming_response """ return AsyncPrintMailResourceWithStreamingResponse(self) diff --git a/src/postgrid/resources/print_mail/reports/exports.py b/src/postgrid/resources/print_mail/reports/exports.py index 5e781fb..3582d02 100644 --- a/src/postgrid/resources/print_mail/reports/exports.py +++ b/src/postgrid/resources/print_mail/reports/exports.py @@ -34,7 +34,7 @@ def with_raw_response(self) -> ExportsResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/postgrid/postgrid-python#accessing-raw-response-data-eg-headers """ return ExportsResourceWithRawResponse(self) @@ -43,7 +43,7 @@ def with_streaming_response(self) -> ExportsResourceWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + For more information, see https://www.github.com/postgrid/postgrid-python#with_streaming_response """ return ExportsResourceWithStreamingResponse(self) @@ -185,7 +185,7 @@ def with_raw_response(self) -> AsyncExportsResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/postgrid/postgrid-python#accessing-raw-response-data-eg-headers """ return AsyncExportsResourceWithRawResponse(self) @@ -194,7 +194,7 @@ def with_streaming_response(self) -> AsyncExportsResourceWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + For more information, see https://www.github.com/postgrid/postgrid-python#with_streaming_response """ return AsyncExportsResourceWithStreamingResponse(self) diff --git a/src/postgrid/resources/print_mail/reports/reports.py b/src/postgrid/resources/print_mail/reports/reports.py index 16a5270..0106b25 100644 --- a/src/postgrid/resources/print_mail/reports/reports.py +++ b/src/postgrid/resources/print_mail/reports/reports.py @@ -60,7 +60,7 @@ def with_raw_response(self) -> ReportsResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/postgrid/postgrid-python#accessing-raw-response-data-eg-headers """ return ReportsResourceWithRawResponse(self) @@ -69,7 +69,7 @@ def with_streaming_response(self) -> ReportsResourceWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + For more information, see https://www.github.com/postgrid/postgrid-python#with_streaming_response """ return ReportsResourceWithStreamingResponse(self) @@ -362,7 +362,7 @@ def with_raw_response(self) -> AsyncReportsResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/postgrid/postgrid-python#accessing-raw-response-data-eg-headers """ return AsyncReportsResourceWithRawResponse(self) @@ -371,7 +371,7 @@ def with_streaming_response(self) -> AsyncReportsResourceWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + For more information, see https://www.github.com/postgrid/postgrid-python#with_streaming_response """ return AsyncReportsResourceWithStreamingResponse(self) diff --git a/src/postgrid/resources/print_mail/reports/samples.py b/src/postgrid/resources/print_mail/reports/samples.py index 6d8bf8a..4b7365e 100644 --- a/src/postgrid/resources/print_mail/reports/samples.py +++ b/src/postgrid/resources/print_mail/reports/samples.py @@ -33,7 +33,7 @@ def with_raw_response(self) -> SamplesResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/postgrid/postgrid-python#accessing-raw-response-data-eg-headers """ return SamplesResourceWithRawResponse(self) @@ -42,7 +42,7 @@ def with_streaming_response(self) -> SamplesResourceWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + For more information, see https://www.github.com/postgrid/postgrid-python#with_streaming_response """ return SamplesResourceWithStreamingResponse(self) @@ -104,7 +104,7 @@ def with_raw_response(self) -> AsyncSamplesResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/postgrid/postgrid-python#accessing-raw-response-data-eg-headers """ return AsyncSamplesResourceWithRawResponse(self) @@ -113,7 +113,7 @@ def with_streaming_response(self) -> AsyncSamplesResourceWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + For more information, see https://www.github.com/postgrid/postgrid-python#with_streaming_response """ return AsyncSamplesResourceWithStreamingResponse(self) diff --git a/src/postgrid/resources/print_mail/self_mailers.py b/src/postgrid/resources/print_mail/self_mailers.py index 94ce717..738af18 100644 --- a/src/postgrid/resources/print_mail/self_mailers.py +++ b/src/postgrid/resources/print_mail/self_mailers.py @@ -48,7 +48,7 @@ def with_raw_response(self) -> SelfMailersResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/postgrid/postgrid-python#accessing-raw-response-data-eg-headers """ return SelfMailersResourceWithRawResponse(self) @@ -57,7 +57,7 @@ def with_streaming_response(self) -> SelfMailersResourceWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + For more information, see https://www.github.com/postgrid/postgrid-python#with_streaming_response """ return SelfMailersResourceWithStreamingResponse(self) @@ -534,7 +534,7 @@ def with_raw_response(self) -> AsyncSelfMailersResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/postgrid/postgrid-python#accessing-raw-response-data-eg-headers """ return AsyncSelfMailersResourceWithRawResponse(self) @@ -543,7 +543,7 @@ def with_streaming_response(self) -> AsyncSelfMailersResourceWithStreamingRespon """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + For more information, see https://www.github.com/postgrid/postgrid-python#with_streaming_response """ return AsyncSelfMailersResourceWithStreamingResponse(self) diff --git a/src/postgrid/resources/print_mail/sub_organizations.py b/src/postgrid/resources/print_mail/sub_organizations.py index 8fc95bd..d9932d6 100644 --- a/src/postgrid/resources/print_mail/sub_organizations.py +++ b/src/postgrid/resources/print_mail/sub_organizations.py @@ -38,7 +38,7 @@ def with_raw_response(self) -> SubOrganizationsResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/postgrid/postgrid-python#accessing-raw-response-data-eg-headers """ return SubOrganizationsResourceWithRawResponse(self) @@ -47,7 +47,7 @@ def with_streaming_response(self) -> SubOrganizationsResourceWithStreamingRespon """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + For more information, see https://www.github.com/postgrid/postgrid-python#with_streaming_response """ return SubOrganizationsResourceWithStreamingResponse(self) @@ -257,7 +257,7 @@ def with_raw_response(self) -> AsyncSubOrganizationsResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/postgrid/postgrid-python#accessing-raw-response-data-eg-headers """ return AsyncSubOrganizationsResourceWithRawResponse(self) @@ -266,7 +266,7 @@ def with_streaming_response(self) -> AsyncSubOrganizationsResourceWithStreamingR """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + For more information, see https://www.github.com/postgrid/postgrid-python#with_streaming_response """ return AsyncSubOrganizationsResourceWithStreamingResponse(self) diff --git a/src/postgrid/resources/print_mail/templates.py b/src/postgrid/resources/print_mail/templates.py index 0f8fe14..63dc966 100644 --- a/src/postgrid/resources/print_mail/templates.py +++ b/src/postgrid/resources/print_mail/templates.py @@ -35,7 +35,7 @@ def with_raw_response(self) -> TemplatesResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/postgrid/postgrid-python#accessing-raw-response-data-eg-headers """ return TemplatesResourceWithRawResponse(self) @@ -44,7 +44,7 @@ def with_streaming_response(self) -> TemplatesResourceWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + For more information, see https://www.github.com/postgrid/postgrid-python#with_streaming_response """ return TemplatesResourceWithStreamingResponse(self) @@ -275,7 +275,7 @@ def with_raw_response(self) -> AsyncTemplatesResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/postgrid/postgrid-python#accessing-raw-response-data-eg-headers """ return AsyncTemplatesResourceWithRawResponse(self) @@ -284,7 +284,7 @@ def with_streaming_response(self) -> AsyncTemplatesResourceWithStreamingResponse """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/postgrid-python#with_streaming_response + For more information, see https://www.github.com/postgrid/postgrid-python#with_streaming_response """ return AsyncTemplatesResourceWithStreamingResponse(self) From 8831e903672a9e505744acb42af75da40c816f61 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 12 Nov 2025 00:43:37 +0000 Subject: [PATCH 03/13] chore: update SDK settings --- README.md | 9 +++------ pyproject.toml | 2 +- requirements-dev.lock | 12 ++++++------ requirements.lock | 12 ++++++------ 4 files changed, 16 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 9a0b918..736a70b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Post Grid Python API library -[![PyPI version](https://img.shields.io/pypi/v/postgrid.svg)](https://pypi.org/project/postgrid/) +[![PyPI version](https://img.shields.io/pypi/v/postgrid-python.svg)](https://pypi.org/project/postgrid-python/) The Post Grid Python library provides convenient access to the Post Grid REST API from any Python 3.8+ application. The library includes type definitions for all request params and response fields, @@ -13,13 +13,10 @@ The REST API documentation can be found on [docs.postgrid.com](https://docs.post ## Installation ```sh -# install from the production repo -pip install git+ssh://git@github.com/postgrid/postgrid-python.git +# install from PyPI +pip install --pre postgrid-python ``` -> [!NOTE] -> Once this package is [published to PyPI](https://app.stainlessapi.com/docs/guides/publish), this will become: `pip install --pre postgrid` - ## Usage The full API of this library can be found in [api.md](api.md). diff --git a/pyproject.toml b/pyproject.toml index da3b4dc..930f677 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [project] -name = "postgrid" +name = "postgrid-python" version = "0.0.1-alpha.0" description = "The official Python library for the PostGrid API" dynamic = ["readme"] diff --git a/requirements-dev.lock b/requirements-dev.lock index cca33f7..b54f530 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -14,7 +14,7 @@ annotated-types==0.6.0 # via pydantic anyio==4.4.0 # via httpx - # via postgrid + # via postgrid-python argcomplete==3.1.2 # via nox certifi==2023.7.22 @@ -26,7 +26,7 @@ dirty-equals==0.6.0 distlib==0.3.7 # via virtualenv distro==1.8.0 - # via postgrid + # via postgrid-python exceptiongroup==1.2.2 # via anyio # via pytest @@ -37,7 +37,7 @@ h11==0.14.0 httpcore==1.0.2 # via httpx httpx==0.28.1 - # via postgrid + # via postgrid-python # via respx idna==3.4 # via anyio @@ -64,7 +64,7 @@ platformdirs==3.11.0 pluggy==1.5.0 # via pytest pydantic==2.10.3 - # via postgrid + # via postgrid-python pydantic-core==2.27.1 # via pydantic pygments==2.18.0 @@ -86,7 +86,7 @@ six==1.16.0 # via python-dateutil sniffio==1.3.0 # via anyio - # via postgrid + # via postgrid-python time-machine==2.9.0 tomli==2.0.2 # via mypy @@ -94,7 +94,7 @@ tomli==2.0.2 typing-extensions==4.12.2 # via anyio # via mypy - # via postgrid + # via postgrid-python # via pydantic # via pydantic-core # via pyright diff --git a/requirements.lock b/requirements.lock index e38ecbf..73f36ce 100644 --- a/requirements.lock +++ b/requirements.lock @@ -14,12 +14,12 @@ annotated-types==0.6.0 # via pydantic anyio==4.4.0 # via httpx - # via postgrid + # via postgrid-python certifi==2023.7.22 # via httpcore # via httpx distro==1.8.0 - # via postgrid + # via postgrid-python exceptiongroup==1.2.2 # via anyio h11==0.14.0 @@ -27,19 +27,19 @@ h11==0.14.0 httpcore==1.0.2 # via httpx httpx==0.28.1 - # via postgrid + # via postgrid-python idna==3.4 # via anyio # via httpx pydantic==2.10.3 - # via postgrid + # via postgrid-python pydantic-core==2.27.1 # via pydantic sniffio==1.3.0 # via anyio - # via postgrid + # via postgrid-python typing-extensions==4.12.2 # via anyio - # via postgrid + # via postgrid-python # via pydantic # via pydantic-core From c23572aeb87f04560983d0bdc664fecd393bab2a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 12 Nov 2025 01:00:05 +0000 Subject: [PATCH 04/13] fix(readme): rename PostGrid --- .github/workflows/publish-pypi.yml | 2 +- .github/workflows/release-doctor.yml | 2 +- LICENSE | 2 +- README.md | 12 ++++++------ SECURITY.md | 4 ++-- bin/check-release-environment | 2 +- pyproject.toml | 2 +- src/postgrid/_client.py | 4 ++-- src/postgrid/_utils/_logs.py | 2 +- tests/test_client.py | 4 ++-- 10 files changed, 18 insertions(+), 18 deletions(-) diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml index 5c5902c..346f030 100644 --- a/.github/workflows/publish-pypi.yml +++ b/.github/workflows/publish-pypi.yml @@ -28,4 +28,4 @@ jobs: run: | bash ./bin/publish-pypi env: - PYPI_TOKEN: ${{ secrets.POST_GRID_PYPI_TOKEN || secrets.PYPI_TOKEN }} + PYPI_TOKEN: ${{ secrets.POSTGRID_PYPI_TOKEN || secrets.PYPI_TOKEN }} diff --git a/.github/workflows/release-doctor.yml b/.github/workflows/release-doctor.yml index f8cb264..69b5676 100644 --- a/.github/workflows/release-doctor.yml +++ b/.github/workflows/release-doctor.yml @@ -18,4 +18,4 @@ jobs: run: | bash ./bin/check-release-environment env: - PYPI_TOKEN: ${{ secrets.POST_GRID_PYPI_TOKEN || secrets.PYPI_TOKEN }} + PYPI_TOKEN: ${{ secrets.POSTGRID_PYPI_TOKEN || secrets.PYPI_TOKEN }} diff --git a/LICENSE b/LICENSE index cbee448..ab63a50 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2025 Post Grid + Copyright 2025 PostGrid Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/README.md b/README.md index 736a70b..252d1cd 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ -# Post Grid Python API library +# PostGrid Python API library [![PyPI version](https://img.shields.io/pypi/v/postgrid-python.svg)](https://pypi.org/project/postgrid-python/) -The Post Grid Python library provides convenient access to the Post Grid REST API from any Python 3.8+ +The PostGrid Python library provides convenient access to the PostGrid REST API from any Python 3.8+ application. The library includes type definitions for all request params and response fields, and offers both synchronous and asynchronous clients powered by [httpx](https://github.com/encode/httpx). @@ -91,7 +91,7 @@ Typed requests and responses provide autocomplete and documentation within your ## Pagination -List methods in the Post Grid API are paginated. +List methods in the PostGrid API are paginated. This library provides auto-paginating iterators with each list response, so you do not have to request successive pages manually: @@ -259,10 +259,10 @@ Note that requests that time out are [retried twice by default](#retries). We use the standard library [`logging`](https://docs.python.org/3/library/logging.html) module. -You can enable logging by setting the environment variable `POST_GRID_LOG` to `info`. +You can enable logging by setting the environment variable `POSTGRID_LOG` to `info`. ```shell -$ export POST_GRID_LOG=info +$ export POSTGRID_LOG=info ``` Or to `debug` for more verbose logging. @@ -365,7 +365,7 @@ import httpx from postgrid import PostGrid, DefaultHttpxClient client = PostGrid( - # Or use the `POST_GRID_BASE_URL` env var + # Or use the `POSTGRID_BASE_URL` env var base_url="http://my.test.server.example.com:8083", http_client=DefaultHttpxClient( proxy="http://my.test.proxy.example.com", diff --git a/SECURITY.md b/SECURITY.md index 993268d..ae45ad6 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -16,9 +16,9 @@ before making any information public. ## Reporting Non-SDK Related Security Issues If you encounter security issues that are not directly related to SDKs but pertain to the services -or products provided by Post Grid please follow the respective company's security reporting guidelines. +or products provided by PostGrid please follow the respective company's security reporting guidelines. -### Post Grid Terms and Policies +### PostGrid Terms and Policies Please contact support@postgrid.com for any questions or concerns regarding security of our services. diff --git a/bin/check-release-environment b/bin/check-release-environment index 210603f..5d500ca 100644 --- a/bin/check-release-environment +++ b/bin/check-release-environment @@ -3,7 +3,7 @@ errors=() if [ -z "${PYPI_TOKEN}" ]; then - errors+=("The POST_GRID_PYPI_TOKEN secret has not been set. Please set it in either this repository's secrets or your organization secrets.") + errors+=("The POSTGRID_PYPI_TOKEN secret has not been set. Please set it in either this repository's secrets or your organization secrets.") fi lenErrors=${#errors[@]} diff --git a/pyproject.toml b/pyproject.toml index 930f677..679d690 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ description = "The official Python library for the PostGrid API" dynamic = ["readme"] license = "Apache-2.0" authors = [ -{ name = "Post Grid", email = "support@postgrid.com" }, +{ name = "PostGrid", email = "support@postgrid.com" }, ] dependencies = [ "httpx>=0.23.0, <1", diff --git a/src/postgrid/_client.py b/src/postgrid/_client.py index a0f2f9a..ff0c50e 100644 --- a/src/postgrid/_client.py +++ b/src/postgrid/_client.py @@ -97,7 +97,7 @@ def __init__( self.print_mail_api_key = print_mail_api_key if base_url is None: - base_url = os.environ.get("POST_GRID_BASE_URL") + base_url = os.environ.get("POSTGRID_BASE_URL") if base_url is None: base_url = f"https://api.postgrid.com" @@ -308,7 +308,7 @@ def __init__( self.print_mail_api_key = print_mail_api_key if base_url is None: - base_url = os.environ.get("POST_GRID_BASE_URL") + base_url = os.environ.get("POSTGRID_BASE_URL") if base_url is None: base_url = f"https://api.postgrid.com" diff --git a/src/postgrid/_utils/_logs.py b/src/postgrid/_utils/_logs.py index 54feab1..406adeb 100644 --- a/src/postgrid/_utils/_logs.py +++ b/src/postgrid/_utils/_logs.py @@ -14,7 +14,7 @@ def _basic_config() -> None: def setup_logging() -> None: - env = os.environ.get("POST_GRID_LOG") + env = os.environ.get("POSTGRID_LOG") if env == "debug": _basic_config() logger.setLevel(logging.DEBUG) diff --git a/tests/test_client.py b/tests/test_client.py index cc339ea..da88f4f 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -631,7 +631,7 @@ def test_base_url_setter(self) -> None: assert client.base_url == "https://example.com/from_setter/" def test_base_url_env(self) -> None: - with update_env(POST_GRID_BASE_URL="http://localhost:5000/from/env"): + with update_env(POSTGRID_BASE_URL="http://localhost:5000/from/env"): client = PostGrid( address_verification_api_key=address_verification_api_key, print_mail_api_key=print_mail_api_key, @@ -1524,7 +1524,7 @@ def test_base_url_setter(self) -> None: assert client.base_url == "https://example.com/from_setter/" def test_base_url_env(self) -> None: - with update_env(POST_GRID_BASE_URL="http://localhost:5000/from/env"): + with update_env(POSTGRID_BASE_URL="http://localhost:5000/from/env"): client = AsyncPostGrid( address_verification_api_key=address_verification_api_key, print_mail_api_key=print_mail_api_key, From 3b415ece8193658f82764f82f6e6c55575b3d005 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 12 Nov 2025 04:14:21 +0000 Subject: [PATCH 05/13] chore(internal): codegen related update --- .devcontainer/Dockerfile | 2 +- .github/workflows/ci.yml | 68 +- .github/workflows/publish-pypi.yml | 2 +- .gitignore | 1 - .stats.yml | 2 + .vscode/settings.json | 3 + CONTRIBUTING.md | 3 +- README.md | 67 +- SECURITY.md | 8 +- bin/check-release-environment | 2 +- bin/publish-pypi | 3 - mypy.ini | 50 -- pyproject.toml | 71 +- requirements-dev.lock | 45 +- requirements.lock | 38 +- scripts/bootstrap | 14 +- scripts/mock | 4 +- scripts/test | 4 +- scripts/utils/upload-artifact.sh | 27 + src/postgrid/__init__.py | 12 +- src/postgrid/_base_client.py | 617 ++++++++---------- src/postgrid/_client.py | 35 +- src/postgrid/_compat.py | 96 +-- src/postgrid/_files.py | 8 +- src/postgrid/_models.py | 142 ++-- src/postgrid/_qs.py | 14 +- src/postgrid/_response.py | 2 +- src/postgrid/_streaming.py | 10 +- src/postgrid/_types.py | 67 +- src/postgrid/_utils/__init__.py | 11 +- src/postgrid/_utils/_compat.py | 45 ++ src/postgrid/_utils/_datetime_parse.py | 136 ++++ src/postgrid/_utils/_proxy.py | 5 +- src/postgrid/_utils/_resources_proxy.py | 24 + src/postgrid/_utils/_transform.py | 61 +- src/postgrid/_utils/_typing.py | 11 +- src/postgrid/_utils/_utils.py | 21 +- .../resources/address_verification.py | 56 +- .../resources/intl_address_verification.py | 56 +- .../resources/print_mail/bank_accounts.py | 191 +++--- src/postgrid/resources/print_mail/boxes.py | 55 +- .../resources/print_mail/campaigns.py | 116 ++-- src/postgrid/resources/print_mail/cheques.py | 99 ++- src/postgrid/resources/print_mail/contacts.py | 204 +++--- src/postgrid/resources/print_mail/letters.py | 244 +++---- .../print_mail/mailing_list_imports.py | 72 +- .../resources/print_mail/mailing_lists.py | 82 ++- .../print_mail/order_profiles/cheques.py | 136 ++-- .../print_mail/order_profiles/letters.py | 157 +++-- .../print_mail/order_profiles/postcards.py | 109 ++-- .../print_mail/order_profiles/self_mailers.py | 109 ++-- .../resources/print_mail/postcards.py | 199 +++--- .../resources/print_mail/reports/exports.py | 33 +- .../resources/print_mail/reports/reports.py | 73 +-- .../resources/print_mail/reports/samples.py | 21 +- .../resources/print_mail/self_mailers.py | 187 +++--- .../resources/print_mail/sub_organizations.py | 51 +- .../resources/print_mail/templates.py | 63 +- .../types/print_mail/box_create_params.py | 2 +- .../cheque_retrieve_url_response.py | 1 - src/postgrid/types/print_mail/digital_only.py | 1 - .../letter_retrieve_url_response.py | 1 - .../print_mail/mailing_list_jobs_params.py | 10 +- .../order_profiles/cheque_create_params.py | 6 +- .../order_profiles/cheque_retrieve_params.py | 5 +- .../order_profiles/cheque_update_params.py | 6 +- .../order_profiles/letter_create_params.py | 5 +- .../order_profiles/letter_retrieve_params.py | 5 +- .../order_profiles/letter_update_params.py | 5 +- .../order_profiles/postcard_create_params.py | 5 +- .../postcard_retrieve_params.py | 5 +- .../order_profiles/postcard_update_params.py | 5 +- .../self_mailer_create_params.py | 5 +- .../self_mailer_retrieve_params.py | 5 +- .../self_mailer_update_params.py | 5 +- .../postcard_retrieve_url_response.py | 1 - .../types/print_mail/report_sample_params.py | 4 +- .../reports/export_create_params.py | 6 +- .../reports/sample_create_params.py | 5 +- .../self_mailer_retrieve_url_response.py | 1 - .../print_mail/verification_status_count.py | 1 - .../print_mail/order_profiles/test_cheques.py | 92 +-- .../print_mail/order_profiles/test_letters.py | 92 +-- .../order_profiles/test_postcards.py | 92 +-- .../order_profiles/test_self_mailers.py | 92 +-- .../print_mail/reports/test_exports.py | 56 +- .../print_mail/reports/test_samples.py | 24 +- .../print_mail/test_bank_accounts.py | 100 +-- tests/api_resources/print_mail/test_boxes.py | 96 +-- .../print_mail/test_campaigns.py | 108 +-- .../api_resources/print_mail/test_cheques.py | 100 +-- .../api_resources/print_mail/test_contacts.py | 84 +-- .../api_resources/print_mail/test_letters.py | 112 ++-- .../print_mail/test_mailing_list_imports.py | 88 +-- .../print_mail/test_mailing_lists.py | 108 +-- .../print_mail/test_postcards.py | 128 ++-- .../api_resources/print_mail/test_reports.py | 104 +-- .../print_mail/test_self_mailers.py | 128 ++-- .../print_mail/test_sub_organizations.py | 72 +- .../print_mail/test_templates.py | 88 +-- .../test_address_verification.py | 36 +- .../test_intl_address_verification.py | 36 +- tests/conftest.py | 42 +- tests/test_client.py | 574 ++++++++-------- tests/test_models.py | 157 ++++- tests/test_transform.py | 44 +- tests/test_utils/test_datetime_parse.py | 110 ++++ tests/test_utils/test_proxy.py | 11 + tests/utils.py | 18 +- 109 files changed, 3881 insertions(+), 3050 deletions(-) create mode 100644 .vscode/settings.json delete mode 100644 mypy.ini create mode 100755 scripts/utils/upload-artifact.sh create mode 100644 src/postgrid/_utils/_compat.py create mode 100644 src/postgrid/_utils/_datetime_parse.py create mode 100644 src/postgrid/_utils/_resources_proxy.py create mode 100644 tests/test_utils/test_datetime_parse.py diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 55d2025..ff261ba 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -3,7 +3,7 @@ FROM mcr.microsoft.com/vscode/devcontainers/python:0-${VARIANT} USER vscode -RUN curl -sSf https://rye.astral.sh/get | RYE_VERSION="0.35.0" RYE_INSTALL_OPTION="--yes" bash +RUN curl -sSf https://rye.astral.sh/get | RYE_VERSION="0.44.0" RYE_INSTALL_OPTION="--yes" bash ENV PATH=/home/vscode/.rye/shims:$PATH RUN echo "[[ -d .venv ]] && source .venv/bin/activate || export PATH=\$PATH" >> /home/vscode/.bashrc diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c8a8a4f..77fd8e0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,18 +1,23 @@ name: CI on: push: - branches: - - main + branches-ignore: + - 'generated' + - 'codegen/**' + - 'integrated/**' + - 'stl-preview-head/**' + - 'stl-preview-base/**' pull_request: - branches: - - main - - next + branches-ignore: + - 'stl-preview-head/**' + - 'stl-preview-base/**' jobs: lint: + timeout-minutes: 10 name: lint - runs-on: ubuntu-latest - + runs-on: ${{ github.repository == 'stainless-sdks/postgrid-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} + if: github.event_name == 'push' || github.event.pull_request.head.repo.fork steps: - uses: actions/checkout@v4 @@ -21,7 +26,7 @@ jobs: curl -sSf https://rye.astral.sh/get | bash echo "$HOME/.rye/shims" >> $GITHUB_PATH env: - RYE_VERSION: '0.35.0' + RYE_VERSION: '0.44.0' RYE_INSTALL_OPTION: '--yes' - name: Install dependencies @@ -30,10 +35,51 @@ jobs: - name: Run lints run: ./scripts/lint + build: + if: github.event_name == 'push' || github.event.pull_request.head.repo.fork + timeout-minutes: 10 + name: build + permissions: + contents: read + id-token: write + runs-on: ${{ github.repository == 'stainless-sdks/postgrid-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} + steps: + - uses: actions/checkout@v4 + + - name: Install Rye + run: | + curl -sSf https://rye.astral.sh/get | bash + echo "$HOME/.rye/shims" >> $GITHUB_PATH + env: + RYE_VERSION: '0.44.0' + RYE_INSTALL_OPTION: '--yes' + + - name: Install dependencies + run: rye sync --all-features + + - name: Run build + run: rye build + + - name: Get GitHub OIDC Token + if: github.repository == 'stainless-sdks/postgrid-python' + id: github-oidc + uses: actions/github-script@v6 + with: + script: core.setOutput('github_token', await core.getIDToken()); + + - name: Upload tarball + if: github.repository == 'stainless-sdks/postgrid-python' + env: + URL: https://pkg.stainless.com/s + AUTH: ${{ steps.github-oidc.outputs.github_token }} + SHA: ${{ github.sha }} + run: ./scripts/utils/upload-artifact.sh + test: + timeout-minutes: 10 name: test - runs-on: ubuntu-latest - + runs-on: ${{ github.repository == 'stainless-sdks/postgrid-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} + if: github.event_name == 'push' || github.event.pull_request.head.repo.fork steps: - uses: actions/checkout@v4 @@ -42,7 +88,7 @@ jobs: curl -sSf https://rye.astral.sh/get | bash echo "$HOME/.rye/shims" >> $GITHUB_PATH env: - RYE_VERSION: '0.35.0' + RYE_VERSION: '0.44.0' RYE_INSTALL_OPTION: '--yes' - name: Bootstrap diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml index 346f030..dcaf2d4 100644 --- a/.github/workflows/publish-pypi.yml +++ b/.github/workflows/publish-pypi.yml @@ -21,7 +21,7 @@ jobs: curl -sSf https://rye.astral.sh/get | bash echo "$HOME/.rye/shims" >> $GITHUB_PATH env: - RYE_VERSION: '0.35.0' + RYE_VERSION: '0.44.0' RYE_INSTALL_OPTION: '--yes' - name: Publish to PyPI diff --git a/.gitignore b/.gitignore index 8779740..95ceb18 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ .prism.log -.vscode _dev __pycache__ diff --git a/.stats.yml b/.stats.yml index 044ee0a..6a4ca1c 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,2 +1,4 @@ configured_endpoints: 91 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/postgrid%2Fpostgrid-be6a47768faf3612d1dc9c8a108edb10a6c5a4e52b78cc7f4768e1d497e11e08.yml +openapi_spec_hash: a3ed2b74031c834a724b67db9ab6b23d +config_hash: 371afa1c13a4b49cacbe76aaa665a8aa diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..5b01030 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "python.analysis.importFormat": "relative", +} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c86a673..13075b3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -17,8 +17,7 @@ $ rye sync --all-features You can then run scripts using `rye run python script.py` or by activating the virtual environment: ```sh -$ rye shell -# or manually activate - https://docs.python.org/3/library/venv.html#how-venvs-work +# Activate the virtual environment - https://docs.python.org/3/library/venv.html#how-venvs-work $ source .venv/bin/activate # now you can omit the `rye run` prefix diff --git a/README.md b/README.md index 252d1cd..b514139 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # PostGrid Python API library -[![PyPI version](https://img.shields.io/pypi/v/postgrid-python.svg)](https://pypi.org/project/postgrid-python/) + +[![PyPI version](https://img.shields.io/pypi/v/postgrid-python.svg?label=pypi%20(stable))](https://pypi.org/project/postgrid-python/) The PostGrid Python library provides convenient access to the PostGrid REST API from any Python 3.8+ application. The library includes type definitions for all request params and response fields, @@ -26,9 +27,6 @@ import os from postgrid import PostGrid client = PostGrid( - address_verification_api_key=os.environ.get( - "POSTGRID_ADDRESS_VERIFICATION_API_KEY" - ), # This is the default and can be omitted print_mail_api_key=os.environ.get( "POSTGRID_PRINT_MAIL_API_KEY" ), # This is the default and can be omitted @@ -57,9 +55,6 @@ import asyncio from postgrid import AsyncPostGrid client = AsyncPostGrid( - address_verification_api_key=os.environ.get( - "POSTGRID_ADDRESS_VERIFICATION_API_KEY" - ), # This is the default and can be omitted print_mail_api_key=os.environ.get( "POSTGRID_PRINT_MAIL_API_KEY" ), # This is the default and can be omitted @@ -80,6 +75,41 @@ asyncio.run(main()) Functionality between the synchronous and asynchronous clients is otherwise identical. +### With aiohttp + +By default, the async client uses `httpx` for HTTP requests. However, for improved concurrency performance you may also use `aiohttp` as the HTTP backend. + +You can enable this by installing `aiohttp`: + +```sh +# install from PyPI +pip install --pre postgrid-python[aiohttp] +``` + +Then you can enable it by instantiating the client with `http_client=DefaultAioHttpClient()`: + +```python +import asyncio +from postgrid import DefaultAioHttpClient +from postgrid import AsyncPostGrid + + +async def main() -> None: + async with AsyncPostGrid( + print_mail_api_key="My Print Mail API Key", + http_client=DefaultAioHttpClient(), + ) as client: + contact = await client.print_mail.contacts.create( + address_line1="addressLine1", + country_code="countryCode", + first_name="firstName", + ) + print(contact.id) + + +asyncio.run(main()) +``` + ## Using types Nested request parameters are [TypedDicts](https://docs.python.org/3/library/typing.html#typing.TypedDict). Responses are [Pydantic models](https://docs.pydantic.dev) which also provide helper methods for things like: @@ -158,6 +188,27 @@ for letter in first_page.data: # Remove `await` for non-async usage. ``` +## Nested params + +Nested parameters are dictionaries, typed using `TypedDict`, for example: + +```python +from postgrid import PostGrid + +client = PostGrid() + +response = client.address_verification.verify( + address={ + "city": "city", + "country": "ca", + "line1": "line1", + "postal_or_zip": "postalOrZip", + "province_or_state": "provinceOrState", + }, +) +print(response.address) +``` + ## Handling errors When the library is unable to connect to the API (for example, due to network connection problems or a timeout), a subclass of `postgrid.APIConnectionError` is raised. @@ -227,7 +278,7 @@ client.with_options(max_retries=5).address_verification.verify( ### Timeouts By default requests time out after 1 minute. You can configure this with a `timeout` option, -which accepts a float or an [`httpx.Timeout`](https://www.python-httpx.org/advanced/#fine-tuning-the-configuration) object: +which accepts a float or an [`httpx.Timeout`](https://www.python-httpx.org/advanced/timeouts/#fine-tuning-the-configuration) object: ```python from postgrid import PostGrid diff --git a/SECURITY.md b/SECURITY.md index ae45ad6..c86a5d3 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,9 +2,9 @@ ## Reporting Security Issues -This SDK is generated by [Stainless Software Inc](http://stainlessapi.com). Stainless takes security seriously, and encourages you to report any security vulnerability promptly so that appropriate action can be taken. +This SDK is generated by [Stainless Software Inc](http://stainless.com). Stainless takes security seriously, and encourages you to report any security vulnerability promptly so that appropriate action can be taken. -To report a security issue, please contact the Stainless team at security@stainlessapi.com. +To report a security issue, please contact the Stainless team at security@stainless.com. ## Responsible Disclosure @@ -16,11 +16,11 @@ before making any information public. ## Reporting Non-SDK Related Security Issues If you encounter security issues that are not directly related to SDKs but pertain to the services -or products provided by PostGrid please follow the respective company's security reporting guidelines. +or products provided by PostGrid, please follow the respective company's security reporting guidelines. ### PostGrid Terms and Policies -Please contact support@postgrid.com for any questions or concerns regarding security of our services. +Please contact support@postgrid.com for any questions or concerns regarding the security of our services. --- diff --git a/bin/check-release-environment b/bin/check-release-environment index 5d500ca..b845b0f 100644 --- a/bin/check-release-environment +++ b/bin/check-release-environment @@ -3,7 +3,7 @@ errors=() if [ -z "${PYPI_TOKEN}" ]; then - errors+=("The POSTGRID_PYPI_TOKEN secret has not been set. Please set it in either this repository's secrets or your organization secrets.") + errors+=("The PYPI_TOKEN secret has not been set. Please set it in either this repository's secrets or your organization secrets.") fi lenErrors=${#errors[@]} diff --git a/bin/publish-pypi b/bin/publish-pypi index 05bfccb..826054e 100644 --- a/bin/publish-pypi +++ b/bin/publish-pypi @@ -3,7 +3,4 @@ set -eux mkdir -p dist rye build --clean -# Patching importlib-metadata version until upstream library version is updated -# https://github.com/pypa/twine/issues/977#issuecomment-2189800841 -"$HOME/.rye/self/bin/python3" -m pip install 'importlib-metadata==7.2.1' rye publish --yes --token=$PYPI_TOKEN diff --git a/mypy.ini b/mypy.ini deleted file mode 100644 index 70761d3..0000000 --- a/mypy.ini +++ /dev/null @@ -1,50 +0,0 @@ -[mypy] -pretty = True -show_error_codes = True - -# Exclude _files.py because mypy isn't smart enough to apply -# the correct type narrowing and as this is an internal module -# it's fine to just use Pyright. -# -# We also exclude our `tests` as mypy doesn't always infer -# types correctly and Pyright will still catch any type errors. -exclude = ^(src/postgrid/_files\.py|_dev/.*\.py|tests/.*)$ - -strict_equality = True -implicit_reexport = True -check_untyped_defs = True -no_implicit_optional = True - -warn_return_any = True -warn_unreachable = True -warn_unused_configs = True - -# Turn these options off as it could cause conflicts -# with the Pyright options. -warn_unused_ignores = False -warn_redundant_casts = False - -disallow_any_generics = True -disallow_untyped_defs = True -disallow_untyped_calls = True -disallow_subclassing_any = True -disallow_incomplete_defs = True -disallow_untyped_decorators = True -cache_fine_grained = True - -# By default, mypy reports an error if you assign a value to the result -# of a function call that doesn't return anything. We do this in our test -# cases: -# ``` -# result = ... -# assert result is None -# ``` -# Changing this codegen to make mypy happy would increase complexity -# and would not be worth it. -disable_error_code = func-returns-value,overload-cannot-match - -# https://github.com/python/mypy/issues/12162 -[mypy.overrides] -module = "black.files.*" -ignore_errors = true -ignore_missing_imports = true diff --git a/pyproject.toml b/pyproject.toml index 679d690..a82d942 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,6 +24,7 @@ classifiers = [ "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", "Operating System :: OS Independent", "Operating System :: POSIX", "Operating System :: MacOS", @@ -37,13 +38,14 @@ classifiers = [ Homepage = "https://github.com/postgrid/postgrid-python" Repository = "https://github.com/postgrid/postgrid-python" - +[project.optional-dependencies] +aiohttp = ["aiohttp", "httpx_aiohttp>=0.1.9"] [tool.rye] managed = true # version pins are in requirements-dev.lock dev-dependencies = [ - "pyright>=1.1.359", + "pyright==1.1.399", "mypy", "respx", "pytest", @@ -54,7 +56,7 @@ dev-dependencies = [ "dirty-equals>=0.6.0", "importlib-metadata>=6.7.0", "rich>=13.7.1", - "nest_asyncio==1.6.0", + "pytest-xdist>=3.6.1", ] [tool.rye.scripts] @@ -87,7 +89,7 @@ typecheck = { chain = [ "typecheck:mypy" = "mypy ." [build-system] -requires = ["hatchling", "hatch-fancy-pypi-readme"] +requires = ["hatchling==1.26.3", "hatch-fancy-pypi-readme"] build-backend = "hatchling.build" [tool.hatch.build] @@ -126,7 +128,7 @@ replacement = '[\1](https://github.com/postgrid/postgrid-python/tree/main/\g<2>) [tool.pytest.ini_options] testpaths = ["tests"] -addopts = "--tb=short" +addopts = "--tb=short -n auto" xfail_strict = true asyncio_mode = "auto" asyncio_default_fixture_loop_scope = "session" @@ -145,18 +147,71 @@ exclude = [ "_dev", ".venv", ".nox", + ".git", ] reportImplicitOverride = true +reportOverlappingOverload = false reportImportCycles = false reportPrivateUsage = false +[tool.mypy] +pretty = true +show_error_codes = true + +# Exclude _files.py because mypy isn't smart enough to apply +# the correct type narrowing and as this is an internal module +# it's fine to just use Pyright. +# +# We also exclude our `tests` as mypy doesn't always infer +# types correctly and Pyright will still catch any type errors. +exclude = ['src/postgrid/_files.py', '_dev/.*.py', 'tests/.*'] + +strict_equality = true +implicit_reexport = true +check_untyped_defs = true +no_implicit_optional = true + +warn_return_any = true +warn_unreachable = true +warn_unused_configs = true + +# Turn these options off as it could cause conflicts +# with the Pyright options. +warn_unused_ignores = false +warn_redundant_casts = false + +disallow_any_generics = true +disallow_untyped_defs = true +disallow_untyped_calls = true +disallow_subclassing_any = true +disallow_incomplete_defs = true +disallow_untyped_decorators = true +cache_fine_grained = true + +# By default, mypy reports an error if you assign a value to the result +# of a function call that doesn't return anything. We do this in our test +# cases: +# ``` +# result = ... +# assert result is None +# ``` +# Changing this codegen to make mypy happy would increase complexity +# and would not be worth it. +disable_error_code = "func-returns-value,overload-cannot-match" + +# https://github.com/python/mypy/issues/12162 +[[tool.mypy.overrides]] +module = "black.files.*" +ignore_errors = true +ignore_missing_imports = true + [tool.ruff] line-length = 120 output-format = "grouped" -target-version = "py37" +target-version = "py38" [tool.ruff.format] docstring-code-format = true @@ -169,6 +224,8 @@ select = [ "B", # remove unused imports "F401", + # check for missing future annotations + "FA102", # bare except statements "E722", # unused arguments @@ -191,6 +248,8 @@ unfixable = [ "T203", ] +extend-safe-fixes = ["FA102"] + [tool.ruff.lint.flake8-tidy-imports.banned-api] "functools.lru_cache".msg = "This function does not retain type information for the wrapped function's arguments; The `lru_cache` function from `_utils` should be used instead" diff --git a/requirements-dev.lock b/requirements-dev.lock index b54f530..c88a01e 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -10,6 +10,13 @@ # universal: false -e file:. +aiohappyeyeballs==2.6.1 + # via aiohttp +aiohttp==3.12.8 + # via httpx-aiohttp + # via postgrid-python +aiosignal==1.3.2 + # via aiohttp annotated-types==0.6.0 # via pydantic anyio==4.4.0 @@ -17,6 +24,10 @@ anyio==4.4.0 # via postgrid-python argcomplete==3.1.2 # via nox +async-timeout==5.0.1 + # via aiohttp +attrs==25.3.0 + # via aiohttp certifi==2023.7.22 # via httpcore # via httpx @@ -30,18 +41,27 @@ distro==1.8.0 exceptiongroup==1.2.2 # via anyio # via pytest +execnet==2.1.1 + # via pytest-xdist filelock==3.12.4 # via virtualenv -h11==0.14.0 +frozenlist==1.6.2 + # via aiohttp + # via aiosignal +h11==0.16.0 # via httpcore -httpcore==1.0.2 +httpcore==1.0.9 # via httpx httpx==0.28.1 + # via httpx-aiohttp # via postgrid-python # via respx +httpx-aiohttp==0.1.9 + # via postgrid-python idna==3.4 # via anyio # via httpx + # via yarl importlib-metadata==7.0.0 iniconfig==2.0.0 # via pytest @@ -49,10 +69,12 @@ markdown-it-py==3.0.0 # via rich mdurl==0.1.2 # via markdown-it-py +multidict==6.4.4 + # via aiohttp + # via yarl mypy==1.14.1 mypy-extensions==1.0.0 # via mypy -nest-asyncio==1.6.0 nodeenv==1.8.0 # via pyright nox==2023.4.22 @@ -63,16 +85,21 @@ platformdirs==3.11.0 # via virtualenv pluggy==1.5.0 # via pytest -pydantic==2.10.3 +propcache==0.3.1 + # via aiohttp + # via yarl +pydantic==2.11.9 # via postgrid-python -pydantic-core==2.27.1 +pydantic-core==2.33.2 # via pydantic pygments==2.18.0 # via rich -pyright==1.1.392.post0 +pyright==1.1.399 pytest==8.3.3 # via pytest-asyncio + # via pytest-xdist pytest-asyncio==0.24.0 +pytest-xdist==3.7.0 python-dateutil==2.8.2 # via time-machine pytz==2023.3.post1 @@ -93,12 +120,18 @@ tomli==2.0.2 # via pytest typing-extensions==4.12.2 # via anyio + # via multidict # via mypy # via postgrid-python # via pydantic # via pydantic-core # via pyright + # via typing-inspection +typing-inspection==0.4.1 + # via pydantic virtualenv==20.24.5 # via nox +yarl==1.20.0 + # via aiohttp zipp==3.17.0 # via importlib-metadata diff --git a/requirements.lock b/requirements.lock index 73f36ce..1187005 100644 --- a/requirements.lock +++ b/requirements.lock @@ -10,11 +10,22 @@ # universal: false -e file:. +aiohappyeyeballs==2.6.1 + # via aiohttp +aiohttp==3.12.8 + # via httpx-aiohttp + # via postgrid-python +aiosignal==1.3.2 + # via aiohttp annotated-types==0.6.0 # via pydantic anyio==4.4.0 # via httpx # via postgrid-python +async-timeout==5.0.1 + # via aiohttp +attrs==25.3.0 + # via aiohttp certifi==2023.7.22 # via httpcore # via httpx @@ -22,24 +33,43 @@ distro==1.8.0 # via postgrid-python exceptiongroup==1.2.2 # via anyio -h11==0.14.0 +frozenlist==1.6.2 + # via aiohttp + # via aiosignal +h11==0.16.0 # via httpcore -httpcore==1.0.2 +httpcore==1.0.9 # via httpx httpx==0.28.1 + # via httpx-aiohttp + # via postgrid-python +httpx-aiohttp==0.1.9 # via postgrid-python idna==3.4 # via anyio # via httpx -pydantic==2.10.3 + # via yarl +multidict==6.4.4 + # via aiohttp + # via yarl +propcache==0.3.1 + # via aiohttp + # via yarl +pydantic==2.11.9 # via postgrid-python -pydantic-core==2.27.1 +pydantic-core==2.33.2 # via pydantic sniffio==1.3.0 # via anyio # via postgrid-python typing-extensions==4.12.2 # via anyio + # via multidict # via postgrid-python # via pydantic # via pydantic-core + # via typing-inspection +typing-inspection==0.4.1 + # via pydantic +yarl==1.20.0 + # via aiohttp diff --git a/scripts/bootstrap b/scripts/bootstrap index e84fe62..b430fee 100755 --- a/scripts/bootstrap +++ b/scripts/bootstrap @@ -4,10 +4,18 @@ set -e cd "$(dirname "$0")/.." -if ! command -v rye >/dev/null 2>&1 && [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ]; then +if [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ] && [ "$SKIP_BREW" != "1" ] && [ -t 0 ]; then brew bundle check >/dev/null 2>&1 || { - echo "==> Installing Homebrew dependencies…" - brew bundle + echo -n "==> Install Homebrew dependencies? (y/N): " + read -r response + case "$response" in + [yY][eE][sS]|[yY]) + brew bundle + ;; + *) + ;; + esac + echo } fi diff --git a/scripts/mock b/scripts/mock index d2814ae..0b28f6e 100755 --- a/scripts/mock +++ b/scripts/mock @@ -21,7 +21,7 @@ echo "==> Starting mock server with URL ${URL}" # Run prism mock on the given spec if [ "$1" == "--daemon" ]; then - npm exec --package=@stainless-api/prism-cli@5.8.5 -- prism mock "$URL" &> .prism.log & + npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock "$URL" &> .prism.log & # Wait for server to come online echo -n "Waiting for server" @@ -37,5 +37,5 @@ if [ "$1" == "--daemon" ]; then echo else - npm exec --package=@stainless-api/prism-cli@5.8.5 -- prism mock "$URL" + npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock "$URL" fi diff --git a/scripts/test b/scripts/test index 4fa5698..dbeda2d 100755 --- a/scripts/test +++ b/scripts/test @@ -43,7 +43,7 @@ elif ! prism_is_running ; then echo -e "To run the server, pass in the path or url of your OpenAPI" echo -e "spec to the prism command:" echo - echo -e " \$ ${YELLOW}npm exec --package=@stoplight/prism-cli@~5.3.2 -- prism mock path/to/your.openapi.yml${NC}" + echo -e " \$ ${YELLOW}npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock path/to/your.openapi.yml${NC}" echo exit 1 @@ -52,6 +52,8 @@ else echo fi +export DEFER_PYDANTIC_BUILD=false + echo "==> Running tests" rye run pytest "$@" diff --git a/scripts/utils/upload-artifact.sh b/scripts/utils/upload-artifact.sh new file mode 100755 index 0000000..18f498b --- /dev/null +++ b/scripts/utils/upload-artifact.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +set -exuo pipefail + +FILENAME=$(basename dist/*.whl) + +RESPONSE=$(curl -X POST "$URL?filename=$FILENAME" \ + -H "Authorization: Bearer $AUTH" \ + -H "Content-Type: application/json") + +SIGNED_URL=$(echo "$RESPONSE" | jq -r '.url') + +if [[ "$SIGNED_URL" == "null" ]]; then + echo -e "\033[31mFailed to get signed URL.\033[0m" + exit 1 +fi + +UPLOAD_RESPONSE=$(curl -v -X PUT \ + -H "Content-Type: binary/octet-stream" \ + --data-binary "@dist/$FILENAME" "$SIGNED_URL" 2>&1) + +if echo "$UPLOAD_RESPONSE" | grep -q "HTTP/[0-9.]* 200"; then + echo -e "\033[32mUploaded build to Stainless storage.\033[0m" + echo -e "\033[32mInstallation: pip install 'https://pkg.stainless.com/s/postgrid-python/$SHA/$FILENAME'\033[0m" +else + echo -e "\033[31mFailed to upload artifact.\033[0m" + exit 1 +fi diff --git a/src/postgrid/__init__.py b/src/postgrid/__init__.py index 57e6a40..c8b5ad3 100644 --- a/src/postgrid/__init__.py +++ b/src/postgrid/__init__.py @@ -1,7 +1,9 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +import typing as _t + from . import types -from ._types import NOT_GIVEN, Omit, NoneType, NotGiven, Transport, ProxiesTypes +from ._types import NOT_GIVEN, Omit, NoneType, NotGiven, Transport, ProxiesTypes, omit, not_given from ._utils import file_from_path from ._client import ( Client, @@ -34,7 +36,7 @@ UnprocessableEntityError, APIResponseValidationError, ) -from ._base_client import DefaultHttpxClient, DefaultAsyncHttpxClient +from ._base_client import DefaultHttpxClient, DefaultAioHttpClient, DefaultAsyncHttpxClient from ._utils._logs import setup_logging as _setup_logging __all__ = [ @@ -46,7 +48,9 @@ "ProxiesTypes", "NotGiven", "NOT_GIVEN", + "not_given", "Omit", + "omit", "PostGridError", "APIError", "APIStatusError", @@ -76,8 +80,12 @@ "DEFAULT_CONNECTION_LIMITS", "DefaultHttpxClient", "DefaultAsyncHttpxClient", + "DefaultAioHttpClient", ] +if not _t.TYPE_CHECKING: + from ._utils._resources_proxy import resources as resources + _setup_logging() # Update the __module__ attribute for exported symbols so that diff --git a/src/postgrid/_base_client.py b/src/postgrid/_base_client.py index 64fa1d5..614d3cf 100644 --- a/src/postgrid/_base_client.py +++ b/src/postgrid/_base_client.py @@ -9,7 +9,6 @@ import inspect import logging import platform -import warnings import email.utils from types import TracebackType from random import random @@ -36,14 +35,13 @@ import httpx import distro import pydantic -from httpx import URL, Limits +from httpx import URL from pydantic import PrivateAttr from . import _exceptions from ._qs import Querystring from ._files import to_httpx_files, async_to_httpx_files from ._types import ( - NOT_GIVEN, Body, Omit, Query, @@ -51,19 +49,17 @@ Timeout, NotGiven, ResponseT, - Transport, AnyMapping, PostParser, - ProxiesTypes, RequestFiles, HttpxSendArgs, - AsyncTransport, RequestOptions, HttpxRequestFiles, ModelBuilderProtocol, + not_given, ) from ._utils import is_dict, is_list, asyncify, is_given, lru_cache, is_mapping -from ._compat import model_copy, model_dump +from ._compat import PYDANTIC_V1, model_copy, model_dump from ._models import GenericModel, FinalRequestOptions, validate_type, construct_type from ._response import ( APIResponse, @@ -102,7 +98,11 @@ _AsyncStreamT = TypeVar("_AsyncStreamT", bound=AsyncStream[Any]) if TYPE_CHECKING: - from httpx._config import DEFAULT_TIMEOUT_CONFIG as HTTPX_DEFAULT_TIMEOUT + from httpx._config import ( + DEFAULT_TIMEOUT_CONFIG, # pyright: ignore[reportPrivateImportUsage] + ) + + HTTPX_DEFAULT_TIMEOUT = DEFAULT_TIMEOUT_CONFIG else: try: from httpx._config import DEFAULT_TIMEOUT_CONFIG as HTTPX_DEFAULT_TIMEOUT @@ -119,6 +119,7 @@ class PageInfo: url: URL | NotGiven params: Query | NotGiven + json: Body | NotGiven @overload def __init__( @@ -134,19 +135,30 @@ def __init__( params: Query, ) -> None: ... + @overload def __init__( self, *, - url: URL | NotGiven = NOT_GIVEN, - params: Query | NotGiven = NOT_GIVEN, + json: Body, + ) -> None: ... + + def __init__( + self, + *, + url: URL | NotGiven = not_given, + json: Body | NotGiven = not_given, + params: Query | NotGiven = not_given, ) -> None: self.url = url + self.json = json self.params = params @override def __repr__(self) -> str: if self.url: return f"{self.__class__.__name__}(url={self.url})" + if self.json: + return f"{self.__class__.__name__}(json={self.json})" return f"{self.__class__.__name__}(params={self.params})" @@ -195,6 +207,19 @@ def _info_to_options(self, info: PageInfo) -> FinalRequestOptions: options.url = str(url) return options + if not isinstance(info.json, NotGiven): + if not is_mapping(info.json): + raise TypeError("Pagination is only supported with mappings") + + if not options.json_data: + options.json_data = {**info.json} + else: + if not is_mapping(options.json_data): + raise TypeError("Pagination is only supported with mappings") + + options.json_data = {**options.json_data, **info.json} + return options + raise ValueError("Unexpected PageInfo state") @@ -207,6 +232,9 @@ def _set_private_attributes( model: Type[_T], options: FinalRequestOptions, ) -> None: + if (not PYDANTIC_V1) and getattr(self, "__pydantic_private__", None) is None: + self.__pydantic_private__ = {} + self._model = model self._client = client self._options = options @@ -292,6 +320,9 @@ def _set_private_attributes( client: AsyncAPIClient, options: FinalRequestOptions, ) -> None: + if (not PYDANTIC_V1) and getattr(self, "__pydantic_private__", None) is None: + self.__pydantic_private__ = {} + self._model = model self._client = client self._options = options @@ -331,9 +362,6 @@ class BaseClient(Generic[_HttpxClientT, _DefaultStreamT]): _base_url: URL max_retries: int timeout: Union[float, Timeout, None] - _limits: httpx.Limits - _proxies: ProxiesTypes | None - _transport: Transport | AsyncTransport | None _strict_response_validation: bool _idempotency_header: str | None _default_stream_cls: type[_DefaultStreamT] | None = None @@ -346,9 +374,6 @@ def __init__( _strict_response_validation: bool, max_retries: int = DEFAULT_MAX_RETRIES, timeout: float | Timeout | None = DEFAULT_TIMEOUT, - limits: httpx.Limits, - transport: Transport | AsyncTransport | None, - proxies: ProxiesTypes | None, custom_headers: Mapping[str, str] | None = None, custom_query: Mapping[str, object] | None = None, ) -> None: @@ -356,9 +381,6 @@ def __init__( self._base_url = self._enforce_trailing_slash(URL(base_url)) self.max_retries = max_retries self.timeout = timeout - self._limits = limits - self._proxies = proxies - self._transport = transport self._custom_headers = custom_headers or {} self._custom_query = custom_query or {} self._strict_response_validation = _strict_response_validation @@ -415,8 +437,8 @@ def _build_headers(self, options: FinalRequestOptions, *, retries_taken: int = 0 headers = httpx.Headers(headers_dict) idempotency_header = self._idempotency_header - if idempotency_header and options.method.lower() != "get" and idempotency_header not in headers: - headers[idempotency_header] = options.idempotency_key or self._idempotency_key() + if idempotency_header and options.idempotency_key and idempotency_header not in headers: + headers[idempotency_header] = options.idempotency_key # Don't set these headers if they were already set or removed by the caller. We check # `custom_headers`, which can contain `Omit()`, instead of `headers` to account for the removal case. @@ -507,6 +529,18 @@ def _build_request( # work around https://github.com/encode/httpx/discussions/2880 kwargs["extensions"] = {"sni_hostname": prepared_url.host.replace("_", "-")} + is_body_allowed = options.method.lower() != "get" + + if is_body_allowed: + if isinstance(json_data, bytes): + kwargs["content"] = json_data + else: + kwargs["json"] = json_data if is_given(json_data) else None + kwargs["files"] = files + else: + headers.pop("Content-Type", None) + kwargs.pop("data", None) + # TODO: report this error to httpx return self._client.build_request( # pyright: ignore[reportUnknownMemberType] headers=headers, @@ -518,8 +552,6 @@ def _build_request( # so that passing a `TypedDict` doesn't cause an error. # https://github.com/microsoft/pyright/issues/3526#event-6715453066 params=self.qs.stringify(cast(Mapping[str, Any], params)) if params else None, - json=json_data if is_given(json_data) else None, - files=files, **kwargs, ) @@ -563,7 +595,7 @@ def _maybe_override_cast_to(self, cast_to: type[ResponseT], options: FinalReques # we internally support defining a temporary header to override the # default `cast_to` type for use with `.with_raw_response` and `.with_streaming_response` # see _response.py for implementation details - override_cast_to = headers.pop(OVERRIDE_CAST_TO_HEADER, NOT_GIVEN) + override_cast_to = headers.pop(OVERRIDE_CAST_TO_HEADER, not_given) if is_given(override_cast_to): options.headers = headers return cast(Type[ResponseT], override_cast_to) @@ -793,47 +825,12 @@ def __init__( version: str, base_url: str | URL, max_retries: int = DEFAULT_MAX_RETRIES, - timeout: float | Timeout | None | NotGiven = NOT_GIVEN, - transport: Transport | None = None, - proxies: ProxiesTypes | None = None, - limits: Limits | None = None, + timeout: float | Timeout | None | NotGiven = not_given, http_client: httpx.Client | None = None, custom_headers: Mapping[str, str] | None = None, custom_query: Mapping[str, object] | None = None, _strict_response_validation: bool, ) -> None: - kwargs: dict[str, Any] = {} - if limits is not None: - warnings.warn( - "The `connection_pool_limits` argument is deprecated. The `http_client` argument should be passed instead", - category=DeprecationWarning, - stacklevel=3, - ) - if http_client is not None: - raise ValueError("The `http_client` argument is mutually exclusive with `connection_pool_limits`") - else: - limits = DEFAULT_CONNECTION_LIMITS - - if transport is not None: - kwargs["transport"] = transport - warnings.warn( - "The `transport` argument is deprecated. The `http_client` argument should be passed instead", - category=DeprecationWarning, - stacklevel=3, - ) - if http_client is not None: - raise ValueError("The `http_client` argument is mutually exclusive with `transport`") - - if proxies is not None: - kwargs["proxies"] = proxies - warnings.warn( - "The `proxies` argument is deprecated. The `http_client` argument should be passed instead", - category=DeprecationWarning, - stacklevel=3, - ) - if http_client is not None: - raise ValueError("The `http_client` argument is mutually exclusive with `proxies`") - if not is_given(timeout): # if the user passed in a custom http client with a non-default # timeout set then we use that timeout. @@ -854,12 +851,9 @@ def __init__( super().__init__( version=version, - limits=limits, # cast to a valid type because mypy doesn't understand our type narrowing timeout=cast(Timeout, timeout), - proxies=proxies, base_url=base_url, - transport=transport, max_retries=max_retries, custom_query=custom_query, custom_headers=custom_headers, @@ -869,9 +863,6 @@ def __init__( base_url=base_url, # cast to a valid type because mypy doesn't understand our type narrowing timeout=cast(Timeout, timeout), - limits=limits, - follow_redirects=True, - **kwargs, # type: ignore ) def is_closed(self) -> bool: @@ -921,7 +912,6 @@ def request( self, cast_to: Type[ResponseT], options: FinalRequestOptions, - remaining_retries: Optional[int] = None, *, stream: Literal[True], stream_cls: Type[_StreamT], @@ -932,7 +922,6 @@ def request( self, cast_to: Type[ResponseT], options: FinalRequestOptions, - remaining_retries: Optional[int] = None, *, stream: Literal[False] = False, ) -> ResponseT: ... @@ -942,7 +931,6 @@ def request( self, cast_to: Type[ResponseT], options: FinalRequestOptions, - remaining_retries: Optional[int] = None, *, stream: bool = False, stream_cls: Type[_StreamT] | None = None, @@ -952,121 +940,112 @@ def request( self, cast_to: Type[ResponseT], options: FinalRequestOptions, - remaining_retries: Optional[int] = None, *, stream: bool = False, stream_cls: type[_StreamT] | None = None, ) -> ResponseT | _StreamT: - if remaining_retries is not None: - retries_taken = options.get_max_retries(self.max_retries) - remaining_retries - else: - retries_taken = 0 - - return self._request( - cast_to=cast_to, - options=options, - stream=stream, - stream_cls=stream_cls, - retries_taken=retries_taken, - ) + cast_to = self._maybe_override_cast_to(cast_to, options) - def _request( - self, - *, - cast_to: Type[ResponseT], - options: FinalRequestOptions, - retries_taken: int, - stream: bool, - stream_cls: type[_StreamT] | None, - ) -> ResponseT | _StreamT: # create a copy of the options we were given so that if the # options are mutated later & we then retry, the retries are # given the original options input_options = model_copy(options) + if input_options.idempotency_key is None and input_options.method.lower() != "get": + # ensure the idempotency key is reused between requests + input_options.idempotency_key = self._idempotency_key() - cast_to = self._maybe_override_cast_to(cast_to, options) - options = self._prepare_options(options) + response: httpx.Response | None = None + max_retries = input_options.get_max_retries(self.max_retries) - remaining_retries = options.get_max_retries(self.max_retries) - retries_taken - request = self._build_request(options, retries_taken=retries_taken) - self._prepare_request(request) + retries_taken = 0 + for retries_taken in range(max_retries + 1): + options = model_copy(input_options) + options = self._prepare_options(options) - kwargs: HttpxSendArgs = {} - if self.custom_auth is not None: - kwargs["auth"] = self.custom_auth + remaining_retries = max_retries - retries_taken + request = self._build_request(options, retries_taken=retries_taken) + self._prepare_request(request) - log.debug("Sending HTTP Request: %s %s", request.method, request.url) + kwargs: HttpxSendArgs = {} + if self.custom_auth is not None: + kwargs["auth"] = self.custom_auth - try: - response = self._client.send( - request, - stream=stream or self._should_stream_response_body(request=request), - **kwargs, - ) - except httpx.TimeoutException as err: - log.debug("Encountered httpx.TimeoutException", exc_info=True) - - if remaining_retries > 0: - return self._retry_request( - input_options, - cast_to, - retries_taken=retries_taken, - stream=stream, - stream_cls=stream_cls, - response_headers=None, - ) + if options.follow_redirects is not None: + kwargs["follow_redirects"] = options.follow_redirects - log.debug("Raising timeout error") - raise APITimeoutError(request=request) from err - except Exception as err: - log.debug("Encountered Exception", exc_info=True) + log.debug("Sending HTTP Request: %s %s", request.method, request.url) - if remaining_retries > 0: - return self._retry_request( - input_options, - cast_to, - retries_taken=retries_taken, - stream=stream, - stream_cls=stream_cls, - response_headers=None, + response = None + try: + response = self._client.send( + request, + stream=stream or self._should_stream_response_body(request=request), + **kwargs, ) + except httpx.TimeoutException as err: + log.debug("Encountered httpx.TimeoutException", exc_info=True) + + if remaining_retries > 0: + self._sleep_for_retry( + retries_taken=retries_taken, + max_retries=max_retries, + options=input_options, + response=None, + ) + continue + + log.debug("Raising timeout error") + raise APITimeoutError(request=request) from err + except Exception as err: + log.debug("Encountered Exception", exc_info=True) + + if remaining_retries > 0: + self._sleep_for_retry( + retries_taken=retries_taken, + max_retries=max_retries, + options=input_options, + response=None, + ) + continue + + log.debug("Raising connection error") + raise APIConnectionError(request=request) from err + + log.debug( + 'HTTP Response: %s %s "%i %s" %s', + request.method, + request.url, + response.status_code, + response.reason_phrase, + response.headers, + ) - log.debug("Raising connection error") - raise APIConnectionError(request=request) from err - - log.debug( - 'HTTP Response: %s %s "%i %s" %s', - request.method, - request.url, - response.status_code, - response.reason_phrase, - response.headers, - ) + try: + response.raise_for_status() + except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code + log.debug("Encountered httpx.HTTPStatusError", exc_info=True) + + if remaining_retries > 0 and self._should_retry(err.response): + err.response.close() + self._sleep_for_retry( + retries_taken=retries_taken, + max_retries=max_retries, + options=input_options, + response=response, + ) + continue - try: - response.raise_for_status() - except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code - log.debug("Encountered httpx.HTTPStatusError", exc_info=True) - - if remaining_retries > 0 and self._should_retry(err.response): - err.response.close() - return self._retry_request( - input_options, - cast_to, - retries_taken=retries_taken, - response_headers=err.response.headers, - stream=stream, - stream_cls=stream_cls, - ) + # If the response is streamed then we need to explicitly read the response + # to completion before attempting to access the response text. + if not err.response.is_closed: + err.response.read() - # If the response is streamed then we need to explicitly read the response - # to completion before attempting to access the response text. - if not err.response.is_closed: - err.response.read() + log.debug("Re-raising status error") + raise self._make_status_error_from_response(err.response) from None - log.debug("Re-raising status error") - raise self._make_status_error_from_response(err.response) from None + break + assert response is not None, "could not resolve response (should never happen)" return self._process_response( cast_to=cast_to, options=options, @@ -1076,37 +1055,20 @@ def _request( retries_taken=retries_taken, ) - def _retry_request( - self, - options: FinalRequestOptions, - cast_to: Type[ResponseT], - *, - retries_taken: int, - response_headers: httpx.Headers | None, - stream: bool, - stream_cls: type[_StreamT] | None, - ) -> ResponseT | _StreamT: - remaining_retries = options.get_max_retries(self.max_retries) - retries_taken + def _sleep_for_retry( + self, *, retries_taken: int, max_retries: int, options: FinalRequestOptions, response: httpx.Response | None + ) -> None: + remaining_retries = max_retries - retries_taken if remaining_retries == 1: log.debug("1 retry left") else: log.debug("%i retries left", remaining_retries) - timeout = self._calculate_retry_timeout(remaining_retries, options, response_headers) + timeout = self._calculate_retry_timeout(remaining_retries, options, response.headers if response else None) log.info("Retrying request to %s in %f seconds", options.url, timeout) - # In a synchronous context we are blocking the entire thread. Up to the library user to run the client in a - # different thread if necessary. time.sleep(timeout) - return self._request( - options=options, - cast_to=cast_to, - retries_taken=retries_taken + 1, - stream=stream, - stream_cls=stream_cls, - ) - def _process_response( self, *, @@ -1119,7 +1081,14 @@ def _process_response( ) -> ResponseT: origin = get_origin(cast_to) or cast_to - if inspect.isclass(origin) and issubclass(origin, BaseAPIResponse): + if ( + inspect.isclass(origin) + and issubclass(origin, BaseAPIResponse) + # we only want to actually return the custom BaseAPIResponse class if we're + # returning the raw response, or if we're not streaming SSE, as if we're streaming + # SSE then `cast_to` doesn't actively reflect the type we need to parse into + and (not stream or bool(response.request.headers.get(RAW_RESPONSE_HEADER))) + ): if not issubclass(origin, APIResponse): raise TypeError(f"API Response types must subclass {APIResponse}; Received {origin}") @@ -1330,6 +1299,24 @@ def __init__(self, **kwargs: Any) -> None: super().__init__(**kwargs) +try: + import httpx_aiohttp +except ImportError: + + class _DefaultAioHttpClient(httpx.AsyncClient): + def __init__(self, **_kwargs: Any) -> None: + raise RuntimeError("To use the aiohttp client you must have installed the package with the `aiohttp` extra") +else: + + class _DefaultAioHttpClient(httpx_aiohttp.HttpxAiohttpClient): # type: ignore + def __init__(self, **kwargs: Any) -> None: + kwargs.setdefault("timeout", DEFAULT_TIMEOUT) + kwargs.setdefault("limits", DEFAULT_CONNECTION_LIMITS) + kwargs.setdefault("follow_redirects", True) + + super().__init__(**kwargs) + + if TYPE_CHECKING: DefaultAsyncHttpxClient = httpx.AsyncClient """An alias to `httpx.AsyncClient` that provides the same defaults that this SDK @@ -1338,8 +1325,12 @@ def __init__(self, **kwargs: Any) -> None: This is useful because overriding the `http_client` with your own instance of `httpx.AsyncClient` will result in httpx's defaults being used, not ours. """ + + DefaultAioHttpClient = httpx.AsyncClient + """An alias to `httpx.AsyncClient` that changes the default HTTP transport to `aiohttp`.""" else: DefaultAsyncHttpxClient = _DefaultAsyncHttpxClient + DefaultAioHttpClient = _DefaultAioHttpClient class AsyncHttpxClientWrapper(DefaultAsyncHttpxClient): @@ -1365,46 +1356,11 @@ def __init__( base_url: str | URL, _strict_response_validation: bool, max_retries: int = DEFAULT_MAX_RETRIES, - timeout: float | Timeout | None | NotGiven = NOT_GIVEN, - transport: AsyncTransport | None = None, - proxies: ProxiesTypes | None = None, - limits: Limits | None = None, + timeout: float | Timeout | None | NotGiven = not_given, http_client: httpx.AsyncClient | None = None, custom_headers: Mapping[str, str] | None = None, custom_query: Mapping[str, object] | None = None, ) -> None: - kwargs: dict[str, Any] = {} - if limits is not None: - warnings.warn( - "The `connection_pool_limits` argument is deprecated. The `http_client` argument should be passed instead", - category=DeprecationWarning, - stacklevel=3, - ) - if http_client is not None: - raise ValueError("The `http_client` argument is mutually exclusive with `connection_pool_limits`") - else: - limits = DEFAULT_CONNECTION_LIMITS - - if transport is not None: - kwargs["transport"] = transport - warnings.warn( - "The `transport` argument is deprecated. The `http_client` argument should be passed instead", - category=DeprecationWarning, - stacklevel=3, - ) - if http_client is not None: - raise ValueError("The `http_client` argument is mutually exclusive with `transport`") - - if proxies is not None: - kwargs["proxies"] = proxies - warnings.warn( - "The `proxies` argument is deprecated. The `http_client` argument should be passed instead", - category=DeprecationWarning, - stacklevel=3, - ) - if http_client is not None: - raise ValueError("The `http_client` argument is mutually exclusive with `proxies`") - if not is_given(timeout): # if the user passed in a custom http client with a non-default # timeout set then we use that timeout. @@ -1426,11 +1382,8 @@ def __init__( super().__init__( version=version, base_url=base_url, - limits=limits, # cast to a valid type because mypy doesn't understand our type narrowing timeout=cast(Timeout, timeout), - proxies=proxies, - transport=transport, max_retries=max_retries, custom_query=custom_query, custom_headers=custom_headers, @@ -1440,9 +1393,6 @@ def __init__( base_url=base_url, # cast to a valid type because mypy doesn't understand our type narrowing timeout=cast(Timeout, timeout), - limits=limits, - follow_redirects=True, - **kwargs, # type: ignore ) def is_closed(self) -> bool: @@ -1491,7 +1441,6 @@ async def request( options: FinalRequestOptions, *, stream: Literal[False] = False, - remaining_retries: Optional[int] = None, ) -> ResponseT: ... @overload @@ -1502,7 +1451,6 @@ async def request( *, stream: Literal[True], stream_cls: type[_AsyncStreamT], - remaining_retries: Optional[int] = None, ) -> _AsyncStreamT: ... @overload @@ -1513,7 +1461,6 @@ async def request( *, stream: bool, stream_cls: type[_AsyncStreamT] | None = None, - remaining_retries: Optional[int] = None, ) -> ResponseT | _AsyncStreamT: ... async def request( @@ -1523,116 +1470,114 @@ async def request( *, stream: bool = False, stream_cls: type[_AsyncStreamT] | None = None, - remaining_retries: Optional[int] = None, - ) -> ResponseT | _AsyncStreamT: - if remaining_retries is not None: - retries_taken = options.get_max_retries(self.max_retries) - remaining_retries - else: - retries_taken = 0 - - return await self._request( - cast_to=cast_to, - options=options, - stream=stream, - stream_cls=stream_cls, - retries_taken=retries_taken, - ) - - async def _request( - self, - cast_to: Type[ResponseT], - options: FinalRequestOptions, - *, - stream: bool, - stream_cls: type[_AsyncStreamT] | None, - retries_taken: int, ) -> ResponseT | _AsyncStreamT: if self._platform is None: # `get_platform` can make blocking IO calls so we # execute it earlier while we are in an async context self._platform = await asyncify(get_platform)() + cast_to = self._maybe_override_cast_to(cast_to, options) + # create a copy of the options we were given so that if the # options are mutated later & we then retry, the retries are # given the original options input_options = model_copy(options) + if input_options.idempotency_key is None and input_options.method.lower() != "get": + # ensure the idempotency key is reused between requests + input_options.idempotency_key = self._idempotency_key() - cast_to = self._maybe_override_cast_to(cast_to, options) - options = await self._prepare_options(options) + response: httpx.Response | None = None + max_retries = input_options.get_max_retries(self.max_retries) - remaining_retries = options.get_max_retries(self.max_retries) - retries_taken - request = self._build_request(options, retries_taken=retries_taken) - await self._prepare_request(request) + retries_taken = 0 + for retries_taken in range(max_retries + 1): + options = model_copy(input_options) + options = await self._prepare_options(options) - kwargs: HttpxSendArgs = {} - if self.custom_auth is not None: - kwargs["auth"] = self.custom_auth + remaining_retries = max_retries - retries_taken + request = self._build_request(options, retries_taken=retries_taken) + await self._prepare_request(request) - try: - response = await self._client.send( - request, - stream=stream or self._should_stream_response_body(request=request), - **kwargs, - ) - except httpx.TimeoutException as err: - log.debug("Encountered httpx.TimeoutException", exc_info=True) + kwargs: HttpxSendArgs = {} + if self.custom_auth is not None: + kwargs["auth"] = self.custom_auth - if remaining_retries > 0: - return await self._retry_request( - input_options, - cast_to, - retries_taken=retries_taken, - stream=stream, - stream_cls=stream_cls, - response_headers=None, - ) + if options.follow_redirects is not None: + kwargs["follow_redirects"] = options.follow_redirects - log.debug("Raising timeout error") - raise APITimeoutError(request=request) from err - except Exception as err: - log.debug("Encountered Exception", exc_info=True) + log.debug("Sending HTTP Request: %s %s", request.method, request.url) - if remaining_retries > 0: - return await self._retry_request( - input_options, - cast_to, - retries_taken=retries_taken, - stream=stream, - stream_cls=stream_cls, - response_headers=None, + response = None + try: + response = await self._client.send( + request, + stream=stream or self._should_stream_response_body(request=request), + **kwargs, ) + except httpx.TimeoutException as err: + log.debug("Encountered httpx.TimeoutException", exc_info=True) + + if remaining_retries > 0: + await self._sleep_for_retry( + retries_taken=retries_taken, + max_retries=max_retries, + options=input_options, + response=None, + ) + continue + + log.debug("Raising timeout error") + raise APITimeoutError(request=request) from err + except Exception as err: + log.debug("Encountered Exception", exc_info=True) + + if remaining_retries > 0: + await self._sleep_for_retry( + retries_taken=retries_taken, + max_retries=max_retries, + options=input_options, + response=None, + ) + continue + + log.debug("Raising connection error") + raise APIConnectionError(request=request) from err + + log.debug( + 'HTTP Response: %s %s "%i %s" %s', + request.method, + request.url, + response.status_code, + response.reason_phrase, + response.headers, + ) - log.debug("Raising connection error") - raise APIConnectionError(request=request) from err + try: + response.raise_for_status() + except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code + log.debug("Encountered httpx.HTTPStatusError", exc_info=True) + + if remaining_retries > 0 and self._should_retry(err.response): + await err.response.aclose() + await self._sleep_for_retry( + retries_taken=retries_taken, + max_retries=max_retries, + options=input_options, + response=response, + ) + continue - log.debug( - 'HTTP Request: %s %s "%i %s"', request.method, request.url, response.status_code, response.reason_phrase - ) + # If the response is streamed then we need to explicitly read the response + # to completion before attempting to access the response text. + if not err.response.is_closed: + await err.response.aread() - try: - response.raise_for_status() - except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code - log.debug("Encountered httpx.HTTPStatusError", exc_info=True) - - if remaining_retries > 0 and self._should_retry(err.response): - await err.response.aclose() - return await self._retry_request( - input_options, - cast_to, - retries_taken=retries_taken, - response_headers=err.response.headers, - stream=stream, - stream_cls=stream_cls, - ) + log.debug("Re-raising status error") + raise self._make_status_error_from_response(err.response) from None - # If the response is streamed then we need to explicitly read the response - # to completion before attempting to access the response text. - if not err.response.is_closed: - await err.response.aread() - - log.debug("Re-raising status error") - raise self._make_status_error_from_response(err.response) from None + break + assert response is not None, "could not resolve response (should never happen)" return await self._process_response( cast_to=cast_to, options=options, @@ -1642,35 +1587,20 @@ async def _request( retries_taken=retries_taken, ) - async def _retry_request( - self, - options: FinalRequestOptions, - cast_to: Type[ResponseT], - *, - retries_taken: int, - response_headers: httpx.Headers | None, - stream: bool, - stream_cls: type[_AsyncStreamT] | None, - ) -> ResponseT | _AsyncStreamT: - remaining_retries = options.get_max_retries(self.max_retries) - retries_taken + async def _sleep_for_retry( + self, *, retries_taken: int, max_retries: int, options: FinalRequestOptions, response: httpx.Response | None + ) -> None: + remaining_retries = max_retries - retries_taken if remaining_retries == 1: log.debug("1 retry left") else: log.debug("%i retries left", remaining_retries) - timeout = self._calculate_retry_timeout(remaining_retries, options, response_headers) + timeout = self._calculate_retry_timeout(remaining_retries, options, response.headers if response else None) log.info("Retrying request to %s in %f seconds", options.url, timeout) await anyio.sleep(timeout) - return await self._request( - options=options, - cast_to=cast_to, - retries_taken=retries_taken + 1, - stream=stream, - stream_cls=stream_cls, - ) - async def _process_response( self, *, @@ -1683,7 +1613,14 @@ async def _process_response( ) -> ResponseT: origin = get_origin(cast_to) or cast_to - if inspect.isclass(origin) and issubclass(origin, BaseAPIResponse): + if ( + inspect.isclass(origin) + and issubclass(origin, BaseAPIResponse) + # we only want to actually return the custom BaseAPIResponse class if we're + # returning the raw response, or if we're not streaming SSE, as if we're streaming + # SSE then `cast_to` doesn't actively reflect the type we need to parse into + and (not stream or bool(response.request.headers.get(RAW_RESPONSE_HEADER))) + ): if not issubclass(origin, AsyncAPIResponse): raise TypeError(f"API Response types must subclass {AsyncAPIResponse}; Received {origin}") @@ -1881,8 +1818,8 @@ def make_request_options( extra_query: Query | None = None, extra_body: Body | None = None, idempotency_key: str | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - post_parser: PostParser | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + post_parser: PostParser | NotGiven = not_given, ) -> RequestOptions: """Create a dict of type RequestOptions without keys of NotGiven values.""" options: RequestOptions = {} diff --git a/src/postgrid/_client.py b/src/postgrid/_client.py index ff0c50e..c3e506b 100644 --- a/src/postgrid/_client.py +++ b/src/postgrid/_client.py @@ -3,7 +3,7 @@ from __future__ import annotations import os -from typing import Any, Union, Mapping +from typing import Any, Mapping from typing_extensions import Self, override import httpx @@ -11,7 +11,6 @@ from . import _exceptions from ._qs import Querystring from ._types import ( - NOT_GIVEN, Omit, Headers, Timeout, @@ -19,11 +18,9 @@ Transport, ProxiesTypes, RequestOptions, + not_given, ) -from ._utils import ( - is_given, - get_async_library, -) +from ._utils import is_given, get_async_library from ._version import __version__ from .resources import address_verification, intl_address_verification from ._streaming import Stream as Stream, AsyncStream as AsyncStream @@ -64,7 +61,7 @@ def __init__( address_verification_api_key: str | None = None, print_mail_api_key: str | None = None, base_url: str | httpx.URL | None = None, - timeout: Union[float, Timeout, None, NotGiven] = NOT_GIVEN, + timeout: float | Timeout | None | NotGiven = not_given, max_retries: int = DEFAULT_MAX_RETRIES, default_headers: Mapping[str, str] | None = None, default_query: Mapping[str, object] | None = None, @@ -126,11 +123,7 @@ def qs(self) -> Querystring: @property @override def auth_headers(self) -> dict[str, str]: - if self._address_verification_api_key_auth: - return self._address_verification_api_key_auth - if self._print_mail_api_key_auth: - return self._print_mail_api_key_auth - return {} + return {**self._address_verification_api_key_auth, **self._print_mail_api_key_auth} @property def _address_verification_api_key_auth(self) -> dict[str, str]: @@ -177,9 +170,9 @@ def copy( address_verification_api_key: str | None = None, print_mail_api_key: str | None = None, base_url: str | httpx.URL | None = None, - timeout: float | Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | Timeout | None | NotGiven = not_given, http_client: httpx.Client | None = None, - max_retries: int | NotGiven = NOT_GIVEN, + max_retries: int | NotGiven = not_given, default_headers: Mapping[str, str] | None = None, set_default_headers: Mapping[str, str] | None = None, default_query: Mapping[str, object] | None = None, @@ -275,7 +268,7 @@ def __init__( address_verification_api_key: str | None = None, print_mail_api_key: str | None = None, base_url: str | httpx.URL | None = None, - timeout: Union[float, Timeout, None, NotGiven] = NOT_GIVEN, + timeout: float | Timeout | None | NotGiven = not_given, max_retries: int = DEFAULT_MAX_RETRIES, default_headers: Mapping[str, str] | None = None, default_query: Mapping[str, object] | None = None, @@ -293,7 +286,7 @@ def __init__( # part of our public interface in the future. _strict_response_validation: bool = False, ) -> None: - """Construct a new async PostGrid client instance. + """Construct a new async AsyncPostGrid client instance. This automatically infers the following arguments from their corresponding environment variables if they are not provided: - `address_verification_api_key` from `POSTGRID_ADDRESS_VERIFICATION_API_KEY` @@ -337,11 +330,7 @@ def qs(self) -> Querystring: @property @override def auth_headers(self) -> dict[str, str]: - if self._address_verification_api_key_auth: - return self._address_verification_api_key_auth - if self._print_mail_api_key_auth: - return self._print_mail_api_key_auth - return {} + return {**self._address_verification_api_key_auth, **self._print_mail_api_key_auth} @property def _address_verification_api_key_auth(self) -> dict[str, str]: @@ -388,9 +377,9 @@ def copy( address_verification_api_key: str | None = None, print_mail_api_key: str | None = None, base_url: str | httpx.URL | None = None, - timeout: float | Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | Timeout | None | NotGiven = not_given, http_client: httpx.AsyncClient | None = None, - max_retries: int | NotGiven = NOT_GIVEN, + max_retries: int | NotGiven = not_given, default_headers: Mapping[str, str] | None = None, set_default_headers: Mapping[str, str] | None = None, default_query: Mapping[str, object] | None = None, diff --git a/src/postgrid/_compat.py b/src/postgrid/_compat.py index 92d9ee6..bdef67f 100644 --- a/src/postgrid/_compat.py +++ b/src/postgrid/_compat.py @@ -12,14 +12,13 @@ _T = TypeVar("_T") _ModelT = TypeVar("_ModelT", bound=pydantic.BaseModel) -# --------------- Pydantic v2 compatibility --------------- +# --------------- Pydantic v2, v3 compatibility --------------- # Pyright incorrectly reports some of our functions as overriding a method when they don't # pyright: reportIncompatibleMethodOverride=false -PYDANTIC_V2 = pydantic.VERSION.startswith("2.") +PYDANTIC_V1 = pydantic.VERSION.startswith("1.") -# v1 re-exports if TYPE_CHECKING: def parse_date(value: date | StrBytesIntFloat) -> date: # noqa: ARG001 @@ -44,90 +43,92 @@ def is_typeddict(type_: type[Any]) -> bool: # noqa: ARG001 ... else: - if PYDANTIC_V2: - from pydantic.v1.typing import ( + # v1 re-exports + if PYDANTIC_V1: + from pydantic.typing import ( get_args as get_args, is_union as is_union, get_origin as get_origin, is_typeddict as is_typeddict, is_literal_type as is_literal_type, ) - from pydantic.v1.datetime_parse import parse_date as parse_date, parse_datetime as parse_datetime + from pydantic.datetime_parse import parse_date as parse_date, parse_datetime as parse_datetime else: - from pydantic.typing import ( + from ._utils import ( get_args as get_args, is_union as is_union, get_origin as get_origin, + parse_date as parse_date, is_typeddict as is_typeddict, + parse_datetime as parse_datetime, is_literal_type as is_literal_type, ) - from pydantic.datetime_parse import parse_date as parse_date, parse_datetime as parse_datetime # refactored config if TYPE_CHECKING: from pydantic import ConfigDict as ConfigDict else: - if PYDANTIC_V2: - from pydantic import ConfigDict - else: + if PYDANTIC_V1: # TODO: provide an error message here? ConfigDict = None + else: + from pydantic import ConfigDict as ConfigDict # renamed methods / properties def parse_obj(model: type[_ModelT], value: object) -> _ModelT: - if PYDANTIC_V2: - return model.model_validate(value) - else: + if PYDANTIC_V1: return cast(_ModelT, model.parse_obj(value)) # pyright: ignore[reportDeprecated, reportUnnecessaryCast] + else: + return model.model_validate(value) def field_is_required(field: FieldInfo) -> bool: - if PYDANTIC_V2: - return field.is_required() - return field.required # type: ignore + if PYDANTIC_V1: + return field.required # type: ignore + return field.is_required() def field_get_default(field: FieldInfo) -> Any: value = field.get_default() - if PYDANTIC_V2: - from pydantic_core import PydanticUndefined - - if value == PydanticUndefined: - return None + if PYDANTIC_V1: return value + from pydantic_core import PydanticUndefined + + if value == PydanticUndefined: + return None return value def field_outer_type(field: FieldInfo) -> Any: - if PYDANTIC_V2: - return field.annotation - return field.outer_type_ # type: ignore + if PYDANTIC_V1: + return field.outer_type_ # type: ignore + return field.annotation def get_model_config(model: type[pydantic.BaseModel]) -> Any: - if PYDANTIC_V2: - return model.model_config - return model.__config__ # type: ignore + if PYDANTIC_V1: + return model.__config__ # type: ignore + return model.model_config def get_model_fields(model: type[pydantic.BaseModel]) -> dict[str, FieldInfo]: - if PYDANTIC_V2: - return model.model_fields - return model.__fields__ # type: ignore + if PYDANTIC_V1: + return model.__fields__ # type: ignore + return model.model_fields def model_copy(model: _ModelT, *, deep: bool = False) -> _ModelT: - if PYDANTIC_V2: - return model.model_copy(deep=deep) - return model.copy(deep=deep) # type: ignore + if PYDANTIC_V1: + return model.copy(deep=deep) # type: ignore + return model.model_copy(deep=deep) def model_json(model: pydantic.BaseModel, *, indent: int | None = None) -> str: - if PYDANTIC_V2: - return model.model_dump_json(indent=indent) - return model.json(indent=indent) # type: ignore + if PYDANTIC_V1: + return model.json(indent=indent) # type: ignore + return model.model_dump_json(indent=indent) def model_dump( @@ -139,14 +140,14 @@ def model_dump( warnings: bool = True, mode: Literal["json", "python"] = "python", ) -> dict[str, Any]: - if PYDANTIC_V2 or hasattr(model, "model_dump"): + if (not PYDANTIC_V1) or hasattr(model, "model_dump"): return model.model_dump( mode=mode, exclude=exclude, exclude_unset=exclude_unset, exclude_defaults=exclude_defaults, # warnings are not supported in Pydantic v1 - warnings=warnings if PYDANTIC_V2 else True, + warnings=True if PYDANTIC_V1 else warnings, ) return cast( "dict[str, Any]", @@ -159,9 +160,9 @@ def model_dump( def model_parse(model: type[_ModelT], data: Any) -> _ModelT: - if PYDANTIC_V2: - return model.model_validate(data) - return model.parse_obj(data) # pyright: ignore[reportDeprecated] + if PYDANTIC_V1: + return model.parse_obj(data) # pyright: ignore[reportDeprecated] + return model.model_validate(data) # generic models @@ -170,17 +171,16 @@ def model_parse(model: type[_ModelT], data: Any) -> _ModelT: class GenericModel(pydantic.BaseModel): ... else: - if PYDANTIC_V2: + if PYDANTIC_V1: + import pydantic.generics + + class GenericModel(pydantic.generics.GenericModel, pydantic.BaseModel): ... + else: # there no longer needs to be a distinction in v2 but # we still have to create our own subclass to avoid # inconsistent MRO ordering errors class GenericModel(pydantic.BaseModel): ... - else: - import pydantic.generics - - class GenericModel(pydantic.generics.GenericModel, pydantic.BaseModel): ... - # cached properties if TYPE_CHECKING: diff --git a/src/postgrid/_files.py b/src/postgrid/_files.py index 715cc20..cc14c14 100644 --- a/src/postgrid/_files.py +++ b/src/postgrid/_files.py @@ -69,12 +69,12 @@ def _transform_file(file: FileTypes) -> HttpxFileTypes: return file if is_tuple_t(file): - return (file[0], _read_file_content(file[1]), *file[2:]) + return (file[0], read_file_content(file[1]), *file[2:]) raise TypeError(f"Expected file types input to be a FileContent type or to be a tuple") -def _read_file_content(file: FileContent) -> HttpxFileContent: +def read_file_content(file: FileContent) -> HttpxFileContent: if isinstance(file, os.PathLike): return pathlib.Path(file).read_bytes() return file @@ -111,12 +111,12 @@ async def _async_transform_file(file: FileTypes) -> HttpxFileTypes: return file if is_tuple_t(file): - return (file[0], await _async_read_file_content(file[1]), *file[2:]) + return (file[0], await async_read_file_content(file[1]), *file[2:]) raise TypeError(f"Expected file types input to be a FileContent type or to be a tuple") -async def _async_read_file_content(file: FileContent) -> HttpxFileContent: +async def async_read_file_content(file: FileContent) -> HttpxFileContent: if isinstance(file, os.PathLike): return await anyio.Path(file).read_bytes() diff --git a/src/postgrid/_models.py b/src/postgrid/_models.py index c4401ff..6a3cd1d 100644 --- a/src/postgrid/_models.py +++ b/src/postgrid/_models.py @@ -2,9 +2,10 @@ import os import inspect -from typing import TYPE_CHECKING, Any, Type, Union, Generic, TypeVar, Callable, cast +from typing import TYPE_CHECKING, Any, Type, Union, Generic, TypeVar, Callable, Optional, cast from datetime import date, datetime from typing_extensions import ( + List, Unpack, Literal, ClassVar, @@ -19,7 +20,6 @@ ) import pydantic -import pydantic.generics from pydantic.fields import FieldInfo from ._types import ( @@ -50,7 +50,7 @@ strip_annotated_type, ) from ._compat import ( - PYDANTIC_V2, + PYDANTIC_V1, ConfigDict, GenericModel as BaseGenericModel, get_args, @@ -65,7 +65,7 @@ from ._constants import RAW_RESPONSE_HEADER if TYPE_CHECKING: - from pydantic_core.core_schema import ModelField, LiteralSchema, ModelFieldsSchema + from pydantic_core.core_schema import ModelField, ModelSchema, LiteralSchema, ModelFieldsSchema __all__ = ["BaseModel", "GenericModel"] @@ -81,11 +81,7 @@ class _ConfigProtocol(Protocol): class BaseModel(pydantic.BaseModel): - if PYDANTIC_V2: - model_config: ClassVar[ConfigDict] = ConfigDict( - extra="allow", defer_build=coerce_boolean(os.environ.get("DEFER_PYDANTIC_BUILD", "true")) - ) - else: + if PYDANTIC_V1: @property @override @@ -95,6 +91,10 @@ def model_fields_set(self) -> set[str]: class Config(pydantic.BaseConfig): # pyright: ignore[reportDeprecated] extra: Any = pydantic.Extra.allow # type: ignore + else: + model_config: ClassVar[ConfigDict] = ConfigDict( + extra="allow", defer_build=coerce_boolean(os.environ.get("DEFER_PYDANTIC_BUILD", "true")) + ) def to_dict( self, @@ -208,28 +208,32 @@ def construct( # pyright: ignore[reportIncompatibleMethodOverride] else: fields_values[name] = field_get_default(field) + extra_field_type = _get_extra_fields_type(__cls) + _extra = {} for key, value in values.items(): if key not in model_fields: - if PYDANTIC_V2: - _extra[key] = value - else: + parsed = construct_type(value=value, type_=extra_field_type) if extra_field_type is not None else value + + if PYDANTIC_V1: _fields_set.add(key) - fields_values[key] = value + fields_values[key] = parsed + else: + _extra[key] = parsed object.__setattr__(m, "__dict__", fields_values) - if PYDANTIC_V2: - # these properties are copied from Pydantic's `model_construct()` method - object.__setattr__(m, "__pydantic_private__", None) - object.__setattr__(m, "__pydantic_extra__", _extra) - object.__setattr__(m, "__pydantic_fields_set__", _fields_set) - else: + if PYDANTIC_V1: # init_private_attributes() does not exist in v2 m._init_private_attributes() # type: ignore # copied from Pydantic v1's `construct()` method object.__setattr__(m, "__fields_set__", _fields_set) + else: + # these properties are copied from Pydantic's `model_construct()` method + object.__setattr__(m, "__pydantic_private__", None) + object.__setattr__(m, "__pydantic_extra__", _extra) + object.__setattr__(m, "__pydantic_fields_set__", _fields_set) return m @@ -239,7 +243,7 @@ def construct( # pyright: ignore[reportIncompatibleMethodOverride] # although not in practice model_construct = construct - if not PYDANTIC_V2: + if PYDANTIC_V1: # we define aliases for some of the new pydantic v2 methods so # that we can just document these methods without having to specify # a specific pydantic version as some users may not know which @@ -252,7 +256,7 @@ def model_dump( mode: Literal["json", "python"] | str = "python", include: IncEx | None = None, exclude: IncEx | None = None, - by_alias: bool = False, + by_alias: bool | None = None, exclude_unset: bool = False, exclude_defaults: bool = False, exclude_none: bool = False, @@ -260,6 +264,7 @@ def model_dump( warnings: bool | Literal["none", "warn", "error"] = True, context: dict[str, Any] | None = None, serialize_as_any: bool = False, + fallback: Callable[[Any], Any] | None = None, ) -> dict[str, Any]: """Usage docs: https://docs.pydantic.dev/2.4/concepts/serialization/#modelmodel_dump @@ -291,16 +296,18 @@ def model_dump( raise ValueError("context is only supported in Pydantic v2") if serialize_as_any != False: raise ValueError("serialize_as_any is only supported in Pydantic v2") + if fallback is not None: + raise ValueError("fallback is only supported in Pydantic v2") dumped = super().dict( # pyright: ignore[reportDeprecated] include=include, exclude=exclude, - by_alias=by_alias, + by_alias=by_alias if by_alias is not None else False, exclude_unset=exclude_unset, exclude_defaults=exclude_defaults, exclude_none=exclude_none, ) - return cast(dict[str, Any], json_safe(dumped)) if mode == "json" else dumped + return cast("dict[str, Any]", json_safe(dumped)) if mode == "json" else dumped @override def model_dump_json( @@ -309,13 +316,14 @@ def model_dump_json( indent: int | None = None, include: IncEx | None = None, exclude: IncEx | None = None, - by_alias: bool = False, + by_alias: bool | None = None, exclude_unset: bool = False, exclude_defaults: bool = False, exclude_none: bool = False, round_trip: bool = False, warnings: bool | Literal["none", "warn", "error"] = True, context: dict[str, Any] | None = None, + fallback: Callable[[Any], Any] | None = None, serialize_as_any: bool = False, ) -> str: """Usage docs: https://docs.pydantic.dev/2.4/concepts/serialization/#modelmodel_dump_json @@ -344,11 +352,13 @@ def model_dump_json( raise ValueError("context is only supported in Pydantic v2") if serialize_as_any != False: raise ValueError("serialize_as_any is only supported in Pydantic v2") + if fallback is not None: + raise ValueError("fallback is only supported in Pydantic v2") return super().json( # type: ignore[reportDeprecated] indent=indent, include=include, exclude=exclude, - by_alias=by_alias, + by_alias=by_alias if by_alias is not None else False, exclude_unset=exclude_unset, exclude_defaults=exclude_defaults, exclude_none=exclude_none, @@ -359,15 +369,32 @@ def _construct_field(value: object, field: FieldInfo, key: str) -> object: if value is None: return field_get_default(field) - if PYDANTIC_V2: - type_ = field.annotation - else: + if PYDANTIC_V1: type_ = cast(type, field.outer_type_) # type: ignore + else: + type_ = field.annotation # type: ignore if type_ is None: raise RuntimeError(f"Unexpected field type is None for {key}") - return construct_type(value=value, type_=type_) + return construct_type(value=value, type_=type_, metadata=getattr(field, "metadata", None)) + + +def _get_extra_fields_type(cls: type[pydantic.BaseModel]) -> type | None: + if PYDANTIC_V1: + # TODO + return None + + schema = cls.__pydantic_core_schema__ + if schema["type"] == "model": + fields = schema["schema"] + if fields["type"] == "model-fields": + extras = fields.get("extras_schema") + if extras and "cls" in extras: + # mypy can't narrow the type + return extras["cls"] # type: ignore[no-any-return] + + return None def is_basemodel(type_: type) -> bool: @@ -421,7 +448,7 @@ def construct_type_unchecked(*, value: object, type_: type[_T]) -> _T: return cast(_T, construct_type(value=value, type_=type_)) -def construct_type(*, value: object, type_: object) -> object: +def construct_type(*, value: object, type_: object, metadata: Optional[List[Any]] = None) -> object: """Loose coercion to the expected type with construction of nested values. If the given value does not match the expected type then it is returned as-is. @@ -439,8 +466,10 @@ def construct_type(*, value: object, type_: object) -> object: type_ = type_.__value__ # type: ignore[unreachable] # unwrap `Annotated[T, ...]` -> `T` - if is_annotated_type(type_): - meta: tuple[Any, ...] = get_args(type_)[1:] + if metadata is not None and len(metadata) > 0: + meta: tuple[Any, ...] = tuple(metadata) + elif is_annotated_type(type_): + meta = get_args(type_)[1:] type_ = extract_type_arg(type_, 0) else: meta = tuple() @@ -605,30 +634,30 @@ def _build_discriminated_union_meta(*, union: type, meta_annotations: tuple[Any, for variant in get_args(union): variant = strip_annotated_type(variant) if is_basemodel_type(variant): - if PYDANTIC_V2: - field = _extract_field_schema_pv2(variant, discriminator_field_name) - if not field: + if PYDANTIC_V1: + field_info = cast("dict[str, FieldInfo]", variant.__fields__).get(discriminator_field_name) # pyright: ignore[reportDeprecated, reportUnnecessaryCast] + if not field_info: continue # Note: if one variant defines an alias then they all should - discriminator_alias = field.get("serialization_alias") - - field_schema = field["schema"] + discriminator_alias = field_info.alias - if field_schema["type"] == "literal": - for entry in cast("LiteralSchema", field_schema)["expected"]: + if (annotation := getattr(field_info, "annotation", None)) and is_literal_type(annotation): + for entry in get_args(annotation): if isinstance(entry, str): mapping[entry] = variant else: - field_info = cast("dict[str, FieldInfo]", variant.__fields__).get(discriminator_field_name) # pyright: ignore[reportDeprecated, reportUnnecessaryCast] - if not field_info: + field = _extract_field_schema_pv2(variant, discriminator_field_name) + if not field: continue # Note: if one variant defines an alias then they all should - discriminator_alias = field_info.alias + discriminator_alias = field.get("serialization_alias") - if field_info.annotation and is_literal_type(field_info.annotation): - for entry in get_args(field_info.annotation): + field_schema = field["schema"] + + if field_schema["type"] == "literal": + for entry in cast("LiteralSchema", field_schema)["expected"]: if isinstance(entry, str): mapping[entry] = variant @@ -646,15 +675,18 @@ def _build_discriminated_union_meta(*, union: type, meta_annotations: tuple[Any, def _extract_field_schema_pv2(model: type[BaseModel], field_name: str) -> ModelField | None: schema = model.__pydantic_core_schema__ + if schema["type"] == "definitions": + schema = schema["schema"] + if schema["type"] != "model": return None + schema = cast("ModelSchema", schema) fields_schema = schema["schema"] if fields_schema["type"] != "model-fields": return None fields_schema = cast("ModelFieldsSchema", fields_schema) - field = fields_schema["fields"].get(field_name) if not field: return None @@ -678,7 +710,7 @@ def set_pydantic_config(typ: Any, config: pydantic.ConfigDict) -> None: setattr(typ, "__pydantic_config__", config) # noqa: B010 -# our use of subclasssing here causes weirdness for type checkers, +# our use of subclassing here causes weirdness for type checkers, # so we just pretend that we don't subclass if TYPE_CHECKING: GenericModel = BaseModel @@ -688,7 +720,7 @@ class GenericModel(BaseGenericModel, BaseModel): pass -if PYDANTIC_V2: +if not PYDANTIC_V1: from pydantic import TypeAdapter as _TypeAdapter _CachedTypeAdapter = cast("TypeAdapter[object]", lru_cache(maxsize=None)(_TypeAdapter)) @@ -735,6 +767,7 @@ class FinalRequestOptionsInput(TypedDict, total=False): idempotency_key: str json_data: Body extra_json: AnyMapping + follow_redirects: bool @final @@ -748,18 +781,19 @@ class FinalRequestOptions(pydantic.BaseModel): files: Union[HttpxRequestFiles, None] = None idempotency_key: Union[str, None] = None post_parser: Union[Callable[[Any], Any], NotGiven] = NotGiven() + follow_redirects: Union[bool, None] = None # It should be noted that we cannot use `json` here as that would override # a BaseModel method in an incompatible fashion. json_data: Union[Body, None] = None extra_json: Union[AnyMapping, None] = None - if PYDANTIC_V2: - model_config: ClassVar[ConfigDict] = ConfigDict(arbitrary_types_allowed=True) - else: + if PYDANTIC_V1: class Config(pydantic.BaseConfig): # pyright: ignore[reportDeprecated] arbitrary_types_allowed: bool = True + else: + model_config: ClassVar[ConfigDict] = ConfigDict(arbitrary_types_allowed=True) def get_max_retries(self, max_retries: int) -> int: if isinstance(self.max_retries, NotGiven): @@ -792,9 +826,9 @@ def construct( # type: ignore key: strip_not_given(value) for key, value in values.items() } - if PYDANTIC_V2: - return super().model_construct(_fields_set, **kwargs) - return cast(FinalRequestOptions, super().construct(_fields_set, **kwargs)) # pyright: ignore[reportDeprecated] + if PYDANTIC_V1: + return cast(FinalRequestOptions, super().construct(_fields_set, **kwargs)) # pyright: ignore[reportDeprecated] + return super().model_construct(_fields_set, **kwargs) if not TYPE_CHECKING: # type checkers incorrectly complain about this assignment diff --git a/src/postgrid/_qs.py b/src/postgrid/_qs.py index 274320c..ada6fd3 100644 --- a/src/postgrid/_qs.py +++ b/src/postgrid/_qs.py @@ -4,7 +4,7 @@ from urllib.parse import parse_qs, urlencode from typing_extensions import Literal, get_args -from ._types import NOT_GIVEN, NotGiven, NotGivenOr +from ._types import NotGiven, not_given from ._utils import flatten _T = TypeVar("_T") @@ -41,8 +41,8 @@ def stringify( self, params: Params, *, - array_format: NotGivenOr[ArrayFormat] = NOT_GIVEN, - nested_format: NotGivenOr[NestedFormat] = NOT_GIVEN, + array_format: ArrayFormat | NotGiven = not_given, + nested_format: NestedFormat | NotGiven = not_given, ) -> str: return urlencode( self.stringify_items( @@ -56,8 +56,8 @@ def stringify_items( self, params: Params, *, - array_format: NotGivenOr[ArrayFormat] = NOT_GIVEN, - nested_format: NotGivenOr[NestedFormat] = NOT_GIVEN, + array_format: ArrayFormat | NotGiven = not_given, + nested_format: NestedFormat | NotGiven = not_given, ) -> list[tuple[str, str]]: opts = Options( qs=self, @@ -143,8 +143,8 @@ def __init__( self, qs: Querystring = _qs, *, - array_format: NotGivenOr[ArrayFormat] = NOT_GIVEN, - nested_format: NotGivenOr[NestedFormat] = NOT_GIVEN, + array_format: ArrayFormat | NotGiven = not_given, + nested_format: NestedFormat | NotGiven = not_given, ) -> None: self.array_format = qs.array_format if isinstance(array_format, NotGiven) else array_format self.nested_format = qs.nested_format if isinstance(nested_format, NotGiven) else nested_format diff --git a/src/postgrid/_response.py b/src/postgrid/_response.py index 2fa48d8..6fcb73f 100644 --- a/src/postgrid/_response.py +++ b/src/postgrid/_response.py @@ -233,7 +233,7 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T: # split is required to handle cases where additional information is included # in the response, e.g. application/json; charset=utf-8 content_type, *_ = response.headers.get("content-type", "*").split(";") - if content_type != "application/json": + if not content_type.endswith("json"): if is_basemodel(cast_to): try: data = response.json() diff --git a/src/postgrid/_streaming.py b/src/postgrid/_streaming.py index 25941e4..37e30f8 100644 --- a/src/postgrid/_streaming.py +++ b/src/postgrid/_streaming.py @@ -57,9 +57,8 @@ def __stream__(self) -> Iterator[_T]: for sse in iterator: yield process_data(data=sse.json(), cast_to=cast_to, response=response) - # Ensure the entire stream is consumed - for _sse in iterator: - ... + # As we might not fully consume the response stream, we need to close it explicitly + response.close() def __enter__(self) -> Self: return self @@ -121,9 +120,8 @@ async def __stream__(self) -> AsyncIterator[_T]: async for sse in iterator: yield process_data(data=sse.json(), cast_to=cast_to, response=response) - # Ensure the entire stream is consumed - async for _sse in iterator: - ... + # As we might not fully consume the response stream, we need to close it explicitly + await response.aclose() async def __aenter__(self) -> Self: return self diff --git a/src/postgrid/_types.py b/src/postgrid/_types.py index 02899b6..225e017 100644 --- a/src/postgrid/_types.py +++ b/src/postgrid/_types.py @@ -13,10 +13,21 @@ Mapping, TypeVar, Callable, + Iterator, Optional, Sequence, ) -from typing_extensions import Set, Literal, Protocol, TypeAlias, TypedDict, override, runtime_checkable +from typing_extensions import ( + Set, + Literal, + Protocol, + TypeAlias, + TypedDict, + SupportsIndex, + overload, + override, + runtime_checkable, +) import httpx import pydantic @@ -100,23 +111,27 @@ class RequestOptions(TypedDict, total=False): params: Query extra_json: AnyMapping idempotency_key: str + follow_redirects: bool # Sentinel class used until PEP 0661 is accepted class NotGiven: """ - A sentinel singleton class used to distinguish omitted keyword arguments - from those passed in with the value None (which may have different behavior). + For parameters with a meaningful None value, we need to distinguish between + the user explicitly passing None, and the user not passing the parameter at + all. + + User code shouldn't need to use not_given directly. For example: ```py - def get(timeout: Union[int, NotGiven, None] = NotGiven()) -> Response: ... + def create(timeout: Timeout | None | NotGiven = not_given): ... - get(timeout=1) # 1s timeout - get(timeout=None) # No timeout - get() # Default timeout behavior, which may not be statically known at the method definition. + create(timeout=1) # 1s timeout + create(timeout=None) # No timeout + create() # Default timeout behavior ``` """ @@ -128,13 +143,14 @@ def __repr__(self) -> str: return "NOT_GIVEN" -NotGivenOr = Union[_T, NotGiven] +not_given = NotGiven() +# for backwards compatibility: NOT_GIVEN = NotGiven() class Omit: - """In certain situations you need to be able to represent a case where a default value has - to be explicitly removed and `None` is not an appropriate substitute, for example: + """ + To explicitly omit something from being sent in a request, use `omit`. ```py # as the default `Content-Type` header is `application/json` that will be sent @@ -144,8 +160,8 @@ class Omit: # to look something like: 'multipart/form-data; boundary=0d8382fcf5f8c3be01ca2e11002d2983' client.post(..., headers={"Content-Type": "multipart/form-data"}) - # instead you can remove the default `application/json` header by passing Omit - client.post(..., headers={"Content-Type": Omit()}) + # instead you can remove the default `application/json` header by passing omit + client.post(..., headers={"Content-Type": omit}) ``` """ @@ -153,6 +169,9 @@ def __bool__(self) -> Literal[False]: return False +omit = Omit() + + @runtime_checkable class ModelBuilderProtocol(Protocol): @classmethod @@ -215,3 +234,27 @@ class _GenericAlias(Protocol): class HttpxSendArgs(TypedDict, total=False): auth: httpx.Auth + follow_redirects: bool + + +_T_co = TypeVar("_T_co", covariant=True) + + +if TYPE_CHECKING: + # This works because str.__contains__ does not accept object (either in typeshed or at runtime) + # https://github.com/hauntsaninja/useful_types/blob/5e9710f3875107d068e7679fd7fec9cfab0eff3b/useful_types/__init__.py#L285 + class SequenceNotStr(Protocol[_T_co]): + @overload + def __getitem__(self, index: SupportsIndex, /) -> _T_co: ... + @overload + def __getitem__(self, index: slice, /) -> Sequence[_T_co]: ... + def __contains__(self, value: object, /) -> bool: ... + def __len__(self) -> int: ... + def __iter__(self) -> Iterator[_T_co]: ... + def index(self, value: Any, start: int = 0, stop: int = ..., /) -> int: ... + def count(self, value: Any, /) -> int: ... + def __reversed__(self) -> Iterator[_T_co]: ... +else: + # just point this to a normal `Sequence` at runtime to avoid having to special case + # deserializing our custom sequence type + SequenceNotStr = Sequence diff --git a/src/postgrid/_utils/__init__.py b/src/postgrid/_utils/__init__.py index d4fda26..dc64e29 100644 --- a/src/postgrid/_utils/__init__.py +++ b/src/postgrid/_utils/__init__.py @@ -10,7 +10,6 @@ lru_cache as lru_cache, is_mapping as is_mapping, is_tuple_t as is_tuple_t, - parse_date as parse_date, is_iterable as is_iterable, is_sequence as is_sequence, coerce_float as coerce_float, @@ -23,7 +22,6 @@ coerce_boolean as coerce_boolean, coerce_integer as coerce_integer, file_from_path as file_from_path, - parse_datetime as parse_datetime, strip_not_given as strip_not_given, deepcopy_minimal as deepcopy_minimal, get_async_library as get_async_library, @@ -32,12 +30,20 @@ maybe_coerce_boolean as maybe_coerce_boolean, maybe_coerce_integer as maybe_coerce_integer, ) +from ._compat import ( + get_args as get_args, + is_union as is_union, + get_origin as get_origin, + is_typeddict as is_typeddict, + is_literal_type as is_literal_type, +) from ._typing import ( is_list_type as is_list_type, is_union_type as is_union_type, extract_type_arg as extract_type_arg, is_iterable_type as is_iterable_type, is_required_type as is_required_type, + is_sequence_type as is_sequence_type, is_annotated_type as is_annotated_type, is_type_alias_type as is_type_alias_type, strip_annotated_type as strip_annotated_type, @@ -55,3 +61,4 @@ function_has_argument as function_has_argument, assert_signatures_in_sync as assert_signatures_in_sync, ) +from ._datetime_parse import parse_date as parse_date, parse_datetime as parse_datetime diff --git a/src/postgrid/_utils/_compat.py b/src/postgrid/_utils/_compat.py new file mode 100644 index 0000000..dd70323 --- /dev/null +++ b/src/postgrid/_utils/_compat.py @@ -0,0 +1,45 @@ +from __future__ import annotations + +import sys +import typing_extensions +from typing import Any, Type, Union, Literal, Optional +from datetime import date, datetime +from typing_extensions import get_args as _get_args, get_origin as _get_origin + +from .._types import StrBytesIntFloat +from ._datetime_parse import parse_date as _parse_date, parse_datetime as _parse_datetime + +_LITERAL_TYPES = {Literal, typing_extensions.Literal} + + +def get_args(tp: type[Any]) -> tuple[Any, ...]: + return _get_args(tp) + + +def get_origin(tp: type[Any]) -> type[Any] | None: + return _get_origin(tp) + + +def is_union(tp: Optional[Type[Any]]) -> bool: + if sys.version_info < (3, 10): + return tp is Union # type: ignore[comparison-overlap] + else: + import types + + return tp is Union or tp is types.UnionType + + +def is_typeddict(tp: Type[Any]) -> bool: + return typing_extensions.is_typeddict(tp) + + +def is_literal_type(tp: Type[Any]) -> bool: + return get_origin(tp) in _LITERAL_TYPES + + +def parse_date(value: Union[date, StrBytesIntFloat]) -> date: + return _parse_date(value) + + +def parse_datetime(value: Union[datetime, StrBytesIntFloat]) -> datetime: + return _parse_datetime(value) diff --git a/src/postgrid/_utils/_datetime_parse.py b/src/postgrid/_utils/_datetime_parse.py new file mode 100644 index 0000000..7cb9d9e --- /dev/null +++ b/src/postgrid/_utils/_datetime_parse.py @@ -0,0 +1,136 @@ +""" +This file contains code from https://github.com/pydantic/pydantic/blob/main/pydantic/v1/datetime_parse.py +without the Pydantic v1 specific errors. +""" + +from __future__ import annotations + +import re +from typing import Dict, Union, Optional +from datetime import date, datetime, timezone, timedelta + +from .._types import StrBytesIntFloat + +date_expr = r"(?P\d{4})-(?P\d{1,2})-(?P\d{1,2})" +time_expr = ( + r"(?P\d{1,2}):(?P\d{1,2})" + r"(?::(?P\d{1,2})(?:\.(?P\d{1,6})\d{0,6})?)?" + r"(?PZ|[+-]\d{2}(?::?\d{2})?)?$" +) + +date_re = re.compile(f"{date_expr}$") +datetime_re = re.compile(f"{date_expr}[T ]{time_expr}") + + +EPOCH = datetime(1970, 1, 1) +# if greater than this, the number is in ms, if less than or equal it's in seconds +# (in seconds this is 11th October 2603, in ms it's 20th August 1970) +MS_WATERSHED = int(2e10) +# slightly more than datetime.max in ns - (datetime.max - EPOCH).total_seconds() * 1e9 +MAX_NUMBER = int(3e20) + + +def _get_numeric(value: StrBytesIntFloat, native_expected_type: str) -> Union[None, int, float]: + if isinstance(value, (int, float)): + return value + try: + return float(value) + except ValueError: + return None + except TypeError: + raise TypeError(f"invalid type; expected {native_expected_type}, string, bytes, int or float") from None + + +def _from_unix_seconds(seconds: Union[int, float]) -> datetime: + if seconds > MAX_NUMBER: + return datetime.max + elif seconds < -MAX_NUMBER: + return datetime.min + + while abs(seconds) > MS_WATERSHED: + seconds /= 1000 + dt = EPOCH + timedelta(seconds=seconds) + return dt.replace(tzinfo=timezone.utc) + + +def _parse_timezone(value: Optional[str]) -> Union[None, int, timezone]: + if value == "Z": + return timezone.utc + elif value is not None: + offset_mins = int(value[-2:]) if len(value) > 3 else 0 + offset = 60 * int(value[1:3]) + offset_mins + if value[0] == "-": + offset = -offset + return timezone(timedelta(minutes=offset)) + else: + return None + + +def parse_datetime(value: Union[datetime, StrBytesIntFloat]) -> datetime: + """ + Parse a datetime/int/float/string and return a datetime.datetime. + + This function supports time zone offsets. When the input contains one, + the output uses a timezone with a fixed offset from UTC. + + Raise ValueError if the input is well formatted but not a valid datetime. + Raise ValueError if the input isn't well formatted. + """ + if isinstance(value, datetime): + return value + + number = _get_numeric(value, "datetime") + if number is not None: + return _from_unix_seconds(number) + + if isinstance(value, bytes): + value = value.decode() + + assert not isinstance(value, (float, int)) + + match = datetime_re.match(value) + if match is None: + raise ValueError("invalid datetime format") + + kw = match.groupdict() + if kw["microsecond"]: + kw["microsecond"] = kw["microsecond"].ljust(6, "0") + + tzinfo = _parse_timezone(kw.pop("tzinfo")) + kw_: Dict[str, Union[None, int, timezone]] = {k: int(v) for k, v in kw.items() if v is not None} + kw_["tzinfo"] = tzinfo + + return datetime(**kw_) # type: ignore + + +def parse_date(value: Union[date, StrBytesIntFloat]) -> date: + """ + Parse a date/int/float/string and return a datetime.date. + + Raise ValueError if the input is well formatted but not a valid date. + Raise ValueError if the input isn't well formatted. + """ + if isinstance(value, date): + if isinstance(value, datetime): + return value.date() + else: + return value + + number = _get_numeric(value, "date") + if number is not None: + return _from_unix_seconds(number).date() + + if isinstance(value, bytes): + value = value.decode() + + assert not isinstance(value, (float, int)) + match = date_re.match(value) + if match is None: + raise ValueError("invalid date format") + + kw = {k: int(v) for k, v in match.groupdict().items()} + + try: + return date(**kw) + except ValueError: + raise ValueError("invalid date format") from None diff --git a/src/postgrid/_utils/_proxy.py b/src/postgrid/_utils/_proxy.py index ffd883e..0f239a3 100644 --- a/src/postgrid/_utils/_proxy.py +++ b/src/postgrid/_utils/_proxy.py @@ -46,7 +46,10 @@ def __dir__(self) -> Iterable[str]: @property # type: ignore @override def __class__(self) -> type: # pyright: ignore - proxied = self.__get_proxied__() + try: + proxied = self.__get_proxied__() + except Exception: + return type(self) if issubclass(type(proxied), LazyProxy): return type(proxied) return proxied.__class__ diff --git a/src/postgrid/_utils/_resources_proxy.py b/src/postgrid/_utils/_resources_proxy.py new file mode 100644 index 0000000..cfa9f57 --- /dev/null +++ b/src/postgrid/_utils/_resources_proxy.py @@ -0,0 +1,24 @@ +from __future__ import annotations + +from typing import Any +from typing_extensions import override + +from ._proxy import LazyProxy + + +class ResourcesProxy(LazyProxy[Any]): + """A proxy for the `postgrid.resources` module. + + This is used so that we can lazily import `postgrid.resources` only when + needed *and* so that users can just import `postgrid` and reference `postgrid.resources` + """ + + @override + def __load__(self) -> Any: + import importlib + + mod = importlib.import_module("postgrid.resources") + return mod + + +resources = ResourcesProxy().__as_proxied__() diff --git a/src/postgrid/_utils/_transform.py b/src/postgrid/_utils/_transform.py index 18afd9d..5207549 100644 --- a/src/postgrid/_utils/_transform.py +++ b/src/postgrid/_utils/_transform.py @@ -5,27 +5,31 @@ import pathlib from typing import Any, Mapping, TypeVar, cast from datetime import date, datetime -from typing_extensions import Literal, get_args, override, get_type_hints +from typing_extensions import Literal, get_args, override, get_type_hints as _get_type_hints import anyio import pydantic from ._utils import ( is_list, + is_given, + lru_cache, is_mapping, is_iterable, + is_sequence, ) from .._files import is_base64_file_input +from ._compat import get_origin, is_typeddict from ._typing import ( is_list_type, is_union_type, extract_type_arg, is_iterable_type, is_required_type, + is_sequence_type, is_annotated_type, strip_annotated_type, ) -from .._compat import get_origin, model_dump, is_typeddict _T = TypeVar("_T") @@ -108,6 +112,7 @@ class Params(TypedDict, total=False): return cast(_T, transformed) +@lru_cache(maxsize=8096) def _get_annotated_type(type_: type) -> type | None: """If the given type is an `Annotated` type then it is returned, if not `None` is returned. @@ -126,7 +131,7 @@ def _get_annotated_type(type_: type) -> type | None: def _maybe_transform_key(key: str, type_: type) -> str: """Transform the given `data` based on the annotations provided in `type_`. - Note: this function only looks at `Annotated` types that contain `PropertInfo` metadata. + Note: this function only looks at `Annotated` types that contain `PropertyInfo` metadata. """ annotated_type = _get_annotated_type(type_) if annotated_type is None: @@ -142,6 +147,10 @@ def _maybe_transform_key(key: str, type_: type) -> str: return key +def _no_transform_needed(annotation: type) -> bool: + return annotation == float or annotation == int + + def _transform_recursive( data: object, *, @@ -160,6 +169,8 @@ def _transform_recursive( Defaults to the same value as the `annotation` argument. """ + from .._compat import model_dump + if inner_type is None: inner_type = annotation @@ -177,6 +188,8 @@ def _transform_recursive( (is_list_type(stripped_type) and is_list(data)) # Iterable[T] or (is_iterable_type(stripped_type) and is_iterable(data) and not isinstance(data, str)) + # Sequence[T] + or (is_sequence_type(stripped_type) and is_sequence(data) and not isinstance(data, str)) ): # dicts are technically iterable, but it is an iterable on the keys of the dict and is not usually # intended as an iterable, so we don't transform it. @@ -184,6 +197,15 @@ def _transform_recursive( return cast(object, data) inner_type = extract_type_arg(stripped_type, 0) + if _no_transform_needed(inner_type): + # for some types there is no need to transform anything, so we can get a small + # perf boost from skipping that work. + # + # but we still need to convert to a list to ensure the data is json-serializable + if is_list(data): + return data + return list(data) + return [_transform_recursive(d, annotation=annotation, inner_type=inner_type) for d in data] if is_union_type(stripped_type): @@ -245,6 +267,11 @@ def _transform_typeddict( result: dict[str, object] = {} annotations = get_type_hints(expected_type, include_extras=True) for key, value in data.items(): + if not is_given(value): + # we don't need to include omitted values here as they'll + # be stripped out before the request is sent anyway + continue + type_ = annotations.get(key) if type_ is None: # we do not have a type annotation for this field, leave it as is @@ -308,6 +335,8 @@ async def _async_transform_recursive( Defaults to the same value as the `annotation` argument. """ + from .._compat import model_dump + if inner_type is None: inner_type = annotation @@ -325,6 +354,8 @@ async def _async_transform_recursive( (is_list_type(stripped_type) and is_list(data)) # Iterable[T] or (is_iterable_type(stripped_type) and is_iterable(data) and not isinstance(data, str)) + # Sequence[T] + or (is_sequence_type(stripped_type) and is_sequence(data) and not isinstance(data, str)) ): # dicts are technically iterable, but it is an iterable on the keys of the dict and is not usually # intended as an iterable, so we don't transform it. @@ -332,6 +363,15 @@ async def _async_transform_recursive( return cast(object, data) inner_type = extract_type_arg(stripped_type, 0) + if _no_transform_needed(inner_type): + # for some types there is no need to transform anything, so we can get a small + # perf boost from skipping that work. + # + # but we still need to convert to a list to ensure the data is json-serializable + if is_list(data): + return data + return list(data) + return [await _async_transform_recursive(d, annotation=annotation, inner_type=inner_type) for d in data] if is_union_type(stripped_type): @@ -393,6 +433,11 @@ async def _async_transform_typeddict( result: dict[str, object] = {} annotations = get_type_hints(expected_type, include_extras=True) for key, value in data.items(): + if not is_given(value): + # we don't need to include omitted values here as they'll + # be stripped out before the request is sent anyway + continue + type_ = annotations.get(key) if type_ is None: # we do not have a type annotation for this field, leave it as is @@ -400,3 +445,13 @@ async def _async_transform_typeddict( else: result[_maybe_transform_key(key, type_)] = await _async_transform_recursive(value, annotation=type_) return result + + +@lru_cache(maxsize=8096) +def get_type_hints( + obj: Any, + globalns: dict[str, Any] | None = None, + localns: Mapping[str, Any] | None = None, + include_extras: bool = False, +) -> dict[str, Any]: + return _get_type_hints(obj, globalns=globalns, localns=localns, include_extras=include_extras) diff --git a/src/postgrid/_utils/_typing.py b/src/postgrid/_utils/_typing.py index 278749b..193109f 100644 --- a/src/postgrid/_utils/_typing.py +++ b/src/postgrid/_utils/_typing.py @@ -13,8 +13,9 @@ get_origin, ) +from ._utils import lru_cache from .._types import InheritsGeneric -from .._compat import is_union as _is_union +from ._compat import is_union as _is_union def is_annotated_type(typ: type) -> bool: @@ -25,6 +26,11 @@ def is_list_type(typ: type) -> bool: return (get_origin(typ) or typ) == list +def is_sequence_type(typ: type) -> bool: + origin = get_origin(typ) or typ + return origin == typing_extensions.Sequence or origin == typing.Sequence or origin == _c_abc.Sequence + + def is_iterable_type(typ: type) -> bool: """If the given type is `typing.Iterable[T]`""" origin = get_origin(typ) or typ @@ -66,6 +72,7 @@ def is_type_alias_type(tp: Any, /) -> TypeIs[typing_extensions.TypeAliasType]: # Extracts T from Annotated[T, ...] or from Required[Annotated[T, ...]] +@lru_cache(maxsize=8096) def strip_annotated_type(typ: type) -> type: if is_required_type(typ) or is_annotated_type(typ): return strip_annotated_type(cast(type, get_args(typ)[0])) @@ -108,7 +115,7 @@ class MyResponse(Foo[_T]): ``` """ cls = cast(object, get_origin(typ) or typ) - if cls in generic_bases: + if cls in generic_bases: # pyright: ignore[reportUnnecessaryContains] # we're given the class directly return extract_type_arg(typ, index) diff --git a/src/postgrid/_utils/_utils.py b/src/postgrid/_utils/_utils.py index e5811bb..eec7f4a 100644 --- a/src/postgrid/_utils/_utils.py +++ b/src/postgrid/_utils/_utils.py @@ -21,8 +21,7 @@ import sniffio -from .._types import NotGiven, FileTypes, NotGivenOr, HeadersLike -from .._compat import parse_date as parse_date, parse_datetime as parse_datetime +from .._types import Omit, NotGiven, FileTypes, HeadersLike _T = TypeVar("_T") _TupleT = TypeVar("_TupleT", bound=Tuple[object, ...]) @@ -64,7 +63,7 @@ def _extract_items( try: key = path[index] except IndexError: - if isinstance(obj, NotGiven): + if not is_given(obj): # no value was provided - we can safely ignore return [] @@ -72,8 +71,16 @@ def _extract_items( from .._files import assert_is_file_content # We have exhausted the path, return the entry we found. - assert_is_file_content(obj, key=flattened_key) assert flattened_key is not None + + if is_list(obj): + files: list[tuple[str, FileTypes]] = [] + for entry in obj: + assert_is_file_content(entry, key=flattened_key + "[]" if flattened_key else "") + files.append((flattened_key + "[]", cast(FileTypes, entry))) + return files + + assert_is_file_content(obj, key=flattened_key) return [(flattened_key, cast(FileTypes, obj))] index += 1 @@ -119,14 +126,14 @@ def _extract_items( return [] -def is_given(obj: NotGivenOr[_T]) -> TypeGuard[_T]: - return not isinstance(obj, NotGiven) +def is_given(obj: _T | NotGiven | Omit) -> TypeGuard[_T]: + return not isinstance(obj, NotGiven) and not isinstance(obj, Omit) # Type safe methods for narrowing types with TypeVars. # The default narrowing for isinstance(obj, dict) is dict[unknown, unknown], # however this cause Pyright to rightfully report errors. As we know we don't -# care about the contained types we can safely use `object` in it's place. +# care about the contained types we can safely use `object` in its place. # # There are two separate functions defined, `is_*` and `is_*_t` for different use cases. # `is_*` is for when you're dealing with an unknown input diff --git a/src/postgrid/resources/address_verification.py b/src/postgrid/resources/address_verification.py index ea4cabf..4aaa50c 100644 --- a/src/postgrid/resources/address_verification.py +++ b/src/postgrid/resources/address_verification.py @@ -7,12 +7,8 @@ import httpx from ..types import address_verification_verify_params -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._utils import ( - required_args, - maybe_transform, - async_maybe_transform, -) +from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from .._utils import required_args, maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import ( @@ -52,15 +48,15 @@ def verify( self, *, address: str, - geocode: bool | NotGiven = NOT_GIVEN, - include_details: bool | NotGiven = NOT_GIVEN, - proper_case: bool | NotGiven = NOT_GIVEN, + geocode: bool | Omit = omit, + include_details: bool | Omit = omit, + proper_case: bool | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AddressVerificationVerifyResponse: """1. @@ -93,15 +89,15 @@ def verify( self, *, address: address_verification_verify_params.StandardStructuredAddressInputAddress, - geocode: bool | NotGiven = NOT_GIVEN, - include_details: bool | NotGiven = NOT_GIVEN, - proper_case: bool | NotGiven = NOT_GIVEN, + geocode: bool | Omit = omit, + include_details: bool | Omit = omit, + proper_case: bool | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AddressVerificationVerifyResponse: """1. @@ -132,15 +128,15 @@ def verify( self, *, address: str | address_verification_verify_params.StandardStructuredAddressInputAddress, - geocode: bool | NotGiven = NOT_GIVEN, - include_details: bool | NotGiven = NOT_GIVEN, - proper_case: bool | NotGiven = NOT_GIVEN, + geocode: bool | Omit = omit, + include_details: bool | Omit = omit, + proper_case: bool | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AddressVerificationVerifyResponse: return self._post( "/v1/addver/verifications", @@ -190,15 +186,15 @@ async def verify( self, *, address: str, - geocode: bool | NotGiven = NOT_GIVEN, - include_details: bool | NotGiven = NOT_GIVEN, - proper_case: bool | NotGiven = NOT_GIVEN, + geocode: bool | Omit = omit, + include_details: bool | Omit = omit, + proper_case: bool | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AddressVerificationVerifyResponse: """1. @@ -231,15 +227,15 @@ async def verify( self, *, address: address_verification_verify_params.StandardStructuredAddressInputAddress, - geocode: bool | NotGiven = NOT_GIVEN, - include_details: bool | NotGiven = NOT_GIVEN, - proper_case: bool | NotGiven = NOT_GIVEN, + geocode: bool | Omit = omit, + include_details: bool | Omit = omit, + proper_case: bool | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AddressVerificationVerifyResponse: """1. @@ -270,15 +266,15 @@ async def verify( self, *, address: str | address_verification_verify_params.StandardStructuredAddressInputAddress, - geocode: bool | NotGiven = NOT_GIVEN, - include_details: bool | NotGiven = NOT_GIVEN, - proper_case: bool | NotGiven = NOT_GIVEN, + geocode: bool | Omit = omit, + include_details: bool | Omit = omit, + proper_case: bool | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AddressVerificationVerifyResponse: return await self._post( "/v1/addver/verifications", diff --git a/src/postgrid/resources/intl_address_verification.py b/src/postgrid/resources/intl_address_verification.py index c4ffe63..1097126 100644 --- a/src/postgrid/resources/intl_address_verification.py +++ b/src/postgrid/resources/intl_address_verification.py @@ -7,12 +7,8 @@ import httpx from ..types import intl_address_verification_verify_params -from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from .._utils import ( - required_args, - maybe_transform, - async_maybe_transform, -) +from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from .._utils import required_args, maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import ( @@ -52,15 +48,15 @@ def verify( self, *, address: intl_address_verification_verify_params.StructuredAddressInputAddress, - geo_data: bool | NotGiven = NOT_GIVEN, - include_details: bool | NotGiven = NOT_GIVEN, - proper_case: bool | NotGiven = NOT_GIVEN, + geo_data: bool | Omit = omit, + include_details: bool | Omit = omit, + proper_case: bool | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> IntlAddressVerificationVerifyResponse: """ Verify and standardize an international address. @@ -86,15 +82,15 @@ def verify( self, *, address: str, - geo_data: bool | NotGiven = NOT_GIVEN, - include_details: bool | NotGiven = NOT_GIVEN, - proper_case: bool | NotGiven = NOT_GIVEN, + geo_data: bool | Omit = omit, + include_details: bool | Omit = omit, + proper_case: bool | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> IntlAddressVerificationVerifyResponse: """ Verify and standardize an international address. @@ -122,15 +118,15 @@ def verify( self, *, address: intl_address_verification_verify_params.StructuredAddressInputAddress | str, - geo_data: bool | NotGiven = NOT_GIVEN, - include_details: bool | NotGiven = NOT_GIVEN, - proper_case: bool | NotGiven = NOT_GIVEN, + geo_data: bool | Omit = omit, + include_details: bool | Omit = omit, + proper_case: bool | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> IntlAddressVerificationVerifyResponse: return self._post( "/v1/intl_addver/verifications", @@ -180,15 +176,15 @@ async def verify( self, *, address: intl_address_verification_verify_params.StructuredAddressInputAddress, - geo_data: bool | NotGiven = NOT_GIVEN, - include_details: bool | NotGiven = NOT_GIVEN, - proper_case: bool | NotGiven = NOT_GIVEN, + geo_data: bool | Omit = omit, + include_details: bool | Omit = omit, + proper_case: bool | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> IntlAddressVerificationVerifyResponse: """ Verify and standardize an international address. @@ -214,15 +210,15 @@ async def verify( self, *, address: str, - geo_data: bool | NotGiven = NOT_GIVEN, - include_details: bool | NotGiven = NOT_GIVEN, - proper_case: bool | NotGiven = NOT_GIVEN, + geo_data: bool | Omit = omit, + include_details: bool | Omit = omit, + proper_case: bool | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> IntlAddressVerificationVerifyResponse: """ Verify and standardize an international address. @@ -250,15 +246,15 @@ async def verify( self, *, address: intl_address_verification_verify_params.StructuredAddressInputAddress | str, - geo_data: bool | NotGiven = NOT_GIVEN, - include_details: bool | NotGiven = NOT_GIVEN, - proper_case: bool | NotGiven = NOT_GIVEN, + geo_data: bool | Omit = omit, + include_details: bool | Omit = omit, + proper_case: bool | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> IntlAddressVerificationVerifyResponse: return await self._post( "/v1/intl_addver/verifications", diff --git a/src/postgrid/resources/print_mail/bank_accounts.py b/src/postgrid/resources/print_mail/bank_accounts.py index 2ca8c66..fe702cf 100644 --- a/src/postgrid/resources/print_mail/bank_accounts.py +++ b/src/postgrid/resources/print_mail/bank_accounts.py @@ -7,19 +7,8 @@ import httpx -from ..._types import ( - NOT_GIVEN, - Body, - Query, - Headers, - NotGiven, - Base64FileInput, -) -from ..._utils import ( - required_args, - maybe_transform, - async_maybe_transform, -) +from ..._types import Body, Omit, Query, Headers, NotGiven, Base64FileInput, omit, not_given +from ..._utils import required_args, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -66,20 +55,20 @@ def create( bank_country_code: BankAccountCountryCode, bank_name: str, signature_text: str, - bank_primary_line: str | NotGiven = NOT_GIVEN, - bank_secondary_line: str | NotGiven = NOT_GIVEN, - ca_designation_number: str | NotGiven = NOT_GIVEN, - description: str | NotGiven = NOT_GIVEN, - metadata: Dict[str, object] | NotGiven = NOT_GIVEN, - route_number: str | NotGiven = NOT_GIVEN, - routing_number: str | NotGiven = NOT_GIVEN, - transit_number: str | NotGiven = NOT_GIVEN, + bank_primary_line: str | Omit = omit, + bank_secondary_line: str | Omit = omit, + ca_designation_number: str | Omit = omit, + description: str | Omit = omit, + metadata: Dict[str, object] | Omit = omit, + route_number: str | Omit = omit, + routing_number: str | Omit = omit, + transit_number: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BankAccount: """Create a bank account. @@ -136,20 +125,20 @@ def create( bank_country_code: BankAccountCountryCode, bank_name: str, signature_image: str, - bank_primary_line: str | NotGiven = NOT_GIVEN, - bank_secondary_line: str | NotGiven = NOT_GIVEN, - ca_designation_number: str | NotGiven = NOT_GIVEN, - description: str | NotGiven = NOT_GIVEN, - metadata: Dict[str, object] | NotGiven = NOT_GIVEN, - route_number: str | NotGiven = NOT_GIVEN, - routing_number: str | NotGiven = NOT_GIVEN, - transit_number: str | NotGiven = NOT_GIVEN, + bank_primary_line: str | Omit = omit, + bank_secondary_line: str | Omit = omit, + ca_designation_number: str | Omit = omit, + description: str | Omit = omit, + metadata: Dict[str, object] | Omit = omit, + route_number: str | Omit = omit, + routing_number: str | Omit = omit, + transit_number: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BankAccount: """Create a bank account. @@ -207,20 +196,20 @@ def create( bank_country_code: BankAccountCountryCode, bank_name: str, signature_image: Union[str, Base64FileInput], - bank_primary_line: str | NotGiven = NOT_GIVEN, - bank_secondary_line: str | NotGiven = NOT_GIVEN, - ca_designation_number: str | NotGiven = NOT_GIVEN, - description: str | NotGiven = NOT_GIVEN, - metadata: Dict[str, object] | NotGiven = NOT_GIVEN, - route_number: str | NotGiven = NOT_GIVEN, - routing_number: str | NotGiven = NOT_GIVEN, - transit_number: str | NotGiven = NOT_GIVEN, + bank_primary_line: str | Omit = omit, + bank_secondary_line: str | Omit = omit, + ca_designation_number: str | Omit = omit, + description: str | Omit = omit, + metadata: Dict[str, object] | Omit = omit, + route_number: str | Omit = omit, + routing_number: str | Omit = omit, + transit_number: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BankAccount: """Create a bank account. @@ -280,22 +269,22 @@ def create( account_number: str, bank_country_code: BankAccountCountryCode, bank_name: str, - signature_text: str | NotGiven = NOT_GIVEN, - bank_primary_line: str | NotGiven = NOT_GIVEN, - bank_secondary_line: str | NotGiven = NOT_GIVEN, - ca_designation_number: str | NotGiven = NOT_GIVEN, - description: str | NotGiven = NOT_GIVEN, - metadata: Dict[str, object] | NotGiven = NOT_GIVEN, - route_number: str | NotGiven = NOT_GIVEN, - routing_number: str | NotGiven = NOT_GIVEN, - transit_number: str | NotGiven = NOT_GIVEN, - signature_image: str | NotGiven = NOT_GIVEN, + signature_text: str | Omit = omit, + bank_primary_line: str | Omit = omit, + bank_secondary_line: str | Omit = omit, + ca_designation_number: str | Omit = omit, + description: str | Omit = omit, + metadata: Dict[str, object] | Omit = omit, + route_number: str | Omit = omit, + routing_number: str | Omit = omit, + transit_number: str | Omit = omit, + signature_image: str | Union[str, Base64FileInput] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BankAccount: return self._post( "/print-mail/v1/bank_accounts", @@ -332,7 +321,7 @@ def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BankAccount: """ Retrieve a bank account by ID. @@ -359,15 +348,15 @@ def retrieve( def list( self, *, - limit: int | NotGiven = NOT_GIVEN, - search: str | NotGiven = NOT_GIVEN, - skip: int | NotGiven = NOT_GIVEN, + limit: int | Omit = omit, + search: str | Omit = omit, + skip: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncSkipLimit[BankAccount]: """ Get a list of bank accounts. @@ -416,7 +405,7 @@ def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BankAccountDeleteResponse: """Delete a bank account by ID. @@ -470,20 +459,20 @@ async def create( bank_country_code: BankAccountCountryCode, bank_name: str, signature_text: str, - bank_primary_line: str | NotGiven = NOT_GIVEN, - bank_secondary_line: str | NotGiven = NOT_GIVEN, - ca_designation_number: str | NotGiven = NOT_GIVEN, - description: str | NotGiven = NOT_GIVEN, - metadata: Dict[str, object] | NotGiven = NOT_GIVEN, - route_number: str | NotGiven = NOT_GIVEN, - routing_number: str | NotGiven = NOT_GIVEN, - transit_number: str | NotGiven = NOT_GIVEN, + bank_primary_line: str | Omit = omit, + bank_secondary_line: str | Omit = omit, + ca_designation_number: str | Omit = omit, + description: str | Omit = omit, + metadata: Dict[str, object] | Omit = omit, + route_number: str | Omit = omit, + routing_number: str | Omit = omit, + transit_number: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BankAccount: """Create a bank account. @@ -540,20 +529,20 @@ async def create( bank_country_code: BankAccountCountryCode, bank_name: str, signature_image: str, - bank_primary_line: str | NotGiven = NOT_GIVEN, - bank_secondary_line: str | NotGiven = NOT_GIVEN, - ca_designation_number: str | NotGiven = NOT_GIVEN, - description: str | NotGiven = NOT_GIVEN, - metadata: Dict[str, object] | NotGiven = NOT_GIVEN, - route_number: str | NotGiven = NOT_GIVEN, - routing_number: str | NotGiven = NOT_GIVEN, - transit_number: str | NotGiven = NOT_GIVEN, + bank_primary_line: str | Omit = omit, + bank_secondary_line: str | Omit = omit, + ca_designation_number: str | Omit = omit, + description: str | Omit = omit, + metadata: Dict[str, object] | Omit = omit, + route_number: str | Omit = omit, + routing_number: str | Omit = omit, + transit_number: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BankAccount: """Create a bank account. @@ -611,20 +600,20 @@ async def create( bank_country_code: BankAccountCountryCode, bank_name: str, signature_image: Union[str, Base64FileInput], - bank_primary_line: str | NotGiven = NOT_GIVEN, - bank_secondary_line: str | NotGiven = NOT_GIVEN, - ca_designation_number: str | NotGiven = NOT_GIVEN, - description: str | NotGiven = NOT_GIVEN, - metadata: Dict[str, object] | NotGiven = NOT_GIVEN, - route_number: str | NotGiven = NOT_GIVEN, - routing_number: str | NotGiven = NOT_GIVEN, - transit_number: str | NotGiven = NOT_GIVEN, + bank_primary_line: str | Omit = omit, + bank_secondary_line: str | Omit = omit, + ca_designation_number: str | Omit = omit, + description: str | Omit = omit, + metadata: Dict[str, object] | Omit = omit, + route_number: str | Omit = omit, + routing_number: str | Omit = omit, + transit_number: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BankAccount: """Create a bank account. @@ -684,22 +673,22 @@ async def create( account_number: str, bank_country_code: BankAccountCountryCode, bank_name: str, - signature_text: str | NotGiven = NOT_GIVEN, - bank_primary_line: str | NotGiven = NOT_GIVEN, - bank_secondary_line: str | NotGiven = NOT_GIVEN, - ca_designation_number: str | NotGiven = NOT_GIVEN, - description: str | NotGiven = NOT_GIVEN, - metadata: Dict[str, object] | NotGiven = NOT_GIVEN, - route_number: str | NotGiven = NOT_GIVEN, - routing_number: str | NotGiven = NOT_GIVEN, - transit_number: str | NotGiven = NOT_GIVEN, - signature_image: str | NotGiven = NOT_GIVEN, + signature_text: str | Omit = omit, + bank_primary_line: str | Omit = omit, + bank_secondary_line: str | Omit = omit, + ca_designation_number: str | Omit = omit, + description: str | Omit = omit, + metadata: Dict[str, object] | Omit = omit, + route_number: str | Omit = omit, + routing_number: str | Omit = omit, + transit_number: str | Omit = omit, + signature_image: str | Union[str, Base64FileInput] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BankAccount: return await self._post( "/print-mail/v1/bank_accounts", @@ -736,7 +725,7 @@ async def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BankAccount: """ Retrieve a bank account by ID. @@ -763,15 +752,15 @@ async def retrieve( def list( self, *, - limit: int | NotGiven = NOT_GIVEN, - search: str | NotGiven = NOT_GIVEN, - skip: int | NotGiven = NOT_GIVEN, + limit: int | Omit = omit, + search: str | Omit = omit, + skip: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[BankAccount, AsyncSkipLimit[BankAccount]]: """ Get a list of bank accounts. @@ -820,7 +809,7 @@ async def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BankAccountDeleteResponse: """Delete a bank account by ID. diff --git a/src/postgrid/resources/print_mail/boxes.py b/src/postgrid/resources/print_mail/boxes.py index 1ed5234..2f9e190 100644 --- a/src/postgrid/resources/print_mail/boxes.py +++ b/src/postgrid/resources/print_mail/boxes.py @@ -7,11 +7,8 @@ import httpx -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ..._utils import ( - maybe_transform, - async_maybe_transform, -) +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -55,17 +52,17 @@ def create( cheques: Iterable[box_create_params.Cheque], from_: box_create_params.From, to: box_create_params.To, - description: str | NotGiven = NOT_GIVEN, - mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, - merge_variables: Dict[str, object] | NotGiven = NOT_GIVEN, - metadata: Dict[str, object] | NotGiven = NOT_GIVEN, - send_date: Union[str, datetime] | NotGiven = NOT_GIVEN, + description: str | Omit = omit, + mailing_class: OrderMailingClass | Omit = omit, + merge_variables: Dict[str, object] | Omit = omit, + metadata: Dict[str, object] | Omit = omit, + send_date: Union[str, datetime] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Box: """This endpoint allows you to create a box containing up to 100 cheques. @@ -152,7 +149,7 @@ def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Box: """ Retrieve a box by ID. @@ -179,15 +176,15 @@ def retrieve( def list( self, *, - limit: int | NotGiven = NOT_GIVEN, - search: str | NotGiven = NOT_GIVEN, - skip: int | NotGiven = NOT_GIVEN, + limit: int | Omit = omit, + search: str | Omit = omit, + skip: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncSkipLimit[Box]: """ List all boxes. @@ -236,7 +233,7 @@ def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Box: """ Cancel a box by ID (cannot be undone). @@ -287,17 +284,17 @@ async def create( cheques: Iterable[box_create_params.Cheque], from_: box_create_params.From, to: box_create_params.To, - description: str | NotGiven = NOT_GIVEN, - mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, - merge_variables: Dict[str, object] | NotGiven = NOT_GIVEN, - metadata: Dict[str, object] | NotGiven = NOT_GIVEN, - send_date: Union[str, datetime] | NotGiven = NOT_GIVEN, + description: str | Omit = omit, + mailing_class: OrderMailingClass | Omit = omit, + merge_variables: Dict[str, object] | Omit = omit, + metadata: Dict[str, object] | Omit = omit, + send_date: Union[str, datetime] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Box: """This endpoint allows you to create a box containing up to 100 cheques. @@ -384,7 +381,7 @@ async def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Box: """ Retrieve a box by ID. @@ -411,15 +408,15 @@ async def retrieve( def list( self, *, - limit: int | NotGiven = NOT_GIVEN, - search: str | NotGiven = NOT_GIVEN, - skip: int | NotGiven = NOT_GIVEN, + limit: int | Omit = omit, + search: str | Omit = omit, + skip: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[Box, AsyncSkipLimit[Box]]: """ List all boxes. @@ -468,7 +465,7 @@ async def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Box: """ Cancel a box by ID (cannot be undone). diff --git a/src/postgrid/resources/print_mail/campaigns.py b/src/postgrid/resources/print_mail/campaigns.py index 81bc744..394e3b5 100644 --- a/src/postgrid/resources/print_mail/campaigns.py +++ b/src/postgrid/resources/print_mail/campaigns.py @@ -7,12 +7,8 @@ import httpx -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ..._utils import ( - maybe_transform, - strip_not_given, - async_maybe_transform, -) +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import maybe_transform, strip_not_given, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -59,21 +55,21 @@ def create( self, *, mailing_list: str, - cheque_profile: str | NotGiven = NOT_GIVEN, - default_sender_contact: str | NotGiven = NOT_GIVEN, - description: str | NotGiven = NOT_GIVEN, - letter_profile: str | NotGiven = NOT_GIVEN, - metadata: Dict[str, object] | NotGiven = NOT_GIVEN, - postcard_profile: str | NotGiven = NOT_GIVEN, - self_mailer_profile: str | NotGiven = NOT_GIVEN, - send_date: Union[str, datetime] | NotGiven = NOT_GIVEN, - idempotency_key: str | NotGiven = NOT_GIVEN, + cheque_profile: str | Omit = omit, + default_sender_contact: str | Omit = omit, + description: str | Omit = omit, + letter_profile: str | Omit = omit, + metadata: Dict[str, object] | Omit = omit, + postcard_profile: str | Omit = omit, + self_mailer_profile: str | Omit = omit, + send_date: Union[str, datetime] | Omit = omit, + idempotency_key: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Campaign: """ Create a new campaign. @@ -143,7 +139,7 @@ def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Campaign: """ Retrieve a specific campaign by its ID. @@ -171,20 +167,20 @@ def update( self, id: str, *, - cheque_profile: Optional[str] | NotGiven = NOT_GIVEN, - default_sender_contact: Optional[str] | NotGiven = NOT_GIVEN, - description: Optional[str] | NotGiven = NOT_GIVEN, - letter_profile: Optional[str] | NotGiven = NOT_GIVEN, - mailing_list: str | NotGiven = NOT_GIVEN, - metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, - postcard_profile: Optional[str] | NotGiven = NOT_GIVEN, - self_mailer_profile: Optional[str] | NotGiven = NOT_GIVEN, + cheque_profile: Optional[str] | Omit = omit, + default_sender_contact: Optional[str] | Omit = omit, + description: Optional[str] | Omit = omit, + letter_profile: Optional[str] | Omit = omit, + mailing_list: str | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + postcard_profile: Optional[str] | Omit = omit, + self_mailer_profile: Optional[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Campaign: """ Update an existing campaign. @@ -250,15 +246,15 @@ def update( def list( self, *, - limit: int | NotGiven = NOT_GIVEN, - search: str | NotGiven = NOT_GIVEN, - skip: int | NotGiven = NOT_GIVEN, + limit: int | Omit = omit, + search: str | Omit = omit, + skip: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncSkipLimit[Campaign]: """ Retrieve a list of campaigns. @@ -310,7 +306,7 @@ def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> CampaignDeleteResponse: """ Delete a campaign. @@ -342,13 +338,13 @@ def send( self, id: str, *, - send_date: Union[Union[str, datetime], str] | NotGiven = NOT_GIVEN, + send_date: Union[Union[str, datetime], str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Campaign: """ Send a campaign for processing. @@ -405,21 +401,21 @@ async def create( self, *, mailing_list: str, - cheque_profile: str | NotGiven = NOT_GIVEN, - default_sender_contact: str | NotGiven = NOT_GIVEN, - description: str | NotGiven = NOT_GIVEN, - letter_profile: str | NotGiven = NOT_GIVEN, - metadata: Dict[str, object] | NotGiven = NOT_GIVEN, - postcard_profile: str | NotGiven = NOT_GIVEN, - self_mailer_profile: str | NotGiven = NOT_GIVEN, - send_date: Union[str, datetime] | NotGiven = NOT_GIVEN, - idempotency_key: str | NotGiven = NOT_GIVEN, + cheque_profile: str | Omit = omit, + default_sender_contact: str | Omit = omit, + description: str | Omit = omit, + letter_profile: str | Omit = omit, + metadata: Dict[str, object] | Omit = omit, + postcard_profile: str | Omit = omit, + self_mailer_profile: str | Omit = omit, + send_date: Union[str, datetime] | Omit = omit, + idempotency_key: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Campaign: """ Create a new campaign. @@ -489,7 +485,7 @@ async def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Campaign: """ Retrieve a specific campaign by its ID. @@ -517,20 +513,20 @@ async def update( self, id: str, *, - cheque_profile: Optional[str] | NotGiven = NOT_GIVEN, - default_sender_contact: Optional[str] | NotGiven = NOT_GIVEN, - description: Optional[str] | NotGiven = NOT_GIVEN, - letter_profile: Optional[str] | NotGiven = NOT_GIVEN, - mailing_list: str | NotGiven = NOT_GIVEN, - metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, - postcard_profile: Optional[str] | NotGiven = NOT_GIVEN, - self_mailer_profile: Optional[str] | NotGiven = NOT_GIVEN, + cheque_profile: Optional[str] | Omit = omit, + default_sender_contact: Optional[str] | Omit = omit, + description: Optional[str] | Omit = omit, + letter_profile: Optional[str] | Omit = omit, + mailing_list: str | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + postcard_profile: Optional[str] | Omit = omit, + self_mailer_profile: Optional[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Campaign: """ Update an existing campaign. @@ -596,15 +592,15 @@ async def update( def list( self, *, - limit: int | NotGiven = NOT_GIVEN, - search: str | NotGiven = NOT_GIVEN, - skip: int | NotGiven = NOT_GIVEN, + limit: int | Omit = omit, + search: str | Omit = omit, + skip: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[Campaign, AsyncSkipLimit[Campaign]]: """ Retrieve a list of campaigns. @@ -656,7 +652,7 @@ async def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> CampaignDeleteResponse: """ Delete a campaign. @@ -688,13 +684,13 @@ async def send( self, id: str, *, - send_date: Union[Union[str, datetime], str] | NotGiven = NOT_GIVEN, + send_date: Union[Union[str, datetime], str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Campaign: """ Send a campaign for processing. diff --git a/src/postgrid/resources/print_mail/cheques.py b/src/postgrid/resources/print_mail/cheques.py index a090568..44d389e 100644 --- a/src/postgrid/resources/print_mail/cheques.py +++ b/src/postgrid/resources/print_mail/cheques.py @@ -8,11 +8,8 @@ import httpx -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ..._utils import ( - maybe_transform, - async_maybe_transform, -) +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -60,26 +57,26 @@ def create( bank_account: str, from_: cheque_create_params.From, to: cheque_create_params.To, - currency_code: Literal["USD", "CAD"] | NotGiven = NOT_GIVEN, - description: str | NotGiven = NOT_GIVEN, - digital_only: DigitalOnlyParam | NotGiven = NOT_GIVEN, - envelope: Union[Literal["standard"], str] | NotGiven = NOT_GIVEN, - logo_url: str | NotGiven = NOT_GIVEN, - mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, - memo: str | NotGiven = NOT_GIVEN, - merge_variables: Dict[str, object] | NotGiven = NOT_GIVEN, - message: str | NotGiven = NOT_GIVEN, - metadata: Dict[str, object] | NotGiven = NOT_GIVEN, - number: int | NotGiven = NOT_GIVEN, - redirect_to: cheque_create_params.RedirectTo | NotGiven = NOT_GIVEN, - send_date: Union[str, datetime] | NotGiven = NOT_GIVEN, - size: ChequeSize | NotGiven = NOT_GIVEN, + currency_code: Literal["USD", "CAD"] | Omit = omit, + description: str | Omit = omit, + digital_only: DigitalOnlyParam | Omit = omit, + envelope: Union[Literal["standard"], str] | Omit = omit, + logo_url: str | Omit = omit, + mailing_class: OrderMailingClass | Omit = omit, + memo: str | Omit = omit, + merge_variables: Dict[str, object] | Omit = omit, + message: str | Omit = omit, + metadata: Dict[str, object] | Omit = omit, + number: int | Omit = omit, + redirect_to: cheque_create_params.RedirectTo | Omit = omit, + send_date: Union[str, datetime] | Omit = omit, + size: ChequeSize | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Cheque: """ Create a cheque. @@ -214,7 +211,7 @@ def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Cheque: """ Retrieve a cheque by ID. @@ -241,15 +238,15 @@ def retrieve( def list( self, *, - limit: int | NotGiven = NOT_GIVEN, - search: str | NotGiven = NOT_GIVEN, - skip: int | NotGiven = NOT_GIVEN, + limit: int | Omit = omit, + search: str | Omit = omit, + skip: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncSkipLimit[Cheque]: """ Get a list of cheques. @@ -298,7 +295,7 @@ def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Cheque: """Cancel a cheque by ID. @@ -332,7 +329,7 @@ def retrieve_url( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ChequeRetrieveURLResponse: """ Retrieve a cheque preview URL. @@ -370,7 +367,7 @@ def retrieve_with_deposit_ready_pdf( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Cheque: """Retrieve the deposit-ready PDF for a digital-only cheque. @@ -426,26 +423,26 @@ async def create( bank_account: str, from_: cheque_create_params.From, to: cheque_create_params.To, - currency_code: Literal["USD", "CAD"] | NotGiven = NOT_GIVEN, - description: str | NotGiven = NOT_GIVEN, - digital_only: DigitalOnlyParam | NotGiven = NOT_GIVEN, - envelope: Union[Literal["standard"], str] | NotGiven = NOT_GIVEN, - logo_url: str | NotGiven = NOT_GIVEN, - mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, - memo: str | NotGiven = NOT_GIVEN, - merge_variables: Dict[str, object] | NotGiven = NOT_GIVEN, - message: str | NotGiven = NOT_GIVEN, - metadata: Dict[str, object] | NotGiven = NOT_GIVEN, - number: int | NotGiven = NOT_GIVEN, - redirect_to: cheque_create_params.RedirectTo | NotGiven = NOT_GIVEN, - send_date: Union[str, datetime] | NotGiven = NOT_GIVEN, - size: ChequeSize | NotGiven = NOT_GIVEN, + currency_code: Literal["USD", "CAD"] | Omit = omit, + description: str | Omit = omit, + digital_only: DigitalOnlyParam | Omit = omit, + envelope: Union[Literal["standard"], str] | Omit = omit, + logo_url: str | Omit = omit, + mailing_class: OrderMailingClass | Omit = omit, + memo: str | Omit = omit, + merge_variables: Dict[str, object] | Omit = omit, + message: str | Omit = omit, + metadata: Dict[str, object] | Omit = omit, + number: int | Omit = omit, + redirect_to: cheque_create_params.RedirectTo | Omit = omit, + send_date: Union[str, datetime] | Omit = omit, + size: ChequeSize | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Cheque: """ Create a cheque. @@ -580,7 +577,7 @@ async def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Cheque: """ Retrieve a cheque by ID. @@ -607,15 +604,15 @@ async def retrieve( def list( self, *, - limit: int | NotGiven = NOT_GIVEN, - search: str | NotGiven = NOT_GIVEN, - skip: int | NotGiven = NOT_GIVEN, + limit: int | Omit = omit, + search: str | Omit = omit, + skip: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[Cheque, AsyncSkipLimit[Cheque]]: """ Get a list of cheques. @@ -664,7 +661,7 @@ async def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Cheque: """Cancel a cheque by ID. @@ -698,7 +695,7 @@ async def retrieve_url( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ChequeRetrieveURLResponse: """ Retrieve a cheque preview URL. @@ -736,7 +733,7 @@ async def retrieve_with_deposit_ready_pdf( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Cheque: """Retrieve the deposit-ready PDF for a digital-only cheque. diff --git a/src/postgrid/resources/print_mail/contacts.py b/src/postgrid/resources/print_mail/contacts.py index ed02081..5442486 100644 --- a/src/postgrid/resources/print_mail/contacts.py +++ b/src/postgrid/resources/print_mail/contacts.py @@ -7,12 +7,8 @@ import httpx -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ..._utils import ( - required_args, - maybe_transform, - async_maybe_transform, -) +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import required_args, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -57,25 +53,25 @@ def create( address_line1: str, country_code: str, first_name: str, - address_line2: str | NotGiven = NOT_GIVEN, - city: str | NotGiven = NOT_GIVEN, - company_name: str | NotGiven = NOT_GIVEN, - description: str | NotGiven = NOT_GIVEN, - email: str | NotGiven = NOT_GIVEN, - force_verified_status: bool | NotGiven = NOT_GIVEN, - job_title: str | NotGiven = NOT_GIVEN, - last_name: str | NotGiven = NOT_GIVEN, - metadata: Dict[str, object] | NotGiven = NOT_GIVEN, - phone_number: str | NotGiven = NOT_GIVEN, - postal_or_zip: str | NotGiven = NOT_GIVEN, - province_or_state: str | NotGiven = NOT_GIVEN, - skip_verification: bool | NotGiven = NOT_GIVEN, + address_line2: str | Omit = omit, + city: str | Omit = omit, + company_name: str | Omit = omit, + description: str | Omit = omit, + email: str | Omit = omit, + force_verified_status: bool | Omit = omit, + job_title: str | Omit = omit, + last_name: str | Omit = omit, + metadata: Dict[str, object] | Omit = omit, + phone_number: str | Omit = omit, + postal_or_zip: str | Omit = omit, + province_or_state: str | Omit = omit, + skip_verification: bool | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Contact: """Creates a contact. @@ -144,25 +140,25 @@ def create( address_line1: str, company_name: str, country_code: str, - address_line2: str | NotGiven = NOT_GIVEN, - city: str | NotGiven = NOT_GIVEN, - description: str | NotGiven = NOT_GIVEN, - email: str | NotGiven = NOT_GIVEN, - first_name: str | NotGiven = NOT_GIVEN, - force_verified_status: bool | NotGiven = NOT_GIVEN, - job_title: str | NotGiven = NOT_GIVEN, - last_name: str | NotGiven = NOT_GIVEN, - metadata: Dict[str, object] | NotGiven = NOT_GIVEN, - phone_number: str | NotGiven = NOT_GIVEN, - postal_or_zip: str | NotGiven = NOT_GIVEN, - province_or_state: str | NotGiven = NOT_GIVEN, - skip_verification: bool | NotGiven = NOT_GIVEN, + address_line2: str | Omit = omit, + city: str | Omit = omit, + description: str | Omit = omit, + email: str | Omit = omit, + first_name: str | Omit = omit, + force_verified_status: bool | Omit = omit, + job_title: str | Omit = omit, + last_name: str | Omit = omit, + metadata: Dict[str, object] | Omit = omit, + phone_number: str | Omit = omit, + postal_or_zip: str | Omit = omit, + province_or_state: str | Omit = omit, + skip_verification: bool | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Contact: """Creates a contact. @@ -230,26 +226,26 @@ def create( *, address_line1: str, country_code: str, - first_name: str | NotGiven = NOT_GIVEN, - address_line2: str | NotGiven = NOT_GIVEN, - city: str | NotGiven = NOT_GIVEN, - company_name: str | NotGiven = NOT_GIVEN, - description: str | NotGiven = NOT_GIVEN, - email: str | NotGiven = NOT_GIVEN, - force_verified_status: bool | NotGiven = NOT_GIVEN, - job_title: str | NotGiven = NOT_GIVEN, - last_name: str | NotGiven = NOT_GIVEN, - metadata: Dict[str, object] | NotGiven = NOT_GIVEN, - phone_number: str | NotGiven = NOT_GIVEN, - postal_or_zip: str | NotGiven = NOT_GIVEN, - province_or_state: str | NotGiven = NOT_GIVEN, - skip_verification: bool | NotGiven = NOT_GIVEN, + first_name: str | Omit = omit, + address_line2: str | Omit = omit, + city: str | Omit = omit, + company_name: str | Omit = omit, + description: str | Omit = omit, + email: str | Omit = omit, + force_verified_status: bool | Omit = omit, + job_title: str | Omit = omit, + last_name: str | Omit = omit, + metadata: Dict[str, object] | Omit = omit, + phone_number: str | Omit = omit, + postal_or_zip: str | Omit = omit, + province_or_state: str | Omit = omit, + skip_verification: bool | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Contact: return self._post( "/print-mail/v1/contacts", @@ -289,7 +285,7 @@ def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Contact: """ Retrieve a contact. @@ -316,15 +312,15 @@ def retrieve( def list( self, *, - limit: int | NotGiven = NOT_GIVEN, - search: str | NotGiven = NOT_GIVEN, - skip: int | NotGiven = NOT_GIVEN, + limit: int | Omit = omit, + search: str | Omit = omit, + skip: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncSkipLimit[Contact]: """ Get a list of contacts. @@ -373,7 +369,7 @@ def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ContactDeleteResponse: """Delete a contact. @@ -427,25 +423,25 @@ async def create( address_line1: str, country_code: str, first_name: str, - address_line2: str | NotGiven = NOT_GIVEN, - city: str | NotGiven = NOT_GIVEN, - company_name: str | NotGiven = NOT_GIVEN, - description: str | NotGiven = NOT_GIVEN, - email: str | NotGiven = NOT_GIVEN, - force_verified_status: bool | NotGiven = NOT_GIVEN, - job_title: str | NotGiven = NOT_GIVEN, - last_name: str | NotGiven = NOT_GIVEN, - metadata: Dict[str, object] | NotGiven = NOT_GIVEN, - phone_number: str | NotGiven = NOT_GIVEN, - postal_or_zip: str | NotGiven = NOT_GIVEN, - province_or_state: str | NotGiven = NOT_GIVEN, - skip_verification: bool | NotGiven = NOT_GIVEN, + address_line2: str | Omit = omit, + city: str | Omit = omit, + company_name: str | Omit = omit, + description: str | Omit = omit, + email: str | Omit = omit, + force_verified_status: bool | Omit = omit, + job_title: str | Omit = omit, + last_name: str | Omit = omit, + metadata: Dict[str, object] | Omit = omit, + phone_number: str | Omit = omit, + postal_or_zip: str | Omit = omit, + province_or_state: str | Omit = omit, + skip_verification: bool | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Contact: """Creates a contact. @@ -514,25 +510,25 @@ async def create( address_line1: str, company_name: str, country_code: str, - address_line2: str | NotGiven = NOT_GIVEN, - city: str | NotGiven = NOT_GIVEN, - description: str | NotGiven = NOT_GIVEN, - email: str | NotGiven = NOT_GIVEN, - first_name: str | NotGiven = NOT_GIVEN, - force_verified_status: bool | NotGiven = NOT_GIVEN, - job_title: str | NotGiven = NOT_GIVEN, - last_name: str | NotGiven = NOT_GIVEN, - metadata: Dict[str, object] | NotGiven = NOT_GIVEN, - phone_number: str | NotGiven = NOT_GIVEN, - postal_or_zip: str | NotGiven = NOT_GIVEN, - province_or_state: str | NotGiven = NOT_GIVEN, - skip_verification: bool | NotGiven = NOT_GIVEN, + address_line2: str | Omit = omit, + city: str | Omit = omit, + description: str | Omit = omit, + email: str | Omit = omit, + first_name: str | Omit = omit, + force_verified_status: bool | Omit = omit, + job_title: str | Omit = omit, + last_name: str | Omit = omit, + metadata: Dict[str, object] | Omit = omit, + phone_number: str | Omit = omit, + postal_or_zip: str | Omit = omit, + province_or_state: str | Omit = omit, + skip_verification: bool | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Contact: """Creates a contact. @@ -600,26 +596,26 @@ async def create( *, address_line1: str, country_code: str, - first_name: str | NotGiven = NOT_GIVEN, - address_line2: str | NotGiven = NOT_GIVEN, - city: str | NotGiven = NOT_GIVEN, - company_name: str | NotGiven = NOT_GIVEN, - description: str | NotGiven = NOT_GIVEN, - email: str | NotGiven = NOT_GIVEN, - force_verified_status: bool | NotGiven = NOT_GIVEN, - job_title: str | NotGiven = NOT_GIVEN, - last_name: str | NotGiven = NOT_GIVEN, - metadata: Dict[str, object] | NotGiven = NOT_GIVEN, - phone_number: str | NotGiven = NOT_GIVEN, - postal_or_zip: str | NotGiven = NOT_GIVEN, - province_or_state: str | NotGiven = NOT_GIVEN, - skip_verification: bool | NotGiven = NOT_GIVEN, + first_name: str | Omit = omit, + address_line2: str | Omit = omit, + city: str | Omit = omit, + company_name: str | Omit = omit, + description: str | Omit = omit, + email: str | Omit = omit, + force_verified_status: bool | Omit = omit, + job_title: str | Omit = omit, + last_name: str | Omit = omit, + metadata: Dict[str, object] | Omit = omit, + phone_number: str | Omit = omit, + postal_or_zip: str | Omit = omit, + province_or_state: str | Omit = omit, + skip_verification: bool | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Contact: return await self._post( "/print-mail/v1/contacts", @@ -659,7 +655,7 @@ async def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Contact: """ Retrieve a contact. @@ -686,15 +682,15 @@ async def retrieve( def list( self, *, - limit: int | NotGiven = NOT_GIVEN, - search: str | NotGiven = NOT_GIVEN, - skip: int | NotGiven = NOT_GIVEN, + limit: int | Omit = omit, + search: str | Omit = omit, + skip: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[Contact, AsyncSkipLimit[Contact]]: """ Get a list of contacts. @@ -743,7 +739,7 @@ async def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ContactDeleteResponse: """Delete a contact. diff --git a/src/postgrid/resources/print_mail/letters.py b/src/postgrid/resources/print_mail/letters.py index 50e037d..b134ee6 100644 --- a/src/postgrid/resources/print_mail/letters.py +++ b/src/postgrid/resources/print_mail/letters.py @@ -8,12 +8,8 @@ import httpx -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ..._utils import ( - required_args, - maybe_transform, - async_maybe_transform, -) +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import required_args, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -69,26 +65,26 @@ def create( from_: letter_create_params.LetterCreateWithHTMLFrom, html: str, to: letter_create_params.LetterCreateWithHTMLTo, - address_placement: AddressPlacement | NotGiven = NOT_GIVEN, - attached_pdf: AttachedPdfParam | NotGiven = NOT_GIVEN, - color: bool | NotGiven = NOT_GIVEN, - description: str | NotGiven = NOT_GIVEN, - double_sided: bool | NotGiven = NOT_GIVEN, - envelope: str | NotGiven = NOT_GIVEN, - mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, - merge_variables: Dict[str, object] | NotGiven = NOT_GIVEN, - metadata: Dict[str, object] | NotGiven = NOT_GIVEN, - perforated_page: Literal[1] | NotGiven = NOT_GIVEN, - plastic_card: PlasticCardParam | NotGiven = NOT_GIVEN, - return_envelope: str | NotGiven = NOT_GIVEN, - send_date: Union[str, datetime] | NotGiven = NOT_GIVEN, - size: LetterSize | NotGiven = NOT_GIVEN, + address_placement: AddressPlacement | Omit = omit, + attached_pdf: AttachedPdfParam | Omit = omit, + color: bool | Omit = omit, + description: str | Omit = omit, + double_sided: bool | Omit = omit, + envelope: str | Omit = omit, + mailing_class: OrderMailingClass | Omit = omit, + merge_variables: Dict[str, object] | Omit = omit, + metadata: Dict[str, object] | Omit = omit, + perforated_page: Literal[1] | Omit = omit, + plastic_card: PlasticCardParam | Omit = omit, + return_envelope: str | Omit = omit, + send_date: Union[str, datetime] | Omit = omit, + size: LetterSize | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Letter: """Create a letter. @@ -167,7 +163,7 @@ def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Letter: """Create a letter. @@ -199,26 +195,26 @@ def create( from_: letter_create_params.LetterCreateWithPdfFrom, pdf: str, to: letter_create_params.LetterCreateWithPdfTo, - address_placement: AddressPlacement | NotGiven = NOT_GIVEN, - attached_pdf: AttachedPdfParam | NotGiven = NOT_GIVEN, - color: bool | NotGiven = NOT_GIVEN, - description: str | NotGiven = NOT_GIVEN, - double_sided: bool | NotGiven = NOT_GIVEN, - envelope: str | NotGiven = NOT_GIVEN, - mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, - merge_variables: Dict[str, object] | NotGiven = NOT_GIVEN, - metadata: Dict[str, object] | NotGiven = NOT_GIVEN, - perforated_page: Literal[1] | NotGiven = NOT_GIVEN, - plastic_card: PlasticCardParam | NotGiven = NOT_GIVEN, - return_envelope: str | NotGiven = NOT_GIVEN, - send_date: Union[str, datetime] | NotGiven = NOT_GIVEN, - size: LetterSize | NotGiven = NOT_GIVEN, + address_placement: AddressPlacement | Omit = omit, + attached_pdf: AttachedPdfParam | Omit = omit, + color: bool | Omit = omit, + description: str | Omit = omit, + double_sided: bool | Omit = omit, + envelope: str | Omit = omit, + mailing_class: OrderMailingClass | Omit = omit, + merge_variables: Dict[str, object] | Omit = omit, + metadata: Dict[str, object] | Omit = omit, + perforated_page: Literal[1] | Omit = omit, + plastic_card: PlasticCardParam | Omit = omit, + return_envelope: str | Omit = omit, + send_date: Union[str, datetime] | Omit = omit, + size: LetterSize | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Letter: """Create a letter. @@ -290,31 +286,33 @@ def create( def create( self, *, - from_: letter_create_params.LetterCreateWithHTMLFrom | NotGiven = NOT_GIVEN, - html: str | NotGiven = NOT_GIVEN, - to: letter_create_params.LetterCreateWithHTMLTo | NotGiven = NOT_GIVEN, - address_placement: AddressPlacement | NotGiven = NOT_GIVEN, - attached_pdf: AttachedPdfParam | NotGiven = NOT_GIVEN, - color: bool | NotGiven = NOT_GIVEN, - description: str | NotGiven = NOT_GIVEN, - double_sided: bool | NotGiven = NOT_GIVEN, - envelope: str | NotGiven = NOT_GIVEN, - mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, - merge_variables: Dict[str, object] | NotGiven = NOT_GIVEN, - metadata: Dict[str, object] | NotGiven = NOT_GIVEN, - perforated_page: Literal[1] | NotGiven = NOT_GIVEN, - plastic_card: PlasticCardParam | NotGiven = NOT_GIVEN, - return_envelope: str | NotGiven = NOT_GIVEN, - send_date: Union[str, datetime] | NotGiven = NOT_GIVEN, - size: LetterSize | NotGiven = NOT_GIVEN, - template: str | NotGiven = NOT_GIVEN, - pdf: str | NotGiven = NOT_GIVEN, + from_: letter_create_params.LetterCreateWithHTMLFrom + | letter_create_params.LetterCreateWithPdfFrom + | Omit = omit, + html: str | Omit = omit, + to: letter_create_params.LetterCreateWithHTMLTo | letter_create_params.LetterCreateWithPdfTo | Omit = omit, + address_placement: AddressPlacement | Omit = omit, + attached_pdf: AttachedPdfParam | Omit = omit, + color: bool | Omit = omit, + description: str | Omit = omit, + double_sided: bool | Omit = omit, + envelope: str | Omit = omit, + mailing_class: OrderMailingClass | Omit = omit, + merge_variables: Dict[str, object] | Omit = omit, + metadata: Dict[str, object] | Omit = omit, + perforated_page: Literal[1] | Omit = omit, + plastic_card: PlasticCardParam | Omit = omit, + return_envelope: str | Omit = omit, + send_date: Union[str, datetime] | Omit = omit, + size: LetterSize | Omit = omit, + template: str | Omit = omit, + pdf: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Letter: return self._post( "/print-mail/v1/letters", @@ -357,7 +355,7 @@ def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Letter: """ Retrieve a letter by ID. @@ -384,15 +382,15 @@ def retrieve( def list( self, *, - limit: int | NotGiven = NOT_GIVEN, - search: str | NotGiven = NOT_GIVEN, - skip: int | NotGiven = NOT_GIVEN, + limit: int | Omit = omit, + search: str | Omit = omit, + skip: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncSkipLimit[Letter]: """ Get a list of letters. @@ -441,7 +439,7 @@ def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Letter: """Cancel a letter by ID. @@ -475,7 +473,7 @@ def retrieve_url( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> LetterRetrieveURLResponse: """ Retrieve a letter preview URL. @@ -532,26 +530,26 @@ async def create( from_: letter_create_params.LetterCreateWithHTMLFrom, html: str, to: letter_create_params.LetterCreateWithHTMLTo, - address_placement: AddressPlacement | NotGiven = NOT_GIVEN, - attached_pdf: AttachedPdfParam | NotGiven = NOT_GIVEN, - color: bool | NotGiven = NOT_GIVEN, - description: str | NotGiven = NOT_GIVEN, - double_sided: bool | NotGiven = NOT_GIVEN, - envelope: str | NotGiven = NOT_GIVEN, - mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, - merge_variables: Dict[str, object] | NotGiven = NOT_GIVEN, - metadata: Dict[str, object] | NotGiven = NOT_GIVEN, - perforated_page: Literal[1] | NotGiven = NOT_GIVEN, - plastic_card: PlasticCardParam | NotGiven = NOT_GIVEN, - return_envelope: str | NotGiven = NOT_GIVEN, - send_date: Union[str, datetime] | NotGiven = NOT_GIVEN, - size: LetterSize | NotGiven = NOT_GIVEN, + address_placement: AddressPlacement | Omit = omit, + attached_pdf: AttachedPdfParam | Omit = omit, + color: bool | Omit = omit, + description: str | Omit = omit, + double_sided: bool | Omit = omit, + envelope: str | Omit = omit, + mailing_class: OrderMailingClass | Omit = omit, + merge_variables: Dict[str, object] | Omit = omit, + metadata: Dict[str, object] | Omit = omit, + perforated_page: Literal[1] | Omit = omit, + plastic_card: PlasticCardParam | Omit = omit, + return_envelope: str | Omit = omit, + send_date: Union[str, datetime] | Omit = omit, + size: LetterSize | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Letter: """Create a letter. @@ -630,7 +628,7 @@ async def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Letter: """Create a letter. @@ -662,26 +660,26 @@ async def create( from_: letter_create_params.LetterCreateWithPdfFrom, pdf: str, to: letter_create_params.LetterCreateWithPdfTo, - address_placement: AddressPlacement | NotGiven = NOT_GIVEN, - attached_pdf: AttachedPdfParam | NotGiven = NOT_GIVEN, - color: bool | NotGiven = NOT_GIVEN, - description: str | NotGiven = NOT_GIVEN, - double_sided: bool | NotGiven = NOT_GIVEN, - envelope: str | NotGiven = NOT_GIVEN, - mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, - merge_variables: Dict[str, object] | NotGiven = NOT_GIVEN, - metadata: Dict[str, object] | NotGiven = NOT_GIVEN, - perforated_page: Literal[1] | NotGiven = NOT_GIVEN, - plastic_card: PlasticCardParam | NotGiven = NOT_GIVEN, - return_envelope: str | NotGiven = NOT_GIVEN, - send_date: Union[str, datetime] | NotGiven = NOT_GIVEN, - size: LetterSize | NotGiven = NOT_GIVEN, + address_placement: AddressPlacement | Omit = omit, + attached_pdf: AttachedPdfParam | Omit = omit, + color: bool | Omit = omit, + description: str | Omit = omit, + double_sided: bool | Omit = omit, + envelope: str | Omit = omit, + mailing_class: OrderMailingClass | Omit = omit, + merge_variables: Dict[str, object] | Omit = omit, + metadata: Dict[str, object] | Omit = omit, + perforated_page: Literal[1] | Omit = omit, + plastic_card: PlasticCardParam | Omit = omit, + return_envelope: str | Omit = omit, + send_date: Union[str, datetime] | Omit = omit, + size: LetterSize | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Letter: """Create a letter. @@ -753,31 +751,33 @@ async def create( async def create( self, *, - from_: letter_create_params.LetterCreateWithHTMLFrom | NotGiven = NOT_GIVEN, - html: str | NotGiven = NOT_GIVEN, - to: letter_create_params.LetterCreateWithHTMLTo | NotGiven = NOT_GIVEN, - address_placement: AddressPlacement | NotGiven = NOT_GIVEN, - attached_pdf: AttachedPdfParam | NotGiven = NOT_GIVEN, - color: bool | NotGiven = NOT_GIVEN, - description: str | NotGiven = NOT_GIVEN, - double_sided: bool | NotGiven = NOT_GIVEN, - envelope: str | NotGiven = NOT_GIVEN, - mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, - merge_variables: Dict[str, object] | NotGiven = NOT_GIVEN, - metadata: Dict[str, object] | NotGiven = NOT_GIVEN, - perforated_page: Literal[1] | NotGiven = NOT_GIVEN, - plastic_card: PlasticCardParam | NotGiven = NOT_GIVEN, - return_envelope: str | NotGiven = NOT_GIVEN, - send_date: Union[str, datetime] | NotGiven = NOT_GIVEN, - size: LetterSize | NotGiven = NOT_GIVEN, - template: str | NotGiven = NOT_GIVEN, - pdf: str | NotGiven = NOT_GIVEN, + from_: letter_create_params.LetterCreateWithHTMLFrom + | letter_create_params.LetterCreateWithPdfFrom + | Omit = omit, + html: str | Omit = omit, + to: letter_create_params.LetterCreateWithHTMLTo | letter_create_params.LetterCreateWithPdfTo | Omit = omit, + address_placement: AddressPlacement | Omit = omit, + attached_pdf: AttachedPdfParam | Omit = omit, + color: bool | Omit = omit, + description: str | Omit = omit, + double_sided: bool | Omit = omit, + envelope: str | Omit = omit, + mailing_class: OrderMailingClass | Omit = omit, + merge_variables: Dict[str, object] | Omit = omit, + metadata: Dict[str, object] | Omit = omit, + perforated_page: Literal[1] | Omit = omit, + plastic_card: PlasticCardParam | Omit = omit, + return_envelope: str | Omit = omit, + send_date: Union[str, datetime] | Omit = omit, + size: LetterSize | Omit = omit, + template: str | Omit = omit, + pdf: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Letter: return await self._post( "/print-mail/v1/letters", @@ -820,7 +820,7 @@ async def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Letter: """ Retrieve a letter by ID. @@ -847,15 +847,15 @@ async def retrieve( def list( self, *, - limit: int | NotGiven = NOT_GIVEN, - search: str | NotGiven = NOT_GIVEN, - skip: int | NotGiven = NOT_GIVEN, + limit: int | Omit = omit, + search: str | Omit = omit, + skip: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[Letter, AsyncSkipLimit[Letter]]: """ Get a list of letters. @@ -904,7 +904,7 @@ async def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Letter: """Cancel a letter by ID. @@ -938,7 +938,7 @@ async def retrieve_url( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> LetterRetrieveURLResponse: """ Retrieve a letter preview URL. diff --git a/src/postgrid/resources/print_mail/mailing_list_imports.py b/src/postgrid/resources/print_mail/mailing_list_imports.py index 3ae7b44..549ba31 100644 --- a/src/postgrid/resources/print_mail/mailing_list_imports.py +++ b/src/postgrid/resources/print_mail/mailing_list_imports.py @@ -6,12 +6,8 @@ import httpx -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ..._utils import ( - maybe_transform, - strip_not_given, - async_maybe_transform, -) +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import maybe_transform, strip_not_given, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -61,18 +57,18 @@ def create( file: str, file_type: FileType, receiver_address_mapping: Dict[str, str], - description: str | NotGiven = NOT_GIVEN, - metadata: Dict[str, object] | NotGiven = NOT_GIVEN, - receiver_merge_variable_mapping: Dict[str, str] | NotGiven = NOT_GIVEN, - sender_address_mapping: Dict[str, str] | NotGiven = NOT_GIVEN, - sender_merge_variable_mapping: Dict[str, str] | NotGiven = NOT_GIVEN, - idempotency_key: str | NotGiven = NOT_GIVEN, + description: str | Omit = omit, + metadata: Dict[str, object] | Omit = omit, + receiver_merge_variable_mapping: Dict[str, str] | Omit = omit, + sender_address_mapping: Dict[str, str] | Omit = omit, + sender_merge_variable_mapping: Dict[str, str] | Omit = omit, + idempotency_key: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> MailingListImportResponse: """ Create a new mailing list import. @@ -138,7 +134,7 @@ def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> MailingListImportResponse: """ Retrieve a specific mailing list import by its ID. @@ -166,14 +162,14 @@ def update( self, id: str, *, - description: Optional[str] | NotGiven = NOT_GIVEN, - metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, + description: Optional[str] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> MailingListImportResponse: """ Update an existing mailing list import. @@ -213,15 +209,15 @@ def update( def list( self, *, - limit: int | NotGiven = NOT_GIVEN, - search: str | NotGiven = NOT_GIVEN, - skip: int | NotGiven = NOT_GIVEN, + limit: int | Omit = omit, + search: str | Omit = omit, + skip: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncSkipLimit[MailingListImportResponse]: """ Retrieve a list of mailing list imports. @@ -273,7 +269,7 @@ def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> MailingListImportDeleteResponse: """ Delete a mailing list import. @@ -327,18 +323,18 @@ async def create( file: str, file_type: FileType, receiver_address_mapping: Dict[str, str], - description: str | NotGiven = NOT_GIVEN, - metadata: Dict[str, object] | NotGiven = NOT_GIVEN, - receiver_merge_variable_mapping: Dict[str, str] | NotGiven = NOT_GIVEN, - sender_address_mapping: Dict[str, str] | NotGiven = NOT_GIVEN, - sender_merge_variable_mapping: Dict[str, str] | NotGiven = NOT_GIVEN, - idempotency_key: str | NotGiven = NOT_GIVEN, + description: str | Omit = omit, + metadata: Dict[str, object] | Omit = omit, + receiver_merge_variable_mapping: Dict[str, str] | Omit = omit, + sender_address_mapping: Dict[str, str] | Omit = omit, + sender_merge_variable_mapping: Dict[str, str] | Omit = omit, + idempotency_key: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> MailingListImportResponse: """ Create a new mailing list import. @@ -404,7 +400,7 @@ async def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> MailingListImportResponse: """ Retrieve a specific mailing list import by its ID. @@ -432,14 +428,14 @@ async def update( self, id: str, *, - description: Optional[str] | NotGiven = NOT_GIVEN, - metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, + description: Optional[str] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> MailingListImportResponse: """ Update an existing mailing list import. @@ -479,15 +475,15 @@ async def update( def list( self, *, - limit: int | NotGiven = NOT_GIVEN, - search: str | NotGiven = NOT_GIVEN, - skip: int | NotGiven = NOT_GIVEN, + limit: int | Omit = omit, + search: str | Omit = omit, + skip: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[MailingListImportResponse, AsyncSkipLimit[MailingListImportResponse]]: """ Retrieve a list of mailing list imports. @@ -539,7 +535,7 @@ async def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> MailingListImportDeleteResponse: """ Delete a mailing list import. diff --git a/src/postgrid/resources/print_mail/mailing_lists.py b/src/postgrid/resources/print_mail/mailing_lists.py index 555563e..c2f8886 100644 --- a/src/postgrid/resources/print_mail/mailing_lists.py +++ b/src/postgrid/resources/print_mail/mailing_lists.py @@ -2,16 +2,12 @@ from __future__ import annotations -from typing import Dict, List +from typing import Dict import httpx -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ..._utils import ( - maybe_transform, - strip_not_given, - async_maybe_transform, -) +from ..._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from ..._utils import maybe_transform, strip_not_given, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -58,15 +54,15 @@ def with_streaming_response(self) -> MailingListsResourceWithStreamingResponse: def create( self, *, - description: str | NotGiven = NOT_GIVEN, - metadata: Dict[str, object] | NotGiven = NOT_GIVEN, - idempotency_key: str | NotGiven = NOT_GIVEN, + description: str | Omit = omit, + metadata: Dict[str, object] | Omit = omit, + idempotency_key: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> MailingList: """ Create a new mailing list. @@ -110,7 +106,7 @@ def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> MailingList: """ Retrieve a specific mailing list by its ID. @@ -138,14 +134,14 @@ def update( self, id: str, *, - description: str | NotGiven = NOT_GIVEN, - metadata: Dict[str, object] | NotGiven = NOT_GIVEN, + description: str | Omit = omit, + metadata: Dict[str, object] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> MailingListUpdate: """ Update an existing mailing list. @@ -184,15 +180,15 @@ def update( def list( self, *, - limit: int | NotGiven = NOT_GIVEN, - search: str | NotGiven = NOT_GIVEN, - skip: int | NotGiven = NOT_GIVEN, + limit: int | Omit = omit, + search: str | Omit = omit, + skip: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncSkipLimit[MailingList]: """ Retrieve a list of mailing lists. @@ -244,7 +240,7 @@ def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> MailingListDeleteResponse: """ Delete a mailing list. @@ -275,16 +271,16 @@ def jobs( self, id: str, *, - add_contacts: List[str] | NotGiven = NOT_GIVEN, - add_mailing_list_imports: List[str] | NotGiven = NOT_GIVEN, - remove_contacts: List[str] | NotGiven = NOT_GIVEN, - remove_mailing_list_imports: List[str] | NotGiven = NOT_GIVEN, + add_contacts: SequenceNotStr[str] | Omit = omit, + add_mailing_list_imports: SequenceNotStr[str] | Omit = omit, + remove_contacts: SequenceNotStr[str] | Omit = omit, + remove_mailing_list_imports: SequenceNotStr[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> MailingList: """Runs a mailing list job. @@ -364,15 +360,15 @@ def with_streaming_response(self) -> AsyncMailingListsResourceWithStreamingRespo async def create( self, *, - description: str | NotGiven = NOT_GIVEN, - metadata: Dict[str, object] | NotGiven = NOT_GIVEN, - idempotency_key: str | NotGiven = NOT_GIVEN, + description: str | Omit = omit, + metadata: Dict[str, object] | Omit = omit, + idempotency_key: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> MailingList: """ Create a new mailing list. @@ -416,7 +412,7 @@ async def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> MailingList: """ Retrieve a specific mailing list by its ID. @@ -444,14 +440,14 @@ async def update( self, id: str, *, - description: str | NotGiven = NOT_GIVEN, - metadata: Dict[str, object] | NotGiven = NOT_GIVEN, + description: str | Omit = omit, + metadata: Dict[str, object] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> MailingListUpdate: """ Update an existing mailing list. @@ -490,15 +486,15 @@ async def update( def list( self, *, - limit: int | NotGiven = NOT_GIVEN, - search: str | NotGiven = NOT_GIVEN, - skip: int | NotGiven = NOT_GIVEN, + limit: int | Omit = omit, + search: str | Omit = omit, + skip: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[MailingList, AsyncSkipLimit[MailingList]]: """ Retrieve a list of mailing lists. @@ -550,7 +546,7 @@ async def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> MailingListDeleteResponse: """ Delete a mailing list. @@ -581,16 +577,16 @@ async def jobs( self, id: str, *, - add_contacts: List[str] | NotGiven = NOT_GIVEN, - add_mailing_list_imports: List[str] | NotGiven = NOT_GIVEN, - remove_contacts: List[str] | NotGiven = NOT_GIVEN, - remove_mailing_list_imports: List[str] | NotGiven = NOT_GIVEN, + add_contacts: SequenceNotStr[str] | Omit = omit, + add_mailing_list_imports: SequenceNotStr[str] | Omit = omit, + remove_contacts: SequenceNotStr[str] | Omit = omit, + remove_mailing_list_imports: SequenceNotStr[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> MailingList: """Runs a mailing list job. diff --git a/src/postgrid/resources/print_mail/order_profiles/cheques.py b/src/postgrid/resources/print_mail/order_profiles/cheques.py index d7d46ba..3cb9194 100644 --- a/src/postgrid/resources/print_mail/order_profiles/cheques.py +++ b/src/postgrid/resources/print_mail/order_profiles/cheques.py @@ -2,22 +2,22 @@ from __future__ import annotations -from typing import Dict, List, Union, Optional +from typing import Dict, Union, Optional import httpx from ...._types import ( - NOT_GIVEN, Body, + Omit, Query, Headers, NotGiven, + SequenceNotStr, Base64FileInput, + omit, + not_given, ) -from ...._utils import ( - maybe_transform, - async_maybe_transform, -) +from ...._utils import maybe_transform, async_maybe_transform from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import ( @@ -71,23 +71,23 @@ def create( *, bank_account: str, size: ChequeSize, - expand: List[str] | NotGiven = NOT_GIVEN, - currency_code: CurrencyCode | NotGiven = NOT_GIVEN, - description: Optional[str] | NotGiven = NOT_GIVEN, - letter_pdf: Union[str, Base64FileInput] | NotGiven = NOT_GIVEN, - letter_template: str | NotGiven = NOT_GIVEN, - logo: Optional[str] | NotGiven = NOT_GIVEN, - mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, - memo: Optional[str] | NotGiven = NOT_GIVEN, - merge_variables: Optional[Dict[str, object]] | NotGiven = NOT_GIVEN, - message: Optional[str] | NotGiven = NOT_GIVEN, - metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, + expand: SequenceNotStr[str] | Omit = omit, + currency_code: CurrencyCode | Omit = omit, + description: Optional[str] | Omit = omit, + letter_pdf: Union[str, Base64FileInput] | Omit = omit, + letter_template: str | Omit = omit, + logo: Optional[str] | Omit = omit, + mailing_class: OrderMailingClass | Omit = omit, + memo: Optional[str] | Omit = omit, + merge_variables: Optional[Dict[str, object]] | Omit = omit, + message: Optional[str] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ChequeProfile: """Creates a new Cheque Profile. @@ -168,13 +168,13 @@ def retrieve( self, id: str, *, - expand: List[str] | NotGiven = NOT_GIVEN, + expand: SequenceNotStr[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ChequeProfile: """ Retrieves the details of a specific Cheque Profile. @@ -210,23 +210,23 @@ def update( *, bank_account: str, size: ChequeSize, - expand: List[str] | NotGiven = NOT_GIVEN, - currency_code: CurrencyCode | NotGiven = NOT_GIVEN, - description: Optional[str] | NotGiven = NOT_GIVEN, - letter_pdf: Union[str, Base64FileInput] | NotGiven = NOT_GIVEN, - letter_template: str | NotGiven = NOT_GIVEN, - logo: Optional[str] | NotGiven = NOT_GIVEN, - mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, - memo: Optional[str] | NotGiven = NOT_GIVEN, - merge_variables: Optional[Dict[str, object]] | NotGiven = NOT_GIVEN, - message: Optional[str] | NotGiven = NOT_GIVEN, - metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, + expand: SequenceNotStr[str] | Omit = omit, + currency_code: CurrencyCode | Omit = omit, + description: Optional[str] | Omit = omit, + letter_pdf: Union[str, Base64FileInput] | Omit = omit, + letter_template: str | Omit = omit, + logo: Optional[str] | Omit = omit, + mailing_class: OrderMailingClass | Omit = omit, + memo: Optional[str] | Omit = omit, + merge_variables: Optional[Dict[str, object]] | Omit = omit, + message: Optional[str] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ChequeProfile: """Updates specific fields of an existing Cheque Profile. @@ -306,15 +306,15 @@ def update( def list( self, *, - limit: int | NotGiven = NOT_GIVEN, - search: str | NotGiven = NOT_GIVEN, - skip: int | NotGiven = NOT_GIVEN, + limit: int | Omit = omit, + search: str | Omit = omit, + skip: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncSkipLimit[ChequeListResponse]: """ Retrieves a list of Cheque Profiles. @@ -363,7 +363,7 @@ def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ChequeDeleteResponse: """ Deletes a Cheque Profile. @@ -413,23 +413,23 @@ async def create( *, bank_account: str, size: ChequeSize, - expand: List[str] | NotGiven = NOT_GIVEN, - currency_code: CurrencyCode | NotGiven = NOT_GIVEN, - description: Optional[str] | NotGiven = NOT_GIVEN, - letter_pdf: Union[str, Base64FileInput] | NotGiven = NOT_GIVEN, - letter_template: str | NotGiven = NOT_GIVEN, - logo: Optional[str] | NotGiven = NOT_GIVEN, - mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, - memo: Optional[str] | NotGiven = NOT_GIVEN, - merge_variables: Optional[Dict[str, object]] | NotGiven = NOT_GIVEN, - message: Optional[str] | NotGiven = NOT_GIVEN, - metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, + expand: SequenceNotStr[str] | Omit = omit, + currency_code: CurrencyCode | Omit = omit, + description: Optional[str] | Omit = omit, + letter_pdf: Union[str, Base64FileInput] | Omit = omit, + letter_template: str | Omit = omit, + logo: Optional[str] | Omit = omit, + mailing_class: OrderMailingClass | Omit = omit, + memo: Optional[str] | Omit = omit, + merge_variables: Optional[Dict[str, object]] | Omit = omit, + message: Optional[str] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ChequeProfile: """Creates a new Cheque Profile. @@ -510,13 +510,13 @@ async def retrieve( self, id: str, *, - expand: List[str] | NotGiven = NOT_GIVEN, + expand: SequenceNotStr[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ChequeProfile: """ Retrieves the details of a specific Cheque Profile. @@ -552,23 +552,23 @@ async def update( *, bank_account: str, size: ChequeSize, - expand: List[str] | NotGiven = NOT_GIVEN, - currency_code: CurrencyCode | NotGiven = NOT_GIVEN, - description: Optional[str] | NotGiven = NOT_GIVEN, - letter_pdf: Union[str, Base64FileInput] | NotGiven = NOT_GIVEN, - letter_template: str | NotGiven = NOT_GIVEN, - logo: Optional[str] | NotGiven = NOT_GIVEN, - mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, - memo: Optional[str] | NotGiven = NOT_GIVEN, - merge_variables: Optional[Dict[str, object]] | NotGiven = NOT_GIVEN, - message: Optional[str] | NotGiven = NOT_GIVEN, - metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, + expand: SequenceNotStr[str] | Omit = omit, + currency_code: CurrencyCode | Omit = omit, + description: Optional[str] | Omit = omit, + letter_pdf: Union[str, Base64FileInput] | Omit = omit, + letter_template: str | Omit = omit, + logo: Optional[str] | Omit = omit, + mailing_class: OrderMailingClass | Omit = omit, + memo: Optional[str] | Omit = omit, + merge_variables: Optional[Dict[str, object]] | Omit = omit, + message: Optional[str] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ChequeProfile: """Updates specific fields of an existing Cheque Profile. @@ -648,15 +648,15 @@ async def update( def list( self, *, - limit: int | NotGiven = NOT_GIVEN, - search: str | NotGiven = NOT_GIVEN, - skip: int | NotGiven = NOT_GIVEN, + limit: int | Omit = omit, + search: str | Omit = omit, + skip: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[ChequeListResponse, AsyncSkipLimit[ChequeListResponse]]: """ Retrieves a list of Cheque Profiles. @@ -705,7 +705,7 @@ async def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ChequeDeleteResponse: """ Deletes a Cheque Profile. diff --git a/src/postgrid/resources/print_mail/order_profiles/letters.py b/src/postgrid/resources/print_mail/order_profiles/letters.py index 8b64b27..692ecbf 100644 --- a/src/postgrid/resources/print_mail/order_profiles/letters.py +++ b/src/postgrid/resources/print_mail/order_profiles/letters.py @@ -2,16 +2,13 @@ from __future__ import annotations -from typing import Dict, List, Optional +from typing import Dict, Optional from typing_extensions import Literal import httpx -from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ...._utils import ( - maybe_transform, - async_maybe_transform, -) +from ...._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from ...._utils import maybe_transform, async_maybe_transform from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import ( @@ -63,26 +60,26 @@ def create( self, *, size: LetterSize, - expand: List[str] | NotGiven = NOT_GIVEN, - address_placement: AddressPlacement | NotGiven = NOT_GIVEN, - attached_pdf: AttachedPdfParam | NotGiven = NOT_GIVEN, - color: bool | NotGiven = NOT_GIVEN, - description: Optional[str] | NotGiven = NOT_GIVEN, - double_sided: bool | NotGiven = NOT_GIVEN, - envelope: str | NotGiven = NOT_GIVEN, - mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, - merge_variables: Optional[Dict[str, object]] | NotGiven = NOT_GIVEN, - metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, - pdf: str | NotGiven = NOT_GIVEN, - perforated_page: Literal[1] | NotGiven = NOT_GIVEN, - return_envelope: str | NotGiven = NOT_GIVEN, - template: str | NotGiven = NOT_GIVEN, + expand: SequenceNotStr[str] | Omit = omit, + address_placement: AddressPlacement | Omit = omit, + attached_pdf: AttachedPdfParam | Omit = omit, + color: bool | Omit = omit, + description: Optional[str] | Omit = omit, + double_sided: bool | Omit = omit, + envelope: str | Omit = omit, + mailing_class: OrderMailingClass | Omit = omit, + merge_variables: Optional[Dict[str, object]] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + pdf: str | Omit = omit, + perforated_page: Literal[1] | Omit = omit, + return_envelope: str | Omit = omit, + template: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> LetterProfile: """Creates a new Letter Profile. @@ -164,13 +161,13 @@ def retrieve( self, id: str, *, - expand: List[str] | NotGiven = NOT_GIVEN, + expand: SequenceNotStr[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> LetterProfile: """ Retrieves the details of a specific Letter Profile by its ID. @@ -204,26 +201,26 @@ def update( self, id: str, *, - expand: List[str] | NotGiven = NOT_GIVEN, - address_placement: AddressPlacement | NotGiven = NOT_GIVEN, - attached_pdf: AttachedPdfParam | NotGiven = NOT_GIVEN, - color: bool | NotGiven = NOT_GIVEN, - description: Optional[str] | NotGiven = NOT_GIVEN, - double_sided: bool | NotGiven = NOT_GIVEN, - envelope: str | NotGiven = NOT_GIVEN, - mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, - merge_variables: Optional[Dict[str, object]] | NotGiven = NOT_GIVEN, - metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, - pdf: str | NotGiven = NOT_GIVEN, - perforated_page: Literal[1] | NotGiven = NOT_GIVEN, - return_envelope: str | NotGiven = NOT_GIVEN, - template: str | NotGiven = NOT_GIVEN, + expand: SequenceNotStr[str] | Omit = omit, + address_placement: AddressPlacement | Omit = omit, + attached_pdf: AttachedPdfParam | Omit = omit, + color: bool | Omit = omit, + description: Optional[str] | Omit = omit, + double_sided: bool | Omit = omit, + envelope: str | Omit = omit, + mailing_class: OrderMailingClass | Omit = omit, + merge_variables: Optional[Dict[str, object]] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + pdf: str | Omit = omit, + perforated_page: Literal[1] | Omit = omit, + return_envelope: str | Omit = omit, + template: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> LetterProfile: """Updates specific fields of an existing Letter Profile. @@ -303,15 +300,15 @@ def update( def list( self, *, - limit: int | NotGiven = NOT_GIVEN, - search: str | NotGiven = NOT_GIVEN, - skip: int | NotGiven = NOT_GIVEN, + limit: int | Omit = omit, + search: str | Omit = omit, + skip: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncSkipLimit[LetterProfile]: """Retrieves a list of Letter Profiles. @@ -362,7 +359,7 @@ def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> LetterDeleteResponse: """Deletes a Letter Profile. @@ -413,26 +410,26 @@ async def create( self, *, size: LetterSize, - expand: List[str] | NotGiven = NOT_GIVEN, - address_placement: AddressPlacement | NotGiven = NOT_GIVEN, - attached_pdf: AttachedPdfParam | NotGiven = NOT_GIVEN, - color: bool | NotGiven = NOT_GIVEN, - description: Optional[str] | NotGiven = NOT_GIVEN, - double_sided: bool | NotGiven = NOT_GIVEN, - envelope: str | NotGiven = NOT_GIVEN, - mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, - merge_variables: Optional[Dict[str, object]] | NotGiven = NOT_GIVEN, - metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, - pdf: str | NotGiven = NOT_GIVEN, - perforated_page: Literal[1] | NotGiven = NOT_GIVEN, - return_envelope: str | NotGiven = NOT_GIVEN, - template: str | NotGiven = NOT_GIVEN, + expand: SequenceNotStr[str] | Omit = omit, + address_placement: AddressPlacement | Omit = omit, + attached_pdf: AttachedPdfParam | Omit = omit, + color: bool | Omit = omit, + description: Optional[str] | Omit = omit, + double_sided: bool | Omit = omit, + envelope: str | Omit = omit, + mailing_class: OrderMailingClass | Omit = omit, + merge_variables: Optional[Dict[str, object]] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + pdf: str | Omit = omit, + perforated_page: Literal[1] | Omit = omit, + return_envelope: str | Omit = omit, + template: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> LetterProfile: """Creates a new Letter Profile. @@ -514,13 +511,13 @@ async def retrieve( self, id: str, *, - expand: List[str] | NotGiven = NOT_GIVEN, + expand: SequenceNotStr[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> LetterProfile: """ Retrieves the details of a specific Letter Profile by its ID. @@ -554,26 +551,26 @@ async def update( self, id: str, *, - expand: List[str] | NotGiven = NOT_GIVEN, - address_placement: AddressPlacement | NotGiven = NOT_GIVEN, - attached_pdf: AttachedPdfParam | NotGiven = NOT_GIVEN, - color: bool | NotGiven = NOT_GIVEN, - description: Optional[str] | NotGiven = NOT_GIVEN, - double_sided: bool | NotGiven = NOT_GIVEN, - envelope: str | NotGiven = NOT_GIVEN, - mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, - merge_variables: Optional[Dict[str, object]] | NotGiven = NOT_GIVEN, - metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, - pdf: str | NotGiven = NOT_GIVEN, - perforated_page: Literal[1] | NotGiven = NOT_GIVEN, - return_envelope: str | NotGiven = NOT_GIVEN, - template: str | NotGiven = NOT_GIVEN, + expand: SequenceNotStr[str] | Omit = omit, + address_placement: AddressPlacement | Omit = omit, + attached_pdf: AttachedPdfParam | Omit = omit, + color: bool | Omit = omit, + description: Optional[str] | Omit = omit, + double_sided: bool | Omit = omit, + envelope: str | Omit = omit, + mailing_class: OrderMailingClass | Omit = omit, + merge_variables: Optional[Dict[str, object]] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + pdf: str | Omit = omit, + perforated_page: Literal[1] | Omit = omit, + return_envelope: str | Omit = omit, + template: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> LetterProfile: """Updates specific fields of an existing Letter Profile. @@ -653,15 +650,15 @@ async def update( def list( self, *, - limit: int | NotGiven = NOT_GIVEN, - search: str | NotGiven = NOT_GIVEN, - skip: int | NotGiven = NOT_GIVEN, + limit: int | Omit = omit, + search: str | Omit = omit, + skip: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[LetterProfile, AsyncSkipLimit[LetterProfile]]: """Retrieves a list of Letter Profiles. @@ -712,7 +709,7 @@ async def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> LetterDeleteResponse: """Deletes a Letter Profile. diff --git a/src/postgrid/resources/print_mail/order_profiles/postcards.py b/src/postgrid/resources/print_mail/order_profiles/postcards.py index 57b1a99..f46367a 100644 --- a/src/postgrid/resources/print_mail/order_profiles/postcards.py +++ b/src/postgrid/resources/print_mail/order_profiles/postcards.py @@ -2,15 +2,12 @@ from __future__ import annotations -from typing import Dict, List, Optional +from typing import Dict, Optional import httpx -from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ...._utils import ( - maybe_transform, - async_maybe_transform, -) +from ...._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from ...._utils import maybe_transform, async_maybe_transform from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import ( @@ -61,20 +58,20 @@ def create( self, *, size: PostcardSize, - expand: List[str] | NotGiven = NOT_GIVEN, - back_template: str | NotGiven = NOT_GIVEN, - description: Optional[str] | NotGiven = NOT_GIVEN, - front_template: str | NotGiven = NOT_GIVEN, - mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, - merge_variables: Optional[Dict[str, object]] | NotGiven = NOT_GIVEN, - metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, - pdf: str | NotGiven = NOT_GIVEN, + expand: SequenceNotStr[str] | Omit = omit, + back_template: str | Omit = omit, + description: Optional[str] | Omit = omit, + front_template: str | Omit = omit, + mailing_class: OrderMailingClass | Omit = omit, + merge_variables: Optional[Dict[str, object]] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + pdf: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> PostcardProfile: """Creates a new Postcard Profile. @@ -140,13 +137,13 @@ def retrieve( self, id: str, *, - expand: List[str] | NotGiven = NOT_GIVEN, + expand: SequenceNotStr[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> PostcardProfile: """ Retrieves the details of a specific Postcard Profile. @@ -180,20 +177,20 @@ def update( self, id: str, *, - expand: List[str] | NotGiven = NOT_GIVEN, - back_template: str | NotGiven = NOT_GIVEN, - description: Optional[str] | NotGiven = NOT_GIVEN, - front_template: str | NotGiven = NOT_GIVEN, - mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, - merge_variables: Optional[Dict[str, object]] | NotGiven = NOT_GIVEN, - metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, - pdf: str | NotGiven = NOT_GIVEN, + expand: SequenceNotStr[str] | Omit = omit, + back_template: str | Omit = omit, + description: Optional[str] | Omit = omit, + front_template: str | Omit = omit, + mailing_class: OrderMailingClass | Omit = omit, + merge_variables: Optional[Dict[str, object]] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + pdf: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> PostcardProfile: """Updates specific fields of an existing Postcard Profile. @@ -256,15 +253,15 @@ def update( def list( self, *, - limit: int | NotGiven = NOT_GIVEN, - search: str | NotGiven = NOT_GIVEN, - skip: int | NotGiven = NOT_GIVEN, + limit: int | Omit = omit, + search: str | Omit = omit, + skip: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncSkipLimit[PostcardProfile]: """ Retrieves a list of Postcard Profiles. @@ -313,7 +310,7 @@ def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> PostcardDeleteResponse: """ Deletes a Postcard Profile. @@ -362,20 +359,20 @@ async def create( self, *, size: PostcardSize, - expand: List[str] | NotGiven = NOT_GIVEN, - back_template: str | NotGiven = NOT_GIVEN, - description: Optional[str] | NotGiven = NOT_GIVEN, - front_template: str | NotGiven = NOT_GIVEN, - mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, - merge_variables: Optional[Dict[str, object]] | NotGiven = NOT_GIVEN, - metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, - pdf: str | NotGiven = NOT_GIVEN, + expand: SequenceNotStr[str] | Omit = omit, + back_template: str | Omit = omit, + description: Optional[str] | Omit = omit, + front_template: str | Omit = omit, + mailing_class: OrderMailingClass | Omit = omit, + merge_variables: Optional[Dict[str, object]] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + pdf: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> PostcardProfile: """Creates a new Postcard Profile. @@ -441,13 +438,13 @@ async def retrieve( self, id: str, *, - expand: List[str] | NotGiven = NOT_GIVEN, + expand: SequenceNotStr[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> PostcardProfile: """ Retrieves the details of a specific Postcard Profile. @@ -481,20 +478,20 @@ async def update( self, id: str, *, - expand: List[str] | NotGiven = NOT_GIVEN, - back_template: str | NotGiven = NOT_GIVEN, - description: Optional[str] | NotGiven = NOT_GIVEN, - front_template: str | NotGiven = NOT_GIVEN, - mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, - merge_variables: Optional[Dict[str, object]] | NotGiven = NOT_GIVEN, - metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, - pdf: str | NotGiven = NOT_GIVEN, + expand: SequenceNotStr[str] | Omit = omit, + back_template: str | Omit = omit, + description: Optional[str] | Omit = omit, + front_template: str | Omit = omit, + mailing_class: OrderMailingClass | Omit = omit, + merge_variables: Optional[Dict[str, object]] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + pdf: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> PostcardProfile: """Updates specific fields of an existing Postcard Profile. @@ -557,15 +554,15 @@ async def update( def list( self, *, - limit: int | NotGiven = NOT_GIVEN, - search: str | NotGiven = NOT_GIVEN, - skip: int | NotGiven = NOT_GIVEN, + limit: int | Omit = omit, + search: str | Omit = omit, + skip: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[PostcardProfile, AsyncSkipLimit[PostcardProfile]]: """ Retrieves a list of Postcard Profiles. @@ -614,7 +611,7 @@ async def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> PostcardDeleteResponse: """ Deletes a Postcard Profile. diff --git a/src/postgrid/resources/print_mail/order_profiles/self_mailers.py b/src/postgrid/resources/print_mail/order_profiles/self_mailers.py index 70b6cb9..7da1682 100644 --- a/src/postgrid/resources/print_mail/order_profiles/self_mailers.py +++ b/src/postgrid/resources/print_mail/order_profiles/self_mailers.py @@ -2,15 +2,12 @@ from __future__ import annotations -from typing import Dict, List, Optional +from typing import Dict, Optional import httpx -from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ...._utils import ( - maybe_transform, - async_maybe_transform, -) +from ...._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from ...._utils import maybe_transform, async_maybe_transform from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import ( @@ -61,20 +58,20 @@ def create( self, *, size: SelfMailerSize, - expand: List[str] | NotGiven = NOT_GIVEN, - description: Optional[str] | NotGiven = NOT_GIVEN, - inside_template: str | NotGiven = NOT_GIVEN, - mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, - merge_variables: Optional[Dict[str, object]] | NotGiven = NOT_GIVEN, - metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, - outside_template: str | NotGiven = NOT_GIVEN, - pdf: str | NotGiven = NOT_GIVEN, + expand: SequenceNotStr[str] | Omit = omit, + description: Optional[str] | Omit = omit, + inside_template: str | Omit = omit, + mailing_class: OrderMailingClass | Omit = omit, + merge_variables: Optional[Dict[str, object]] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + outside_template: str | Omit = omit, + pdf: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SelfMailerProfile: """Creates a new Self-Mailer Profile. @@ -139,13 +136,13 @@ def retrieve( self, id: str, *, - expand: List[str] | NotGiven = NOT_GIVEN, + expand: SequenceNotStr[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SelfMailerProfile: """ Retrieves the details of a specific Self-Mailer Profile. @@ -180,20 +177,20 @@ def update( id: str, *, size: SelfMailerSize, - expand: List[str] | NotGiven = NOT_GIVEN, - description: Optional[str] | NotGiven = NOT_GIVEN, - inside_template: str | NotGiven = NOT_GIVEN, - mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, - merge_variables: Optional[Dict[str, object]] | NotGiven = NOT_GIVEN, - metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, - outside_template: str | NotGiven = NOT_GIVEN, - pdf: str | NotGiven = NOT_GIVEN, + expand: SequenceNotStr[str] | Omit = omit, + description: Optional[str] | Omit = omit, + inside_template: str | Omit = omit, + mailing_class: OrderMailingClass | Omit = omit, + merge_variables: Optional[Dict[str, object]] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + outside_template: str | Omit = omit, + pdf: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SelfMailerProfile: """Updates specific fields of an existing Self-Mailer Profile. @@ -258,15 +255,15 @@ def update( def list( self, *, - limit: int | NotGiven = NOT_GIVEN, - search: str | NotGiven = NOT_GIVEN, - skip: int | NotGiven = NOT_GIVEN, + limit: int | Omit = omit, + search: str | Omit = omit, + skip: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncSkipLimit[SelfMailerProfile]: """ Retrieves a list of Self-Mailer Profiles. @@ -315,7 +312,7 @@ def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SelfMailerDeleteResponse: """ Deletes a Self-Mailer Profile. @@ -364,20 +361,20 @@ async def create( self, *, size: SelfMailerSize, - expand: List[str] | NotGiven = NOT_GIVEN, - description: Optional[str] | NotGiven = NOT_GIVEN, - inside_template: str | NotGiven = NOT_GIVEN, - mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, - merge_variables: Optional[Dict[str, object]] | NotGiven = NOT_GIVEN, - metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, - outside_template: str | NotGiven = NOT_GIVEN, - pdf: str | NotGiven = NOT_GIVEN, + expand: SequenceNotStr[str] | Omit = omit, + description: Optional[str] | Omit = omit, + inside_template: str | Omit = omit, + mailing_class: OrderMailingClass | Omit = omit, + merge_variables: Optional[Dict[str, object]] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + outside_template: str | Omit = omit, + pdf: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SelfMailerProfile: """Creates a new Self-Mailer Profile. @@ -442,13 +439,13 @@ async def retrieve( self, id: str, *, - expand: List[str] | NotGiven = NOT_GIVEN, + expand: SequenceNotStr[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SelfMailerProfile: """ Retrieves the details of a specific Self-Mailer Profile. @@ -485,20 +482,20 @@ async def update( id: str, *, size: SelfMailerSize, - expand: List[str] | NotGiven = NOT_GIVEN, - description: Optional[str] | NotGiven = NOT_GIVEN, - inside_template: str | NotGiven = NOT_GIVEN, - mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, - merge_variables: Optional[Dict[str, object]] | NotGiven = NOT_GIVEN, - metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, - outside_template: str | NotGiven = NOT_GIVEN, - pdf: str | NotGiven = NOT_GIVEN, + expand: SequenceNotStr[str] | Omit = omit, + description: Optional[str] | Omit = omit, + inside_template: str | Omit = omit, + mailing_class: OrderMailingClass | Omit = omit, + merge_variables: Optional[Dict[str, object]] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + outside_template: str | Omit = omit, + pdf: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SelfMailerProfile: """Updates specific fields of an existing Self-Mailer Profile. @@ -563,15 +560,15 @@ async def update( def list( self, *, - limit: int | NotGiven = NOT_GIVEN, - search: str | NotGiven = NOT_GIVEN, - skip: int | NotGiven = NOT_GIVEN, + limit: int | Omit = omit, + search: str | Omit = omit, + skip: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[SelfMailerProfile, AsyncSkipLimit[SelfMailerProfile]]: """ Retrieves a list of Self-Mailer Profiles. @@ -620,7 +617,7 @@ async def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SelfMailerDeleteResponse: """ Deletes a Self-Mailer Profile. diff --git a/src/postgrid/resources/print_mail/postcards.py b/src/postgrid/resources/print_mail/postcards.py index 6d7c4ef..ba39f7c 100644 --- a/src/postgrid/resources/print_mail/postcards.py +++ b/src/postgrid/resources/print_mail/postcards.py @@ -8,19 +8,8 @@ import httpx -from ..._types import ( - NOT_GIVEN, - Body, - Query, - Headers, - NotGiven, - Base64FileInput, -) -from ..._utils import ( - required_args, - maybe_transform, - async_maybe_transform, -) +from ..._types import Body, Omit, Query, Headers, NotGiven, Base64FileInput, omit, not_given +from ..._utils import required_args, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -69,18 +58,18 @@ def create( front_html: str, size: PostcardSize, to: postcard_create_params.PostcardCreateWithHTMLTo, - description: str | NotGiven = NOT_GIVEN, - from_: postcard_create_params.PostcardCreateWithHTMLFrom | NotGiven = NOT_GIVEN, - mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, - merge_variables: Dict[str, object] | NotGiven = NOT_GIVEN, - metadata: Dict[str, object] | NotGiven = NOT_GIVEN, - send_date: Union[str, datetime] | NotGiven = NOT_GIVEN, + description: str | Omit = omit, + from_: postcard_create_params.PostcardCreateWithHTMLFrom | Omit = omit, + mailing_class: OrderMailingClass | Omit = omit, + merge_variables: Dict[str, object] | Omit = omit, + metadata: Dict[str, object] | Omit = omit, + send_date: Union[str, datetime] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Postcard: """Create a postcard. @@ -147,7 +136,7 @@ def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Postcard: """Create a postcard. @@ -183,18 +172,18 @@ def create( pdf: str, size: PostcardSize, to: postcard_create_params.PostcardCreateWithPdfurlTo, - description: str | NotGiven = NOT_GIVEN, - from_: postcard_create_params.PostcardCreateWithPdfurlFrom | NotGiven = NOT_GIVEN, - mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, - merge_variables: Dict[str, object] | NotGiven = NOT_GIVEN, - metadata: Dict[str, object] | NotGiven = NOT_GIVEN, - send_date: Union[str, datetime] | NotGiven = NOT_GIVEN, + description: str | Omit = omit, + from_: postcard_create_params.PostcardCreateWithPdfurlFrom | Omit = omit, + mailing_class: OrderMailingClass | Omit = omit, + merge_variables: Dict[str, object] | Omit = omit, + metadata: Dict[str, object] | Omit = omit, + send_date: Union[str, datetime] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Postcard: """Create a postcard. @@ -254,18 +243,18 @@ def create( pdf: Union[str, Base64FileInput], size: PostcardSize, to: postcard_create_params.PostcardCreateWithPdfFileTo, - description: str | NotGiven = NOT_GIVEN, - from_: postcard_create_params.PostcardCreateWithPdfFileFrom | NotGiven = NOT_GIVEN, - mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, - merge_variables: Dict[str, object] | NotGiven = NOT_GIVEN, - metadata: Dict[str, object] | NotGiven = NOT_GIVEN, - send_date: Union[str, datetime] | NotGiven = NOT_GIVEN, + description: str | Omit = omit, + from_: postcard_create_params.PostcardCreateWithPdfFileFrom | Omit = omit, + mailing_class: OrderMailingClass | Omit = omit, + merge_variables: Dict[str, object] | Omit = omit, + metadata: Dict[str, object] | Omit = omit, + send_date: Union[str, datetime] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Postcard: """Create a postcard. @@ -324,25 +313,31 @@ def create( def create( self, *, - back_html: str | NotGiven = NOT_GIVEN, - front_html: str | NotGiven = NOT_GIVEN, - size: PostcardSize | NotGiven = NOT_GIVEN, - to: postcard_create_params.PostcardCreateWithHTMLTo | NotGiven = NOT_GIVEN, - description: str | NotGiven = NOT_GIVEN, - from_: postcard_create_params.PostcardCreateWithHTMLFrom | NotGiven = NOT_GIVEN, - mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, - merge_variables: Dict[str, object] | NotGiven = NOT_GIVEN, - metadata: Dict[str, object] | NotGiven = NOT_GIVEN, - send_date: Union[str, datetime] | NotGiven = NOT_GIVEN, - back_template: str | NotGiven = NOT_GIVEN, - front_template: str | NotGiven = NOT_GIVEN, - pdf: str | NotGiven = NOT_GIVEN, + back_html: str | Omit = omit, + front_html: str | Omit = omit, + size: PostcardSize | Omit = omit, + to: postcard_create_params.PostcardCreateWithHTMLTo + | postcard_create_params.PostcardCreateWithPdfurlTo + | postcard_create_params.PostcardCreateWithPdfFileTo + | Omit = omit, + description: str | Omit = omit, + from_: postcard_create_params.PostcardCreateWithHTMLFrom + | postcard_create_params.PostcardCreateWithPdfurlFrom + | postcard_create_params.PostcardCreateWithPdfFileFrom + | Omit = omit, + mailing_class: OrderMailingClass | Omit = omit, + merge_variables: Dict[str, object] | Omit = omit, + metadata: Dict[str, object] | Omit = omit, + send_date: Union[str, datetime] | Omit = omit, + back_template: str | Omit = omit, + front_template: str | Omit = omit, + pdf: str | Union[str, Base64FileInput] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Postcard: return self._post( "/print-mail/v1/postcards", @@ -379,7 +374,7 @@ def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Postcard: """ Retrieve a postcard by ID. @@ -406,15 +401,15 @@ def retrieve( def list( self, *, - limit: int | NotGiven = NOT_GIVEN, - search: str | NotGiven = NOT_GIVEN, - skip: int | NotGiven = NOT_GIVEN, + limit: int | Omit = omit, + search: str | Omit = omit, + skip: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncSkipLimit[Postcard]: """ Get a list of postcards. @@ -463,7 +458,7 @@ def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Postcard: """Cancel a postcard by ID. @@ -497,7 +492,7 @@ def retrieve_url( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> PostcardRetrieveURLResponse: """ Retrieve a postcard preview URL. @@ -555,18 +550,18 @@ async def create( front_html: str, size: PostcardSize, to: postcard_create_params.PostcardCreateWithHTMLTo, - description: str | NotGiven = NOT_GIVEN, - from_: postcard_create_params.PostcardCreateWithHTMLFrom | NotGiven = NOT_GIVEN, - mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, - merge_variables: Dict[str, object] | NotGiven = NOT_GIVEN, - metadata: Dict[str, object] | NotGiven = NOT_GIVEN, - send_date: Union[str, datetime] | NotGiven = NOT_GIVEN, + description: str | Omit = omit, + from_: postcard_create_params.PostcardCreateWithHTMLFrom | Omit = omit, + mailing_class: OrderMailingClass | Omit = omit, + merge_variables: Dict[str, object] | Omit = omit, + metadata: Dict[str, object] | Omit = omit, + send_date: Union[str, datetime] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Postcard: """Create a postcard. @@ -633,7 +628,7 @@ async def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Postcard: """Create a postcard. @@ -669,18 +664,18 @@ async def create( pdf: str, size: PostcardSize, to: postcard_create_params.PostcardCreateWithPdfurlTo, - description: str | NotGiven = NOT_GIVEN, - from_: postcard_create_params.PostcardCreateWithPdfurlFrom | NotGiven = NOT_GIVEN, - mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, - merge_variables: Dict[str, object] | NotGiven = NOT_GIVEN, - metadata: Dict[str, object] | NotGiven = NOT_GIVEN, - send_date: Union[str, datetime] | NotGiven = NOT_GIVEN, + description: str | Omit = omit, + from_: postcard_create_params.PostcardCreateWithPdfurlFrom | Omit = omit, + mailing_class: OrderMailingClass | Omit = omit, + merge_variables: Dict[str, object] | Omit = omit, + metadata: Dict[str, object] | Omit = omit, + send_date: Union[str, datetime] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Postcard: """Create a postcard. @@ -740,18 +735,18 @@ async def create( pdf: Union[str, Base64FileInput], size: PostcardSize, to: postcard_create_params.PostcardCreateWithPdfFileTo, - description: str | NotGiven = NOT_GIVEN, - from_: postcard_create_params.PostcardCreateWithPdfFileFrom | NotGiven = NOT_GIVEN, - mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, - merge_variables: Dict[str, object] | NotGiven = NOT_GIVEN, - metadata: Dict[str, object] | NotGiven = NOT_GIVEN, - send_date: Union[str, datetime] | NotGiven = NOT_GIVEN, + description: str | Omit = omit, + from_: postcard_create_params.PostcardCreateWithPdfFileFrom | Omit = omit, + mailing_class: OrderMailingClass | Omit = omit, + merge_variables: Dict[str, object] | Omit = omit, + metadata: Dict[str, object] | Omit = omit, + send_date: Union[str, datetime] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Postcard: """Create a postcard. @@ -810,25 +805,31 @@ async def create( async def create( self, *, - back_html: str | NotGiven = NOT_GIVEN, - front_html: str | NotGiven = NOT_GIVEN, - size: PostcardSize | NotGiven = NOT_GIVEN, - to: postcard_create_params.PostcardCreateWithHTMLTo | NotGiven = NOT_GIVEN, - description: str | NotGiven = NOT_GIVEN, - from_: postcard_create_params.PostcardCreateWithHTMLFrom | NotGiven = NOT_GIVEN, - mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, - merge_variables: Dict[str, object] | NotGiven = NOT_GIVEN, - metadata: Dict[str, object] | NotGiven = NOT_GIVEN, - send_date: Union[str, datetime] | NotGiven = NOT_GIVEN, - back_template: str | NotGiven = NOT_GIVEN, - front_template: str | NotGiven = NOT_GIVEN, - pdf: str | NotGiven = NOT_GIVEN, + back_html: str | Omit = omit, + front_html: str | Omit = omit, + size: PostcardSize | Omit = omit, + to: postcard_create_params.PostcardCreateWithHTMLTo + | postcard_create_params.PostcardCreateWithPdfurlTo + | postcard_create_params.PostcardCreateWithPdfFileTo + | Omit = omit, + description: str | Omit = omit, + from_: postcard_create_params.PostcardCreateWithHTMLFrom + | postcard_create_params.PostcardCreateWithPdfurlFrom + | postcard_create_params.PostcardCreateWithPdfFileFrom + | Omit = omit, + mailing_class: OrderMailingClass | Omit = omit, + merge_variables: Dict[str, object] | Omit = omit, + metadata: Dict[str, object] | Omit = omit, + send_date: Union[str, datetime] | Omit = omit, + back_template: str | Omit = omit, + front_template: str | Omit = omit, + pdf: str | Union[str, Base64FileInput] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Postcard: return await self._post( "/print-mail/v1/postcards", @@ -865,7 +866,7 @@ async def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Postcard: """ Retrieve a postcard by ID. @@ -892,15 +893,15 @@ async def retrieve( def list( self, *, - limit: int | NotGiven = NOT_GIVEN, - search: str | NotGiven = NOT_GIVEN, - skip: int | NotGiven = NOT_GIVEN, + limit: int | Omit = omit, + search: str | Omit = omit, + skip: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[Postcard, AsyncSkipLimit[Postcard]]: """ Get a list of postcards. @@ -949,7 +950,7 @@ async def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Postcard: """Cancel a postcard by ID. @@ -983,7 +984,7 @@ async def retrieve_url( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> PostcardRetrieveURLResponse: """ Retrieve a postcard preview URL. diff --git a/src/postgrid/resources/print_mail/reports/exports.py b/src/postgrid/resources/print_mail/reports/exports.py index 3582d02..ba1d1d7 100644 --- a/src/postgrid/resources/print_mail/reports/exports.py +++ b/src/postgrid/resources/print_mail/reports/exports.py @@ -2,15 +2,12 @@ from __future__ import annotations -from typing import Dict, List +from typing import Dict import httpx -from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ...._utils import ( - maybe_transform, - async_maybe_transform, -) +from ...._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from ...._utils import maybe_transform, async_maybe_transform from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import ( @@ -51,15 +48,15 @@ def create( self, report_id: str, *, - description: str | NotGiven = NOT_GIVEN, - metadata: Dict[str, str] | NotGiven = NOT_GIVEN, - params: List[str] | NotGiven = NOT_GIVEN, + description: str | Omit = omit, + metadata: Dict[str, str] | Omit = omit, + params: SequenceNotStr[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ReportExport: """Create a new export job for a saved report. @@ -111,7 +108,7 @@ def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ReportExport: """Retrieve the status and details of a specific report export job. @@ -149,7 +146,7 @@ def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> DeletedResponse: """ Delete a completed or failed report export job and its associated output file @@ -202,15 +199,15 @@ async def create( self, report_id: str, *, - description: str | NotGiven = NOT_GIVEN, - metadata: Dict[str, str] | NotGiven = NOT_GIVEN, - params: List[str] | NotGiven = NOT_GIVEN, + description: str | Omit = omit, + metadata: Dict[str, str] | Omit = omit, + params: SequenceNotStr[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ReportExport: """Create a new export job for a saved report. @@ -262,7 +259,7 @@ async def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ReportExport: """Retrieve the status and details of a specific report export job. @@ -300,7 +297,7 @@ async def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> DeletedResponse: """ Delete a completed or failed report export job and its associated output file diff --git a/src/postgrid/resources/print_mail/reports/reports.py b/src/postgrid/resources/print_mail/reports/reports.py index 0106b25..6330f76 100644 --- a/src/postgrid/resources/print_mail/reports/reports.py +++ b/src/postgrid/resources/print_mail/reports/reports.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Dict, List, Optional +from typing import Dict, Optional import httpx @@ -22,11 +22,8 @@ SamplesResourceWithStreamingResponse, AsyncSamplesResourceWithStreamingResponse, ) -from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ...._utils import ( - maybe_transform, - async_maybe_transform, -) +from ...._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from ...._utils import maybe_transform, async_maybe_transform from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import ( @@ -77,14 +74,14 @@ def create( self, *, sql_query: str, - description: str | NotGiven = NOT_GIVEN, - metadata: Dict[str, str] | NotGiven = NOT_GIVEN, + description: str | Omit = omit, + metadata: Dict[str, str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Report: """Create a new saved report definition. @@ -134,7 +131,7 @@ def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Report: """ Retrieve the details of a specific saved report by its ID. @@ -162,15 +159,15 @@ def update( self, id: str, *, - description: Optional[str] | NotGiven = NOT_GIVEN, - metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, - sql_query: str | NotGiven = NOT_GIVEN, + description: Optional[str] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + sql_query: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Report: """Update an existing saved report definition. @@ -213,15 +210,15 @@ def update( def list( self, *, - limit: int | NotGiven = NOT_GIVEN, - search: str | NotGiven = NOT_GIVEN, - skip: int | NotGiven = NOT_GIVEN, + limit: int | Omit = omit, + search: str | Omit = omit, + skip: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncSkipLimit[Report]: """ Retrieve a list of saved reports for your organization. @@ -270,7 +267,7 @@ def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> DeletedResponse: """Delete a saved report definition. @@ -300,14 +297,14 @@ def sample( self, *, sql_query: str, - limit: int | NotGiven = NOT_GIVEN, - params: List[str] | NotGiven = NOT_GIVEN, + limit: int | Omit = omit, + params: SequenceNotStr[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ReportSample: """ Run an ad-hoc SQL query against your data lake and get a sample of the results. @@ -379,14 +376,14 @@ async def create( self, *, sql_query: str, - description: str | NotGiven = NOT_GIVEN, - metadata: Dict[str, str] | NotGiven = NOT_GIVEN, + description: str | Omit = omit, + metadata: Dict[str, str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Report: """Create a new saved report definition. @@ -436,7 +433,7 @@ async def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Report: """ Retrieve the details of a specific saved report by its ID. @@ -464,15 +461,15 @@ async def update( self, id: str, *, - description: Optional[str] | NotGiven = NOT_GIVEN, - metadata: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN, - sql_query: str | NotGiven = NOT_GIVEN, + description: Optional[str] | Omit = omit, + metadata: Optional[Dict[str, str]] | Omit = omit, + sql_query: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Report: """Update an existing saved report definition. @@ -515,15 +512,15 @@ async def update( def list( self, *, - limit: int | NotGiven = NOT_GIVEN, - search: str | NotGiven = NOT_GIVEN, - skip: int | NotGiven = NOT_GIVEN, + limit: int | Omit = omit, + search: str | Omit = omit, + skip: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[Report, AsyncSkipLimit[Report]]: """ Retrieve a list of saved reports for your organization. @@ -572,7 +569,7 @@ async def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> DeletedResponse: """Delete a saved report definition. @@ -602,14 +599,14 @@ async def sample( self, *, sql_query: str, - limit: int | NotGiven = NOT_GIVEN, - params: List[str] | NotGiven = NOT_GIVEN, + limit: int | Omit = omit, + params: SequenceNotStr[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ReportSample: """ Run an ad-hoc SQL query against your data lake and get a sample of the results. diff --git a/src/postgrid/resources/print_mail/reports/samples.py b/src/postgrid/resources/print_mail/reports/samples.py index 4b7365e..e3766a2 100644 --- a/src/postgrid/resources/print_mail/reports/samples.py +++ b/src/postgrid/resources/print_mail/reports/samples.py @@ -2,15 +2,10 @@ from __future__ import annotations -from typing import List - import httpx -from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ...._utils import ( - maybe_transform, - async_maybe_transform, -) +from ...._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from ...._utils import maybe_transform, async_maybe_transform from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import ( @@ -50,14 +45,14 @@ def create( self, id: str, *, - limit: int | NotGiven = NOT_GIVEN, - params: List[str] | NotGiven = NOT_GIVEN, + limit: int | Omit = omit, + params: SequenceNotStr[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ReportSample: """ Run the query associated with a saved report and get a sample of the results. @@ -121,14 +116,14 @@ async def create( self, id: str, *, - limit: int | NotGiven = NOT_GIVEN, - params: List[str] | NotGiven = NOT_GIVEN, + limit: int | Omit = omit, + params: SequenceNotStr[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ReportSample: """ Run the query associated with a saved report and get a sample of the results. diff --git a/src/postgrid/resources/print_mail/self_mailers.py b/src/postgrid/resources/print_mail/self_mailers.py index 738af18..b5924e9 100644 --- a/src/postgrid/resources/print_mail/self_mailers.py +++ b/src/postgrid/resources/print_mail/self_mailers.py @@ -8,19 +8,8 @@ import httpx -from ..._types import ( - NOT_GIVEN, - Body, - Query, - Headers, - NotGiven, - Base64FileInput, -) -from ..._utils import ( - required_args, - maybe_transform, - async_maybe_transform, -) +from ..._types import Body, Omit, Query, Headers, NotGiven, Base64FileInput, omit, not_given +from ..._utils import required_args, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -70,17 +59,17 @@ def create( outside_html: str, size: SelfMailerSize, to: self_mailer_create_params.SelfMailerCreateWithHTMLTo, - description: str | NotGiven = NOT_GIVEN, - mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, - merge_variables: Dict[str, object] | NotGiven = NOT_GIVEN, - metadata: Dict[str, object] | NotGiven = NOT_GIVEN, - send_date: Union[str, datetime] | NotGiven = NOT_GIVEN, + description: str | Omit = omit, + mailing_class: OrderMailingClass | Omit = omit, + merge_variables: Dict[str, object] | Omit = omit, + metadata: Dict[str, object] | Omit = omit, + send_date: Union[str, datetime] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SelfMailer: """Create a self-mailer. @@ -146,7 +135,7 @@ def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SelfMailer: """Create a self-mailer. @@ -183,17 +172,17 @@ def create( pdf: str, size: SelfMailerSize, to: self_mailer_create_params.SelfMailerCreateWithPdfurlTo, - description: str | NotGiven = NOT_GIVEN, - mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, - merge_variables: Dict[str, object] | NotGiven = NOT_GIVEN, - metadata: Dict[str, object] | NotGiven = NOT_GIVEN, - send_date: Union[str, datetime] | NotGiven = NOT_GIVEN, + description: str | Omit = omit, + mailing_class: OrderMailingClass | Omit = omit, + merge_variables: Dict[str, object] | Omit = omit, + metadata: Dict[str, object] | Omit = omit, + send_date: Union[str, datetime] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SelfMailer: """Create a self-mailer. @@ -254,17 +243,17 @@ def create( pdf: Union[str, Base64FileInput], size: SelfMailerSize, to: self_mailer_create_params.SelfMailerCreateWithPdfFileTo, - description: str | NotGiven = NOT_GIVEN, - mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, - merge_variables: Dict[str, object] | NotGiven = NOT_GIVEN, - metadata: Dict[str, object] | NotGiven = NOT_GIVEN, - send_date: Union[str, datetime] | NotGiven = NOT_GIVEN, + description: str | Omit = omit, + mailing_class: OrderMailingClass | Omit = omit, + merge_variables: Dict[str, object] | Omit = omit, + metadata: Dict[str, object] | Omit = omit, + send_date: Union[str, datetime] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SelfMailer: """Create a self-mailer. @@ -324,25 +313,31 @@ def create( def create( self, *, - from_: self_mailer_create_params.SelfMailerCreateWithHTMLFrom | NotGiven = NOT_GIVEN, - inside_html: str | NotGiven = NOT_GIVEN, - outside_html: str | NotGiven = NOT_GIVEN, - size: SelfMailerSize | NotGiven = NOT_GIVEN, - to: self_mailer_create_params.SelfMailerCreateWithHTMLTo | NotGiven = NOT_GIVEN, - description: str | NotGiven = NOT_GIVEN, - mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, - merge_variables: Dict[str, object] | NotGiven = NOT_GIVEN, - metadata: Dict[str, object] | NotGiven = NOT_GIVEN, - send_date: Union[str, datetime] | NotGiven = NOT_GIVEN, - inside_template: str | NotGiven = NOT_GIVEN, - outside_template: str | NotGiven = NOT_GIVEN, - pdf: str | NotGiven = NOT_GIVEN, + from_: self_mailer_create_params.SelfMailerCreateWithHTMLFrom + | self_mailer_create_params.SelfMailerCreateWithPdfurlFrom + | self_mailer_create_params.SelfMailerCreateWithPdfFileFrom + | Omit = omit, + inside_html: str | Omit = omit, + outside_html: str | Omit = omit, + size: SelfMailerSize | Omit = omit, + to: self_mailer_create_params.SelfMailerCreateWithHTMLTo + | self_mailer_create_params.SelfMailerCreateWithPdfurlTo + | self_mailer_create_params.SelfMailerCreateWithPdfFileTo + | Omit = omit, + description: str | Omit = omit, + mailing_class: OrderMailingClass | Omit = omit, + merge_variables: Dict[str, object] | Omit = omit, + metadata: Dict[str, object] | Omit = omit, + send_date: Union[str, datetime] | Omit = omit, + inside_template: str | Omit = omit, + outside_template: str | Omit = omit, + pdf: str | Union[str, Base64FileInput] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SelfMailer: return self._post( "/print-mail/v1/self_mailers", @@ -379,7 +374,7 @@ def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SelfMailer: """ Retrieve a self-mailer by ID. @@ -406,15 +401,15 @@ def retrieve( def list( self, *, - limit: int | NotGiven = NOT_GIVEN, - search: str | NotGiven = NOT_GIVEN, - skip: int | NotGiven = NOT_GIVEN, + limit: int | Omit = omit, + search: str | Omit = omit, + skip: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncSkipLimit[SelfMailer]: """ Get a list of self-mailers. @@ -463,7 +458,7 @@ def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SelfMailer: """Cancel a self-mailer by ID. @@ -497,7 +492,7 @@ def retrieve_url( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SelfMailerRetrieveURLResponse: """ Retrieve a self-mailer preview URL. @@ -556,17 +551,17 @@ async def create( outside_html: str, size: SelfMailerSize, to: self_mailer_create_params.SelfMailerCreateWithHTMLTo, - description: str | NotGiven = NOT_GIVEN, - mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, - merge_variables: Dict[str, object] | NotGiven = NOT_GIVEN, - metadata: Dict[str, object] | NotGiven = NOT_GIVEN, - send_date: Union[str, datetime] | NotGiven = NOT_GIVEN, + description: str | Omit = omit, + mailing_class: OrderMailingClass | Omit = omit, + merge_variables: Dict[str, object] | Omit = omit, + metadata: Dict[str, object] | Omit = omit, + send_date: Union[str, datetime] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SelfMailer: """Create a self-mailer. @@ -632,7 +627,7 @@ async def create( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SelfMailer: """Create a self-mailer. @@ -669,17 +664,17 @@ async def create( pdf: str, size: SelfMailerSize, to: self_mailer_create_params.SelfMailerCreateWithPdfurlTo, - description: str | NotGiven = NOT_GIVEN, - mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, - merge_variables: Dict[str, object] | NotGiven = NOT_GIVEN, - metadata: Dict[str, object] | NotGiven = NOT_GIVEN, - send_date: Union[str, datetime] | NotGiven = NOT_GIVEN, + description: str | Omit = omit, + mailing_class: OrderMailingClass | Omit = omit, + merge_variables: Dict[str, object] | Omit = omit, + metadata: Dict[str, object] | Omit = omit, + send_date: Union[str, datetime] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SelfMailer: """Create a self-mailer. @@ -740,17 +735,17 @@ async def create( pdf: Union[str, Base64FileInput], size: SelfMailerSize, to: self_mailer_create_params.SelfMailerCreateWithPdfFileTo, - description: str | NotGiven = NOT_GIVEN, - mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, - merge_variables: Dict[str, object] | NotGiven = NOT_GIVEN, - metadata: Dict[str, object] | NotGiven = NOT_GIVEN, - send_date: Union[str, datetime] | NotGiven = NOT_GIVEN, + description: str | Omit = omit, + mailing_class: OrderMailingClass | Omit = omit, + merge_variables: Dict[str, object] | Omit = omit, + metadata: Dict[str, object] | Omit = omit, + send_date: Union[str, datetime] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SelfMailer: """Create a self-mailer. @@ -810,25 +805,31 @@ async def create( async def create( self, *, - from_: self_mailer_create_params.SelfMailerCreateWithHTMLFrom | NotGiven = NOT_GIVEN, - inside_html: str | NotGiven = NOT_GIVEN, - outside_html: str | NotGiven = NOT_GIVEN, - size: SelfMailerSize | NotGiven = NOT_GIVEN, - to: self_mailer_create_params.SelfMailerCreateWithHTMLTo | NotGiven = NOT_GIVEN, - description: str | NotGiven = NOT_GIVEN, - mailing_class: OrderMailingClass | NotGiven = NOT_GIVEN, - merge_variables: Dict[str, object] | NotGiven = NOT_GIVEN, - metadata: Dict[str, object] | NotGiven = NOT_GIVEN, - send_date: Union[str, datetime] | NotGiven = NOT_GIVEN, - inside_template: str | NotGiven = NOT_GIVEN, - outside_template: str | NotGiven = NOT_GIVEN, - pdf: str | NotGiven = NOT_GIVEN, + from_: self_mailer_create_params.SelfMailerCreateWithHTMLFrom + | self_mailer_create_params.SelfMailerCreateWithPdfurlFrom + | self_mailer_create_params.SelfMailerCreateWithPdfFileFrom + | Omit = omit, + inside_html: str | Omit = omit, + outside_html: str | Omit = omit, + size: SelfMailerSize | Omit = omit, + to: self_mailer_create_params.SelfMailerCreateWithHTMLTo + | self_mailer_create_params.SelfMailerCreateWithPdfurlTo + | self_mailer_create_params.SelfMailerCreateWithPdfFileTo + | Omit = omit, + description: str | Omit = omit, + mailing_class: OrderMailingClass | Omit = omit, + merge_variables: Dict[str, object] | Omit = omit, + metadata: Dict[str, object] | Omit = omit, + send_date: Union[str, datetime] | Omit = omit, + inside_template: str | Omit = omit, + outside_template: str | Omit = omit, + pdf: str | Union[str, Base64FileInput] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SelfMailer: return await self._post( "/print-mail/v1/self_mailers", @@ -865,7 +866,7 @@ async def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SelfMailer: """ Retrieve a self-mailer by ID. @@ -892,15 +893,15 @@ async def retrieve( def list( self, *, - limit: int | NotGiven = NOT_GIVEN, - search: str | NotGiven = NOT_GIVEN, - skip: int | NotGiven = NOT_GIVEN, + limit: int | Omit = omit, + search: str | Omit = omit, + skip: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[SelfMailer, AsyncSkipLimit[SelfMailer]]: """ Get a list of self-mailers. @@ -949,7 +950,7 @@ async def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SelfMailer: """Cancel a self-mailer by ID. @@ -983,7 +984,7 @@ async def retrieve_url( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SelfMailerRetrieveURLResponse: """ Retrieve a self-mailer preview URL. diff --git a/src/postgrid/resources/print_mail/sub_organizations.py b/src/postgrid/resources/print_mail/sub_organizations.py index d9932d6..47ac31b 100644 --- a/src/postgrid/resources/print_mail/sub_organizations.py +++ b/src/postgrid/resources/print_mail/sub_organizations.py @@ -4,11 +4,8 @@ import httpx -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ..._utils import ( - maybe_transform, - async_maybe_transform, -) +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -60,7 +57,7 @@ def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SubOrganization: """ Get a sub-organization. @@ -92,13 +89,13 @@ def update( name: str, organization_name: str, password: str, - phone_number: str | NotGiven = NOT_GIVEN, + phone_number: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SubOrganizationUpdateResponse: """ When creating a user through the API, the verifiedEmail field will automatically @@ -148,15 +145,15 @@ def update( def list( self, *, - limit: int | NotGiven = NOT_GIVEN, - search: str | NotGiven = NOT_GIVEN, - skip: int | NotGiven = NOT_GIVEN, + limit: int | Omit = omit, + search: str | Omit = omit, + skip: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncSkipLimit[SubOrganization]: """ List sub-organizations. @@ -200,15 +197,15 @@ def retrieve_users( self, id: str, *, - limit: int | NotGiven = NOT_GIVEN, - search: str | NotGiven = NOT_GIVEN, - skip: int | NotGiven = NOT_GIVEN, + limit: int | Omit = omit, + search: str | Omit = omit, + skip: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SubOrganizationRetrieveUsersResponse: """ List users for a sub-organization. @@ -279,7 +276,7 @@ async def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SubOrganization: """ Get a sub-organization. @@ -311,13 +308,13 @@ async def update( name: str, organization_name: str, password: str, - phone_number: str | NotGiven = NOT_GIVEN, + phone_number: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SubOrganizationUpdateResponse: """ When creating a user through the API, the verifiedEmail field will automatically @@ -367,15 +364,15 @@ async def update( def list( self, *, - limit: int | NotGiven = NOT_GIVEN, - search: str | NotGiven = NOT_GIVEN, - skip: int | NotGiven = NOT_GIVEN, + limit: int | Omit = omit, + search: str | Omit = omit, + skip: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[SubOrganization, AsyncSkipLimit[SubOrganization]]: """ List sub-organizations. @@ -419,15 +416,15 @@ async def retrieve_users( self, id: str, *, - limit: int | NotGiven = NOT_GIVEN, - search: str | NotGiven = NOT_GIVEN, - skip: int | NotGiven = NOT_GIVEN, + limit: int | Omit = omit, + search: str | Omit = omit, + skip: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SubOrganizationRetrieveUsersResponse: """ List users for a sub-organization. diff --git a/src/postgrid/resources/print_mail/templates.py b/src/postgrid/resources/print_mail/templates.py index 63dc966..8e2f294 100644 --- a/src/postgrid/resources/print_mail/templates.py +++ b/src/postgrid/resources/print_mail/templates.py @@ -6,11 +6,8 @@ import httpx -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ..._utils import ( - maybe_transform, - async_maybe_transform, -) +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -51,15 +48,15 @@ def with_streaming_response(self) -> TemplatesResourceWithStreamingResponse: def create( self, *, - description: str | NotGiven = NOT_GIVEN, - html: str | NotGiven = NOT_GIVEN, - metadata: Dict[str, object] | NotGiven = NOT_GIVEN, + description: str | Omit = omit, + html: str | Omit = omit, + metadata: Dict[str, object] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Template: """Create a template. @@ -107,7 +104,7 @@ def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Template: """ Retrieve a template by ID. @@ -135,15 +132,15 @@ def update( self, id: str, *, - description: str | NotGiven = NOT_GIVEN, - html: str | NotGiven = NOT_GIVEN, - metadata: Dict[str, object] | NotGiven = NOT_GIVEN, + description: str | Omit = omit, + html: str | Omit = omit, + metadata: Dict[str, object] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Template: """ Update a template by ID. @@ -185,15 +182,15 @@ def update( def list( self, *, - limit: int | NotGiven = NOT_GIVEN, - search: str | NotGiven = NOT_GIVEN, - skip: int | NotGiven = NOT_GIVEN, + limit: int | Omit = omit, + search: str | Omit = omit, + skip: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncSkipLimit[Template]: """ Get a list of templates. @@ -242,7 +239,7 @@ def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> TemplateDeleteResponse: """Delete a template by ID. @@ -291,15 +288,15 @@ def with_streaming_response(self) -> AsyncTemplatesResourceWithStreamingResponse async def create( self, *, - description: str | NotGiven = NOT_GIVEN, - html: str | NotGiven = NOT_GIVEN, - metadata: Dict[str, object] | NotGiven = NOT_GIVEN, + description: str | Omit = omit, + html: str | Omit = omit, + metadata: Dict[str, object] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Template: """Create a template. @@ -347,7 +344,7 @@ async def retrieve( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Template: """ Retrieve a template by ID. @@ -375,15 +372,15 @@ async def update( self, id: str, *, - description: str | NotGiven = NOT_GIVEN, - html: str | NotGiven = NOT_GIVEN, - metadata: Dict[str, object] | NotGiven = NOT_GIVEN, + description: str | Omit = omit, + html: str | Omit = omit, + metadata: Dict[str, object] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Template: """ Update a template by ID. @@ -425,15 +422,15 @@ async def update( def list( self, *, - limit: int | NotGiven = NOT_GIVEN, - search: str | NotGiven = NOT_GIVEN, - skip: int | NotGiven = NOT_GIVEN, + limit: int | Omit = omit, + search: str | Omit = omit, + skip: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[Template, AsyncSkipLimit[Template]]: """ Get a list of templates. @@ -482,7 +479,7 @@ async def delete( extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> TemplateDeleteResponse: """Delete a template by ID. diff --git a/src/postgrid/types/print_mail/box_create_params.py b/src/postgrid/types/print_mail/box_create_params.py index 76e2f0e..d3d914a 100644 --- a/src/postgrid/types/print_mail/box_create_params.py +++ b/src/postgrid/types/print_mail/box_create_params.py @@ -72,7 +72,7 @@ class BoxCreateParams(TypedDict, total=False): ChequeTo: TypeAlias = Union[ContactCreateWithFirstNameParam, ContactCreateWithCompanyNameParam, str] -class Cheque(BoxChequeBaseParam): +class Cheque(BoxChequeBaseParam, total=False): to: Required[ChequeTo] diff --git a/src/postgrid/types/print_mail/cheque_retrieve_url_response.py b/src/postgrid/types/print_mail/cheque_retrieve_url_response.py index dfe3a4a..bc1d84f 100644 --- a/src/postgrid/types/print_mail/cheque_retrieve_url_response.py +++ b/src/postgrid/types/print_mail/cheque_retrieve_url_response.py @@ -1,6 +1,5 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - from ..._models import BaseModel __all__ = ["ChequeRetrieveURLResponse"] diff --git a/src/postgrid/types/print_mail/digital_only.py b/src/postgrid/types/print_mail/digital_only.py index 49dbcf2..8d3e693 100644 --- a/src/postgrid/types/print_mail/digital_only.py +++ b/src/postgrid/types/print_mail/digital_only.py @@ -1,6 +1,5 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - from ..._models import BaseModel __all__ = ["DigitalOnly"] diff --git a/src/postgrid/types/print_mail/letter_retrieve_url_response.py b/src/postgrid/types/print_mail/letter_retrieve_url_response.py index 5fd2cde..3bc8b53 100644 --- a/src/postgrid/types/print_mail/letter_retrieve_url_response.py +++ b/src/postgrid/types/print_mail/letter_retrieve_url_response.py @@ -1,6 +1,5 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - from ..._models import BaseModel __all__ = ["LetterRetrieveURLResponse"] diff --git a/src/postgrid/types/print_mail/mailing_list_jobs_params.py b/src/postgrid/types/print_mail/mailing_list_jobs_params.py index 392aa57..8226254 100644 --- a/src/postgrid/types/print_mail/mailing_list_jobs_params.py +++ b/src/postgrid/types/print_mail/mailing_list_jobs_params.py @@ -2,34 +2,34 @@ from __future__ import annotations -from typing import List from typing_extensions import Annotated, TypedDict +from ..._types import SequenceNotStr from ..._utils import PropertyInfo __all__ = ["MailingListJobsParams"] class MailingListJobsParams(TypedDict, total=False): - add_contacts: Annotated[List[str], PropertyInfo(alias="addContacts")] + add_contacts: Annotated[SequenceNotStr[str], PropertyInfo(alias="addContacts")] """List of contact IDs to add to the mailing list. Cannot be used with other operations. """ - add_mailing_list_imports: Annotated[List[str], PropertyInfo(alias="addMailingListImports")] + add_mailing_list_imports: Annotated[SequenceNotStr[str], PropertyInfo(alias="addMailingListImports")] """List of mailing list import IDs to add to the mailing list. Cannot be used with other operations. """ - remove_contacts: Annotated[List[str], PropertyInfo(alias="removeContacts")] + remove_contacts: Annotated[SequenceNotStr[str], PropertyInfo(alias="removeContacts")] """List of contact IDs to remove from the mailing list. Cannot be used with other operations. """ - remove_mailing_list_imports: Annotated[List[str], PropertyInfo(alias="removeMailingListImports")] + remove_mailing_list_imports: Annotated[SequenceNotStr[str], PropertyInfo(alias="removeMailingListImports")] """List of mailing list import IDs to remove from the mailing list. Cannot be used with other operations. diff --git a/src/postgrid/types/print_mail/order_profiles/cheque_create_params.py b/src/postgrid/types/print_mail/order_profiles/cheque_create_params.py index a720e07..0d0ac3f 100644 --- a/src/postgrid/types/print_mail/order_profiles/cheque_create_params.py +++ b/src/postgrid/types/print_mail/order_profiles/cheque_create_params.py @@ -2,10 +2,10 @@ from __future__ import annotations -from typing import Dict, List, Union, Optional +from typing import Dict, Union, Optional from typing_extensions import Required, Annotated, TypedDict -from ...._types import Base64FileInput +from ...._types import SequenceNotStr, Base64FileInput from ...._utils import PropertyInfo from ..cheque_size import ChequeSize from .currency_code import CurrencyCode @@ -21,7 +21,7 @@ class ChequeCreateParams(TypedDict, total=False): size: Required[ChequeSize] """Enum representing the supported cheque sizes.""" - expand: List[str] + expand: SequenceNotStr[str] """Optional list of related resources to expand in the response.""" currency_code: Annotated[CurrencyCode, PropertyInfo(alias="currencyCode")] diff --git a/src/postgrid/types/print_mail/order_profiles/cheque_retrieve_params.py b/src/postgrid/types/print_mail/order_profiles/cheque_retrieve_params.py index 6cd8f00..d877634 100644 --- a/src/postgrid/types/print_mail/order_profiles/cheque_retrieve_params.py +++ b/src/postgrid/types/print_mail/order_profiles/cheque_retrieve_params.py @@ -2,12 +2,13 @@ from __future__ import annotations -from typing import List from typing_extensions import TypedDict +from ...._types import SequenceNotStr + __all__ = ["ChequeRetrieveParams"] class ChequeRetrieveParams(TypedDict, total=False): - expand: List[str] + expand: SequenceNotStr[str] """Optional list of related resources to expand in the response.""" diff --git a/src/postgrid/types/print_mail/order_profiles/cheque_update_params.py b/src/postgrid/types/print_mail/order_profiles/cheque_update_params.py index 9070c12..1793013 100644 --- a/src/postgrid/types/print_mail/order_profiles/cheque_update_params.py +++ b/src/postgrid/types/print_mail/order_profiles/cheque_update_params.py @@ -2,10 +2,10 @@ from __future__ import annotations -from typing import Dict, List, Union, Optional +from typing import Dict, Union, Optional from typing_extensions import Required, Annotated, TypedDict -from ...._types import Base64FileInput +from ...._types import SequenceNotStr, Base64FileInput from ...._utils import PropertyInfo from ..cheque_size import ChequeSize from .currency_code import CurrencyCode @@ -21,7 +21,7 @@ class ChequeUpdateParams(TypedDict, total=False): size: Required[ChequeSize] """Enum representing the supported cheque sizes.""" - expand: List[str] + expand: SequenceNotStr[str] """Optional list of related resources to expand in the response.""" currency_code: Annotated[CurrencyCode, PropertyInfo(alias="currencyCode")] diff --git a/src/postgrid/types/print_mail/order_profiles/letter_create_params.py b/src/postgrid/types/print_mail/order_profiles/letter_create_params.py index 8e49aa3..56ddeb8 100644 --- a/src/postgrid/types/print_mail/order_profiles/letter_create_params.py +++ b/src/postgrid/types/print_mail/order_profiles/letter_create_params.py @@ -2,9 +2,10 @@ from __future__ import annotations -from typing import Dict, List, Optional +from typing import Dict, Optional from typing_extensions import Literal, Required, Annotated, TypedDict +from ...._types import SequenceNotStr from ...._utils import PropertyInfo from ..letter_size import LetterSize from ..address_placement import AddressPlacement @@ -18,7 +19,7 @@ class LetterCreateParams(TypedDict, total=False): size: Required[LetterSize] """Enum representing the supported letter sizes.""" - expand: List[str] + expand: SequenceNotStr[str] """Optional list of related resources to expand in the response.""" address_placement: Annotated[AddressPlacement, PropertyInfo(alias="addressPlacement")] diff --git a/src/postgrid/types/print_mail/order_profiles/letter_retrieve_params.py b/src/postgrid/types/print_mail/order_profiles/letter_retrieve_params.py index f391450..b972d23 100644 --- a/src/postgrid/types/print_mail/order_profiles/letter_retrieve_params.py +++ b/src/postgrid/types/print_mail/order_profiles/letter_retrieve_params.py @@ -2,12 +2,13 @@ from __future__ import annotations -from typing import List from typing_extensions import TypedDict +from ...._types import SequenceNotStr + __all__ = ["LetterRetrieveParams"] class LetterRetrieveParams(TypedDict, total=False): - expand: List[str] + expand: SequenceNotStr[str] """Optional list of related resources to expand in the response.""" diff --git a/src/postgrid/types/print_mail/order_profiles/letter_update_params.py b/src/postgrid/types/print_mail/order_profiles/letter_update_params.py index 68c5cf4..46cc510 100644 --- a/src/postgrid/types/print_mail/order_profiles/letter_update_params.py +++ b/src/postgrid/types/print_mail/order_profiles/letter_update_params.py @@ -2,9 +2,10 @@ from __future__ import annotations -from typing import Dict, List, Optional +from typing import Dict, Optional from typing_extensions import Literal, Annotated, TypedDict +from ...._types import SequenceNotStr from ...._utils import PropertyInfo from ..address_placement import AddressPlacement from ..attached_pdf_param import AttachedPdfParam @@ -14,7 +15,7 @@ class LetterUpdateParams(TypedDict, total=False): - expand: List[str] + expand: SequenceNotStr[str] """Optional list of related resources to expand in the response.""" address_placement: Annotated[AddressPlacement, PropertyInfo(alias="addressPlacement")] diff --git a/src/postgrid/types/print_mail/order_profiles/postcard_create_params.py b/src/postgrid/types/print_mail/order_profiles/postcard_create_params.py index bc5cd8a..91e1c06 100644 --- a/src/postgrid/types/print_mail/order_profiles/postcard_create_params.py +++ b/src/postgrid/types/print_mail/order_profiles/postcard_create_params.py @@ -2,9 +2,10 @@ from __future__ import annotations -from typing import Dict, List, Optional +from typing import Dict, Optional from typing_extensions import Required, Annotated, TypedDict +from ...._types import SequenceNotStr from ...._utils import PropertyInfo from .postcard_size import PostcardSize from ..order_mailing_class import OrderMailingClass @@ -16,7 +17,7 @@ class PostcardCreateParams(TypedDict, total=False): size: Required[PostcardSize] """Enum representing the supported postcard sizes.""" - expand: List[str] + expand: SequenceNotStr[str] """Optional list of related resources to expand in the response.""" back_template: Annotated[str, PropertyInfo(alias="backTemplate")] diff --git a/src/postgrid/types/print_mail/order_profiles/postcard_retrieve_params.py b/src/postgrid/types/print_mail/order_profiles/postcard_retrieve_params.py index 6acb779..fd63330 100644 --- a/src/postgrid/types/print_mail/order_profiles/postcard_retrieve_params.py +++ b/src/postgrid/types/print_mail/order_profiles/postcard_retrieve_params.py @@ -2,12 +2,13 @@ from __future__ import annotations -from typing import List from typing_extensions import TypedDict +from ...._types import SequenceNotStr + __all__ = ["PostcardRetrieveParams"] class PostcardRetrieveParams(TypedDict, total=False): - expand: List[str] + expand: SequenceNotStr[str] """Optional list of related resources to expand in the response.""" diff --git a/src/postgrid/types/print_mail/order_profiles/postcard_update_params.py b/src/postgrid/types/print_mail/order_profiles/postcard_update_params.py index 0ba8187..32fc824 100644 --- a/src/postgrid/types/print_mail/order_profiles/postcard_update_params.py +++ b/src/postgrid/types/print_mail/order_profiles/postcard_update_params.py @@ -2,9 +2,10 @@ from __future__ import annotations -from typing import Dict, List, Optional +from typing import Dict, Optional from typing_extensions import Annotated, TypedDict +from ...._types import SequenceNotStr from ...._utils import PropertyInfo from ..order_mailing_class import OrderMailingClass @@ -12,7 +13,7 @@ class PostcardUpdateParams(TypedDict, total=False): - expand: List[str] + expand: SequenceNotStr[str] """Optional list of related resources to expand in the response.""" back_template: Annotated[str, PropertyInfo(alias="backTemplate")] diff --git a/src/postgrid/types/print_mail/order_profiles/self_mailer_create_params.py b/src/postgrid/types/print_mail/order_profiles/self_mailer_create_params.py index 347e49f..f979fb8 100644 --- a/src/postgrid/types/print_mail/order_profiles/self_mailer_create_params.py +++ b/src/postgrid/types/print_mail/order_profiles/self_mailer_create_params.py @@ -2,9 +2,10 @@ from __future__ import annotations -from typing import Dict, List, Optional +from typing import Dict, Optional from typing_extensions import Required, Annotated, TypedDict +from ...._types import SequenceNotStr from ...._utils import PropertyInfo from .self_mailer_size import SelfMailerSize from ..order_mailing_class import OrderMailingClass @@ -16,7 +17,7 @@ class SelfMailerCreateParams(TypedDict, total=False): size: Required[SelfMailerSize] """Enum representing the supported self-mailer sizes.""" - expand: List[str] + expand: SequenceNotStr[str] """Optional list of related resources to expand in the response.""" description: Optional[str] diff --git a/src/postgrid/types/print_mail/order_profiles/self_mailer_retrieve_params.py b/src/postgrid/types/print_mail/order_profiles/self_mailer_retrieve_params.py index 2ba7c3f..9f45b38 100644 --- a/src/postgrid/types/print_mail/order_profiles/self_mailer_retrieve_params.py +++ b/src/postgrid/types/print_mail/order_profiles/self_mailer_retrieve_params.py @@ -2,12 +2,13 @@ from __future__ import annotations -from typing import List from typing_extensions import TypedDict +from ...._types import SequenceNotStr + __all__ = ["SelfMailerRetrieveParams"] class SelfMailerRetrieveParams(TypedDict, total=False): - expand: List[str] + expand: SequenceNotStr[str] """Optional list of related resources to expand in the response.""" diff --git a/src/postgrid/types/print_mail/order_profiles/self_mailer_update_params.py b/src/postgrid/types/print_mail/order_profiles/self_mailer_update_params.py index 56141f6..f617a22 100644 --- a/src/postgrid/types/print_mail/order_profiles/self_mailer_update_params.py +++ b/src/postgrid/types/print_mail/order_profiles/self_mailer_update_params.py @@ -2,9 +2,10 @@ from __future__ import annotations -from typing import Dict, List, Optional +from typing import Dict, Optional from typing_extensions import Required, Annotated, TypedDict +from ...._types import SequenceNotStr from ...._utils import PropertyInfo from .self_mailer_size import SelfMailerSize from ..order_mailing_class import OrderMailingClass @@ -16,7 +17,7 @@ class SelfMailerUpdateParams(TypedDict, total=False): size: Required[SelfMailerSize] """Enum representing the supported self-mailer sizes.""" - expand: List[str] + expand: SequenceNotStr[str] """Optional list of related resources to expand in the response.""" description: Optional[str] diff --git a/src/postgrid/types/print_mail/postcard_retrieve_url_response.py b/src/postgrid/types/print_mail/postcard_retrieve_url_response.py index 6082563..af475a0 100644 --- a/src/postgrid/types/print_mail/postcard_retrieve_url_response.py +++ b/src/postgrid/types/print_mail/postcard_retrieve_url_response.py @@ -1,6 +1,5 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - from ..._models import BaseModel __all__ = ["PostcardRetrieveURLResponse"] diff --git a/src/postgrid/types/print_mail/report_sample_params.py b/src/postgrid/types/print_mail/report_sample_params.py index 13244bb..734276a 100644 --- a/src/postgrid/types/print_mail/report_sample_params.py +++ b/src/postgrid/types/print_mail/report_sample_params.py @@ -2,9 +2,9 @@ from __future__ import annotations -from typing import List from typing_extensions import Required, Annotated, TypedDict +from ..._types import SequenceNotStr from ..._utils import PropertyInfo __all__ = ["ReportSampleParams"] @@ -17,7 +17,7 @@ class ReportSampleParams(TypedDict, total=False): limit: int """Maximum number of rows to return in the sample.""" - params: List[str] + params: SequenceNotStr[str] """ Optional parameters to bind to the SQL query (e.g., for placeholders like ? or $1). diff --git a/src/postgrid/types/print_mail/reports/export_create_params.py b/src/postgrid/types/print_mail/reports/export_create_params.py index 5d11e8f..5b1c9fe 100644 --- a/src/postgrid/types/print_mail/reports/export_create_params.py +++ b/src/postgrid/types/print_mail/reports/export_create_params.py @@ -2,9 +2,11 @@ from __future__ import annotations -from typing import Dict, List +from typing import Dict from typing_extensions import TypedDict +from ...._types import SequenceNotStr + __all__ = ["ExportCreateParams"] @@ -15,5 +17,5 @@ class ExportCreateParams(TypedDict, total=False): metadata: Dict[str, str] """Optional key-value metadata associated with the export.""" - params: List[str] + params: SequenceNotStr[str] """Optional parameters to bind to the SQL query of the associated report.""" diff --git a/src/postgrid/types/print_mail/reports/sample_create_params.py b/src/postgrid/types/print_mail/reports/sample_create_params.py index 14f94ce..59a2136 100644 --- a/src/postgrid/types/print_mail/reports/sample_create_params.py +++ b/src/postgrid/types/print_mail/reports/sample_create_params.py @@ -2,9 +2,10 @@ from __future__ import annotations -from typing import List from typing_extensions import TypedDict +from ...._types import SequenceNotStr + __all__ = ["SampleCreateParams"] @@ -12,7 +13,7 @@ class SampleCreateParams(TypedDict, total=False): limit: int """Maximum number of rows to return in the sample.""" - params: List[str] + params: SequenceNotStr[str] """ Optional parameters to bind to the SQL query (e.g., for placeholders like ? or $1). diff --git a/src/postgrid/types/print_mail/self_mailer_retrieve_url_response.py b/src/postgrid/types/print_mail/self_mailer_retrieve_url_response.py index e5db83d..905c4ec 100644 --- a/src/postgrid/types/print_mail/self_mailer_retrieve_url_response.py +++ b/src/postgrid/types/print_mail/self_mailer_retrieve_url_response.py @@ -1,6 +1,5 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - from ..._models import BaseModel __all__ = ["SelfMailerRetrieveURLResponse"] diff --git a/src/postgrid/types/print_mail/verification_status_count.py b/src/postgrid/types/print_mail/verification_status_count.py index e97d994..f43afb5 100644 --- a/src/postgrid/types/print_mail/verification_status_count.py +++ b/src/postgrid/types/print_mail/verification_status_count.py @@ -1,6 +1,5 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - from pydantic import Field as FieldInfo from ..._models import BaseModel diff --git a/tests/api_resources/print_mail/order_profiles/test_cheques.py b/tests/api_resources/print_mail/order_profiles/test_cheques.py index 33f20e8..34f5ed4 100644 --- a/tests/api_resources/print_mail/order_profiles/test_cheques.py +++ b/tests/api_resources/print_mail/order_profiles/test_cheques.py @@ -22,7 +22,7 @@ class TestCheques: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create(self, client: PostGrid) -> None: cheque = client.print_mail.order_profiles.cheques.create( @@ -31,7 +31,7 @@ def test_method_create(self, client: PostGrid) -> None: ) assert_matches_type(ChequeProfile, cheque, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create_with_all_params(self, client: PostGrid) -> None: cheque = client.print_mail.order_profiles.cheques.create( @@ -51,7 +51,7 @@ def test_method_create_with_all_params(self, client: PostGrid) -> None: ) assert_matches_type(ChequeProfile, cheque, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create(self, client: PostGrid) -> None: response = client.print_mail.order_profiles.cheques.with_raw_response.create( @@ -64,7 +64,7 @@ def test_raw_response_create(self, client: PostGrid) -> None: cheque = response.parse() assert_matches_type(ChequeProfile, cheque, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create(self, client: PostGrid) -> None: with client.print_mail.order_profiles.cheques.with_streaming_response.create( @@ -79,7 +79,7 @@ def test_streaming_response_create(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_retrieve(self, client: PostGrid) -> None: cheque = client.print_mail.order_profiles.cheques.retrieve( @@ -87,7 +87,7 @@ def test_method_retrieve(self, client: PostGrid) -> None: ) assert_matches_type(ChequeProfile, cheque, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_retrieve_with_all_params(self, client: PostGrid) -> None: cheque = client.print_mail.order_profiles.cheques.retrieve( @@ -96,7 +96,7 @@ def test_method_retrieve_with_all_params(self, client: PostGrid) -> None: ) assert_matches_type(ChequeProfile, cheque, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_retrieve(self, client: PostGrid) -> None: response = client.print_mail.order_profiles.cheques.with_raw_response.retrieve( @@ -108,7 +108,7 @@ def test_raw_response_retrieve(self, client: PostGrid) -> None: cheque = response.parse() assert_matches_type(ChequeProfile, cheque, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_retrieve(self, client: PostGrid) -> None: with client.print_mail.order_profiles.cheques.with_streaming_response.retrieve( @@ -122,7 +122,7 @@ def test_streaming_response_retrieve(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_retrieve(self, client: PostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -130,7 +130,7 @@ def test_path_params_retrieve(self, client: PostGrid) -> None: id="", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_update(self, client: PostGrid) -> None: cheque = client.print_mail.order_profiles.cheques.update( @@ -140,7 +140,7 @@ def test_method_update(self, client: PostGrid) -> None: ) assert_matches_type(ChequeProfile, cheque, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_update_with_all_params(self, client: PostGrid) -> None: cheque = client.print_mail.order_profiles.cheques.update( @@ -161,7 +161,7 @@ def test_method_update_with_all_params(self, client: PostGrid) -> None: ) assert_matches_type(ChequeProfile, cheque, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_update(self, client: PostGrid) -> None: response = client.print_mail.order_profiles.cheques.with_raw_response.update( @@ -175,7 +175,7 @@ def test_raw_response_update(self, client: PostGrid) -> None: cheque = response.parse() assert_matches_type(ChequeProfile, cheque, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_update(self, client: PostGrid) -> None: with client.print_mail.order_profiles.cheques.with_streaming_response.update( @@ -191,7 +191,7 @@ def test_streaming_response_update(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_update(self, client: PostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -201,13 +201,13 @@ def test_path_params_update(self, client: PostGrid) -> None: size="us_letter", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list(self, client: PostGrid) -> None: cheque = client.print_mail.order_profiles.cheques.list() assert_matches_type(SyncSkipLimit[ChequeListResponse], cheque, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list_with_all_params(self, client: PostGrid) -> None: cheque = client.print_mail.order_profiles.cheques.list( @@ -217,7 +217,7 @@ def test_method_list_with_all_params(self, client: PostGrid) -> None: ) assert_matches_type(SyncSkipLimit[ChequeListResponse], cheque, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_list(self, client: PostGrid) -> None: response = client.print_mail.order_profiles.cheques.with_raw_response.list() @@ -227,7 +227,7 @@ def test_raw_response_list(self, client: PostGrid) -> None: cheque = response.parse() assert_matches_type(SyncSkipLimit[ChequeListResponse], cheque, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_list(self, client: PostGrid) -> None: with client.print_mail.order_profiles.cheques.with_streaming_response.list() as response: @@ -239,7 +239,7 @@ def test_streaming_response_list(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_delete(self, client: PostGrid) -> None: cheque = client.print_mail.order_profiles.cheques.delete( @@ -247,7 +247,7 @@ def test_method_delete(self, client: PostGrid) -> None: ) assert_matches_type(ChequeDeleteResponse, cheque, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_delete(self, client: PostGrid) -> None: response = client.print_mail.order_profiles.cheques.with_raw_response.delete( @@ -259,7 +259,7 @@ def test_raw_response_delete(self, client: PostGrid) -> None: cheque = response.parse() assert_matches_type(ChequeDeleteResponse, cheque, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_delete(self, client: PostGrid) -> None: with client.print_mail.order_profiles.cheques.with_streaming_response.delete( @@ -273,7 +273,7 @@ def test_streaming_response_delete(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_delete(self, client: PostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -283,9 +283,11 @@ def test_path_params_delete(self, client: PostGrid) -> None: class TestAsyncCheques: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create(self, async_client: AsyncPostGrid) -> None: cheque = await async_client.print_mail.order_profiles.cheques.create( @@ -294,7 +296,7 @@ async def test_method_create(self, async_client: AsyncPostGrid) -> None: ) assert_matches_type(ChequeProfile, cheque, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create_with_all_params(self, async_client: AsyncPostGrid) -> None: cheque = await async_client.print_mail.order_profiles.cheques.create( @@ -314,7 +316,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncPostGrid) ) assert_matches_type(ChequeProfile, cheque, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.order_profiles.cheques.with_raw_response.create( @@ -327,7 +329,7 @@ async def test_raw_response_create(self, async_client: AsyncPostGrid) -> None: cheque = await response.parse() assert_matches_type(ChequeProfile, cheque, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.order_profiles.cheques.with_streaming_response.create( @@ -342,7 +344,7 @@ async def test_streaming_response_create(self, async_client: AsyncPostGrid) -> N assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_retrieve(self, async_client: AsyncPostGrid) -> None: cheque = await async_client.print_mail.order_profiles.cheques.retrieve( @@ -350,7 +352,7 @@ async def test_method_retrieve(self, async_client: AsyncPostGrid) -> None: ) assert_matches_type(ChequeProfile, cheque, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_retrieve_with_all_params(self, async_client: AsyncPostGrid) -> None: cheque = await async_client.print_mail.order_profiles.cheques.retrieve( @@ -359,7 +361,7 @@ async def test_method_retrieve_with_all_params(self, async_client: AsyncPostGrid ) assert_matches_type(ChequeProfile, cheque, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_retrieve(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.order_profiles.cheques.with_raw_response.retrieve( @@ -371,7 +373,7 @@ async def test_raw_response_retrieve(self, async_client: AsyncPostGrid) -> None: cheque = await response.parse() assert_matches_type(ChequeProfile, cheque, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.order_profiles.cheques.with_streaming_response.retrieve( @@ -385,7 +387,7 @@ async def test_streaming_response_retrieve(self, async_client: AsyncPostGrid) -> assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_retrieve(self, async_client: AsyncPostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -393,7 +395,7 @@ async def test_path_params_retrieve(self, async_client: AsyncPostGrid) -> None: id="", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_update(self, async_client: AsyncPostGrid) -> None: cheque = await async_client.print_mail.order_profiles.cheques.update( @@ -403,7 +405,7 @@ async def test_method_update(self, async_client: AsyncPostGrid) -> None: ) assert_matches_type(ChequeProfile, cheque, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_update_with_all_params(self, async_client: AsyncPostGrid) -> None: cheque = await async_client.print_mail.order_profiles.cheques.update( @@ -424,7 +426,7 @@ async def test_method_update_with_all_params(self, async_client: AsyncPostGrid) ) assert_matches_type(ChequeProfile, cheque, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_update(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.order_profiles.cheques.with_raw_response.update( @@ -438,7 +440,7 @@ async def test_raw_response_update(self, async_client: AsyncPostGrid) -> None: cheque = await response.parse() assert_matches_type(ChequeProfile, cheque, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_update(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.order_profiles.cheques.with_streaming_response.update( @@ -454,7 +456,7 @@ async def test_streaming_response_update(self, async_client: AsyncPostGrid) -> N assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_update(self, async_client: AsyncPostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -464,13 +466,13 @@ async def test_path_params_update(self, async_client: AsyncPostGrid) -> None: size="us_letter", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list(self, async_client: AsyncPostGrid) -> None: cheque = await async_client.print_mail.order_profiles.cheques.list() assert_matches_type(AsyncSkipLimit[ChequeListResponse], cheque, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list_with_all_params(self, async_client: AsyncPostGrid) -> None: cheque = await async_client.print_mail.order_profiles.cheques.list( @@ -480,7 +482,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncPostGrid) -> ) assert_matches_type(AsyncSkipLimit[ChequeListResponse], cheque, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_list(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.order_profiles.cheques.with_raw_response.list() @@ -490,7 +492,7 @@ async def test_raw_response_list(self, async_client: AsyncPostGrid) -> None: cheque = await response.parse() assert_matches_type(AsyncSkipLimit[ChequeListResponse], cheque, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_list(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.order_profiles.cheques.with_streaming_response.list() as response: @@ -502,7 +504,7 @@ async def test_streaming_response_list(self, async_client: AsyncPostGrid) -> Non assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_delete(self, async_client: AsyncPostGrid) -> None: cheque = await async_client.print_mail.order_profiles.cheques.delete( @@ -510,7 +512,7 @@ async def test_method_delete(self, async_client: AsyncPostGrid) -> None: ) assert_matches_type(ChequeDeleteResponse, cheque, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_delete(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.order_profiles.cheques.with_raw_response.delete( @@ -522,7 +524,7 @@ async def test_raw_response_delete(self, async_client: AsyncPostGrid) -> None: cheque = await response.parse() assert_matches_type(ChequeDeleteResponse, cheque, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_delete(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.order_profiles.cheques.with_streaming_response.delete( @@ -536,7 +538,7 @@ async def test_streaming_response_delete(self, async_client: AsyncPostGrid) -> N assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_delete(self, async_client: AsyncPostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): diff --git a/tests/api_resources/print_mail/order_profiles/test_letters.py b/tests/api_resources/print_mail/order_profiles/test_letters.py index 19b9bed..31387d0 100644 --- a/tests/api_resources/print_mail/order_profiles/test_letters.py +++ b/tests/api_resources/print_mail/order_profiles/test_letters.py @@ -21,7 +21,7 @@ class TestLetters: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create(self, client: PostGrid) -> None: letter = client.print_mail.order_profiles.letters.create( @@ -29,7 +29,7 @@ def test_method_create(self, client: PostGrid) -> None: ) assert_matches_type(LetterProfile, letter, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create_with_all_params(self, client: PostGrid) -> None: letter = client.print_mail.order_profiles.letters.create( @@ -54,7 +54,7 @@ def test_method_create_with_all_params(self, client: PostGrid) -> None: ) assert_matches_type(LetterProfile, letter, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create(self, client: PostGrid) -> None: response = client.print_mail.order_profiles.letters.with_raw_response.create( @@ -66,7 +66,7 @@ def test_raw_response_create(self, client: PostGrid) -> None: letter = response.parse() assert_matches_type(LetterProfile, letter, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create(self, client: PostGrid) -> None: with client.print_mail.order_profiles.letters.with_streaming_response.create( @@ -80,7 +80,7 @@ def test_streaming_response_create(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_retrieve(self, client: PostGrid) -> None: letter = client.print_mail.order_profiles.letters.retrieve( @@ -88,7 +88,7 @@ def test_method_retrieve(self, client: PostGrid) -> None: ) assert_matches_type(LetterProfile, letter, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_retrieve_with_all_params(self, client: PostGrid) -> None: letter = client.print_mail.order_profiles.letters.retrieve( @@ -97,7 +97,7 @@ def test_method_retrieve_with_all_params(self, client: PostGrid) -> None: ) assert_matches_type(LetterProfile, letter, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_retrieve(self, client: PostGrid) -> None: response = client.print_mail.order_profiles.letters.with_raw_response.retrieve( @@ -109,7 +109,7 @@ def test_raw_response_retrieve(self, client: PostGrid) -> None: letter = response.parse() assert_matches_type(LetterProfile, letter, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_retrieve(self, client: PostGrid) -> None: with client.print_mail.order_profiles.letters.with_streaming_response.retrieve( @@ -123,7 +123,7 @@ def test_streaming_response_retrieve(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_retrieve(self, client: PostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -131,7 +131,7 @@ def test_path_params_retrieve(self, client: PostGrid) -> None: id="", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_update(self, client: PostGrid) -> None: letter = client.print_mail.order_profiles.letters.update( @@ -139,7 +139,7 @@ def test_method_update(self, client: PostGrid) -> None: ) assert_matches_type(LetterProfile, letter, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_update_with_all_params(self, client: PostGrid) -> None: letter = client.print_mail.order_profiles.letters.update( @@ -164,7 +164,7 @@ def test_method_update_with_all_params(self, client: PostGrid) -> None: ) assert_matches_type(LetterProfile, letter, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_update(self, client: PostGrid) -> None: response = client.print_mail.order_profiles.letters.with_raw_response.update( @@ -176,7 +176,7 @@ def test_raw_response_update(self, client: PostGrid) -> None: letter = response.parse() assert_matches_type(LetterProfile, letter, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_update(self, client: PostGrid) -> None: with client.print_mail.order_profiles.letters.with_streaming_response.update( @@ -190,7 +190,7 @@ def test_streaming_response_update(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_update(self, client: PostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -198,13 +198,13 @@ def test_path_params_update(self, client: PostGrid) -> None: id="", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list(self, client: PostGrid) -> None: letter = client.print_mail.order_profiles.letters.list() assert_matches_type(SyncSkipLimit[LetterProfile], letter, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list_with_all_params(self, client: PostGrid) -> None: letter = client.print_mail.order_profiles.letters.list( @@ -214,7 +214,7 @@ def test_method_list_with_all_params(self, client: PostGrid) -> None: ) assert_matches_type(SyncSkipLimit[LetterProfile], letter, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_list(self, client: PostGrid) -> None: response = client.print_mail.order_profiles.letters.with_raw_response.list() @@ -224,7 +224,7 @@ def test_raw_response_list(self, client: PostGrid) -> None: letter = response.parse() assert_matches_type(SyncSkipLimit[LetterProfile], letter, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_list(self, client: PostGrid) -> None: with client.print_mail.order_profiles.letters.with_streaming_response.list() as response: @@ -236,7 +236,7 @@ def test_streaming_response_list(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_delete(self, client: PostGrid) -> None: letter = client.print_mail.order_profiles.letters.delete( @@ -244,7 +244,7 @@ def test_method_delete(self, client: PostGrid) -> None: ) assert_matches_type(LetterDeleteResponse, letter, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_delete(self, client: PostGrid) -> None: response = client.print_mail.order_profiles.letters.with_raw_response.delete( @@ -256,7 +256,7 @@ def test_raw_response_delete(self, client: PostGrid) -> None: letter = response.parse() assert_matches_type(LetterDeleteResponse, letter, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_delete(self, client: PostGrid) -> None: with client.print_mail.order_profiles.letters.with_streaming_response.delete( @@ -270,7 +270,7 @@ def test_streaming_response_delete(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_delete(self, client: PostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -280,9 +280,11 @@ def test_path_params_delete(self, client: PostGrid) -> None: class TestAsyncLetters: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create(self, async_client: AsyncPostGrid) -> None: letter = await async_client.print_mail.order_profiles.letters.create( @@ -290,7 +292,7 @@ async def test_method_create(self, async_client: AsyncPostGrid) -> None: ) assert_matches_type(LetterProfile, letter, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create_with_all_params(self, async_client: AsyncPostGrid) -> None: letter = await async_client.print_mail.order_profiles.letters.create( @@ -315,7 +317,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncPostGrid) ) assert_matches_type(LetterProfile, letter, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.order_profiles.letters.with_raw_response.create( @@ -327,7 +329,7 @@ async def test_raw_response_create(self, async_client: AsyncPostGrid) -> None: letter = await response.parse() assert_matches_type(LetterProfile, letter, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.order_profiles.letters.with_streaming_response.create( @@ -341,7 +343,7 @@ async def test_streaming_response_create(self, async_client: AsyncPostGrid) -> N assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_retrieve(self, async_client: AsyncPostGrid) -> None: letter = await async_client.print_mail.order_profiles.letters.retrieve( @@ -349,7 +351,7 @@ async def test_method_retrieve(self, async_client: AsyncPostGrid) -> None: ) assert_matches_type(LetterProfile, letter, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_retrieve_with_all_params(self, async_client: AsyncPostGrid) -> None: letter = await async_client.print_mail.order_profiles.letters.retrieve( @@ -358,7 +360,7 @@ async def test_method_retrieve_with_all_params(self, async_client: AsyncPostGrid ) assert_matches_type(LetterProfile, letter, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_retrieve(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.order_profiles.letters.with_raw_response.retrieve( @@ -370,7 +372,7 @@ async def test_raw_response_retrieve(self, async_client: AsyncPostGrid) -> None: letter = await response.parse() assert_matches_type(LetterProfile, letter, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.order_profiles.letters.with_streaming_response.retrieve( @@ -384,7 +386,7 @@ async def test_streaming_response_retrieve(self, async_client: AsyncPostGrid) -> assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_retrieve(self, async_client: AsyncPostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -392,7 +394,7 @@ async def test_path_params_retrieve(self, async_client: AsyncPostGrid) -> None: id="", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_update(self, async_client: AsyncPostGrid) -> None: letter = await async_client.print_mail.order_profiles.letters.update( @@ -400,7 +402,7 @@ async def test_method_update(self, async_client: AsyncPostGrid) -> None: ) assert_matches_type(LetterProfile, letter, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_update_with_all_params(self, async_client: AsyncPostGrid) -> None: letter = await async_client.print_mail.order_profiles.letters.update( @@ -425,7 +427,7 @@ async def test_method_update_with_all_params(self, async_client: AsyncPostGrid) ) assert_matches_type(LetterProfile, letter, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_update(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.order_profiles.letters.with_raw_response.update( @@ -437,7 +439,7 @@ async def test_raw_response_update(self, async_client: AsyncPostGrid) -> None: letter = await response.parse() assert_matches_type(LetterProfile, letter, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_update(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.order_profiles.letters.with_streaming_response.update( @@ -451,7 +453,7 @@ async def test_streaming_response_update(self, async_client: AsyncPostGrid) -> N assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_update(self, async_client: AsyncPostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -459,13 +461,13 @@ async def test_path_params_update(self, async_client: AsyncPostGrid) -> None: id="", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list(self, async_client: AsyncPostGrid) -> None: letter = await async_client.print_mail.order_profiles.letters.list() assert_matches_type(AsyncSkipLimit[LetterProfile], letter, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list_with_all_params(self, async_client: AsyncPostGrid) -> None: letter = await async_client.print_mail.order_profiles.letters.list( @@ -475,7 +477,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncPostGrid) -> ) assert_matches_type(AsyncSkipLimit[LetterProfile], letter, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_list(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.order_profiles.letters.with_raw_response.list() @@ -485,7 +487,7 @@ async def test_raw_response_list(self, async_client: AsyncPostGrid) -> None: letter = await response.parse() assert_matches_type(AsyncSkipLimit[LetterProfile], letter, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_list(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.order_profiles.letters.with_streaming_response.list() as response: @@ -497,7 +499,7 @@ async def test_streaming_response_list(self, async_client: AsyncPostGrid) -> Non assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_delete(self, async_client: AsyncPostGrid) -> None: letter = await async_client.print_mail.order_profiles.letters.delete( @@ -505,7 +507,7 @@ async def test_method_delete(self, async_client: AsyncPostGrid) -> None: ) assert_matches_type(LetterDeleteResponse, letter, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_delete(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.order_profiles.letters.with_raw_response.delete( @@ -517,7 +519,7 @@ async def test_raw_response_delete(self, async_client: AsyncPostGrid) -> None: letter = await response.parse() assert_matches_type(LetterDeleteResponse, letter, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_delete(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.order_profiles.letters.with_streaming_response.delete( @@ -531,7 +533,7 @@ async def test_streaming_response_delete(self, async_client: AsyncPostGrid) -> N assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_delete(self, async_client: AsyncPostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): diff --git a/tests/api_resources/print_mail/order_profiles/test_postcards.py b/tests/api_resources/print_mail/order_profiles/test_postcards.py index 30ca7dc..98e2862 100644 --- a/tests/api_resources/print_mail/order_profiles/test_postcards.py +++ b/tests/api_resources/print_mail/order_profiles/test_postcards.py @@ -21,7 +21,7 @@ class TestPostcards: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create(self, client: PostGrid) -> None: postcard = client.print_mail.order_profiles.postcards.create( @@ -29,7 +29,7 @@ def test_method_create(self, client: PostGrid) -> None: ) assert_matches_type(PostcardProfile, postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create_with_all_params(self, client: PostGrid) -> None: postcard = client.print_mail.order_profiles.postcards.create( @@ -45,7 +45,7 @@ def test_method_create_with_all_params(self, client: PostGrid) -> None: ) assert_matches_type(PostcardProfile, postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create(self, client: PostGrid) -> None: response = client.print_mail.order_profiles.postcards.with_raw_response.create( @@ -57,7 +57,7 @@ def test_raw_response_create(self, client: PostGrid) -> None: postcard = response.parse() assert_matches_type(PostcardProfile, postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create(self, client: PostGrid) -> None: with client.print_mail.order_profiles.postcards.with_streaming_response.create( @@ -71,7 +71,7 @@ def test_streaming_response_create(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_retrieve(self, client: PostGrid) -> None: postcard = client.print_mail.order_profiles.postcards.retrieve( @@ -79,7 +79,7 @@ def test_method_retrieve(self, client: PostGrid) -> None: ) assert_matches_type(PostcardProfile, postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_retrieve_with_all_params(self, client: PostGrid) -> None: postcard = client.print_mail.order_profiles.postcards.retrieve( @@ -88,7 +88,7 @@ def test_method_retrieve_with_all_params(self, client: PostGrid) -> None: ) assert_matches_type(PostcardProfile, postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_retrieve(self, client: PostGrid) -> None: response = client.print_mail.order_profiles.postcards.with_raw_response.retrieve( @@ -100,7 +100,7 @@ def test_raw_response_retrieve(self, client: PostGrid) -> None: postcard = response.parse() assert_matches_type(PostcardProfile, postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_retrieve(self, client: PostGrid) -> None: with client.print_mail.order_profiles.postcards.with_streaming_response.retrieve( @@ -114,7 +114,7 @@ def test_streaming_response_retrieve(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_retrieve(self, client: PostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -122,7 +122,7 @@ def test_path_params_retrieve(self, client: PostGrid) -> None: id="", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_update(self, client: PostGrid) -> None: postcard = client.print_mail.order_profiles.postcards.update( @@ -130,7 +130,7 @@ def test_method_update(self, client: PostGrid) -> None: ) assert_matches_type(PostcardProfile, postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_update_with_all_params(self, client: PostGrid) -> None: postcard = client.print_mail.order_profiles.postcards.update( @@ -146,7 +146,7 @@ def test_method_update_with_all_params(self, client: PostGrid) -> None: ) assert_matches_type(PostcardProfile, postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_update(self, client: PostGrid) -> None: response = client.print_mail.order_profiles.postcards.with_raw_response.update( @@ -158,7 +158,7 @@ def test_raw_response_update(self, client: PostGrid) -> None: postcard = response.parse() assert_matches_type(PostcardProfile, postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_update(self, client: PostGrid) -> None: with client.print_mail.order_profiles.postcards.with_streaming_response.update( @@ -172,7 +172,7 @@ def test_streaming_response_update(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_update(self, client: PostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -180,13 +180,13 @@ def test_path_params_update(self, client: PostGrid) -> None: id="", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list(self, client: PostGrid) -> None: postcard = client.print_mail.order_profiles.postcards.list() assert_matches_type(SyncSkipLimit[PostcardProfile], postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list_with_all_params(self, client: PostGrid) -> None: postcard = client.print_mail.order_profiles.postcards.list( @@ -196,7 +196,7 @@ def test_method_list_with_all_params(self, client: PostGrid) -> None: ) assert_matches_type(SyncSkipLimit[PostcardProfile], postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_list(self, client: PostGrid) -> None: response = client.print_mail.order_profiles.postcards.with_raw_response.list() @@ -206,7 +206,7 @@ def test_raw_response_list(self, client: PostGrid) -> None: postcard = response.parse() assert_matches_type(SyncSkipLimit[PostcardProfile], postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_list(self, client: PostGrid) -> None: with client.print_mail.order_profiles.postcards.with_streaming_response.list() as response: @@ -218,7 +218,7 @@ def test_streaming_response_list(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_delete(self, client: PostGrid) -> None: postcard = client.print_mail.order_profiles.postcards.delete( @@ -226,7 +226,7 @@ def test_method_delete(self, client: PostGrid) -> None: ) assert_matches_type(PostcardDeleteResponse, postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_delete(self, client: PostGrid) -> None: response = client.print_mail.order_profiles.postcards.with_raw_response.delete( @@ -238,7 +238,7 @@ def test_raw_response_delete(self, client: PostGrid) -> None: postcard = response.parse() assert_matches_type(PostcardDeleteResponse, postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_delete(self, client: PostGrid) -> None: with client.print_mail.order_profiles.postcards.with_streaming_response.delete( @@ -252,7 +252,7 @@ def test_streaming_response_delete(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_delete(self, client: PostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -262,9 +262,11 @@ def test_path_params_delete(self, client: PostGrid) -> None: class TestAsyncPostcards: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create(self, async_client: AsyncPostGrid) -> None: postcard = await async_client.print_mail.order_profiles.postcards.create( @@ -272,7 +274,7 @@ async def test_method_create(self, async_client: AsyncPostGrid) -> None: ) assert_matches_type(PostcardProfile, postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create_with_all_params(self, async_client: AsyncPostGrid) -> None: postcard = await async_client.print_mail.order_profiles.postcards.create( @@ -288,7 +290,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncPostGrid) ) assert_matches_type(PostcardProfile, postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.order_profiles.postcards.with_raw_response.create( @@ -300,7 +302,7 @@ async def test_raw_response_create(self, async_client: AsyncPostGrid) -> None: postcard = await response.parse() assert_matches_type(PostcardProfile, postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.order_profiles.postcards.with_streaming_response.create( @@ -314,7 +316,7 @@ async def test_streaming_response_create(self, async_client: AsyncPostGrid) -> N assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_retrieve(self, async_client: AsyncPostGrid) -> None: postcard = await async_client.print_mail.order_profiles.postcards.retrieve( @@ -322,7 +324,7 @@ async def test_method_retrieve(self, async_client: AsyncPostGrid) -> None: ) assert_matches_type(PostcardProfile, postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_retrieve_with_all_params(self, async_client: AsyncPostGrid) -> None: postcard = await async_client.print_mail.order_profiles.postcards.retrieve( @@ -331,7 +333,7 @@ async def test_method_retrieve_with_all_params(self, async_client: AsyncPostGrid ) assert_matches_type(PostcardProfile, postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_retrieve(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.order_profiles.postcards.with_raw_response.retrieve( @@ -343,7 +345,7 @@ async def test_raw_response_retrieve(self, async_client: AsyncPostGrid) -> None: postcard = await response.parse() assert_matches_type(PostcardProfile, postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.order_profiles.postcards.with_streaming_response.retrieve( @@ -357,7 +359,7 @@ async def test_streaming_response_retrieve(self, async_client: AsyncPostGrid) -> assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_retrieve(self, async_client: AsyncPostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -365,7 +367,7 @@ async def test_path_params_retrieve(self, async_client: AsyncPostGrid) -> None: id="", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_update(self, async_client: AsyncPostGrid) -> None: postcard = await async_client.print_mail.order_profiles.postcards.update( @@ -373,7 +375,7 @@ async def test_method_update(self, async_client: AsyncPostGrid) -> None: ) assert_matches_type(PostcardProfile, postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_update_with_all_params(self, async_client: AsyncPostGrid) -> None: postcard = await async_client.print_mail.order_profiles.postcards.update( @@ -389,7 +391,7 @@ async def test_method_update_with_all_params(self, async_client: AsyncPostGrid) ) assert_matches_type(PostcardProfile, postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_update(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.order_profiles.postcards.with_raw_response.update( @@ -401,7 +403,7 @@ async def test_raw_response_update(self, async_client: AsyncPostGrid) -> None: postcard = await response.parse() assert_matches_type(PostcardProfile, postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_update(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.order_profiles.postcards.with_streaming_response.update( @@ -415,7 +417,7 @@ async def test_streaming_response_update(self, async_client: AsyncPostGrid) -> N assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_update(self, async_client: AsyncPostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -423,13 +425,13 @@ async def test_path_params_update(self, async_client: AsyncPostGrid) -> None: id="", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list(self, async_client: AsyncPostGrid) -> None: postcard = await async_client.print_mail.order_profiles.postcards.list() assert_matches_type(AsyncSkipLimit[PostcardProfile], postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list_with_all_params(self, async_client: AsyncPostGrid) -> None: postcard = await async_client.print_mail.order_profiles.postcards.list( @@ -439,7 +441,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncPostGrid) -> ) assert_matches_type(AsyncSkipLimit[PostcardProfile], postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_list(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.order_profiles.postcards.with_raw_response.list() @@ -449,7 +451,7 @@ async def test_raw_response_list(self, async_client: AsyncPostGrid) -> None: postcard = await response.parse() assert_matches_type(AsyncSkipLimit[PostcardProfile], postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_list(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.order_profiles.postcards.with_streaming_response.list() as response: @@ -461,7 +463,7 @@ async def test_streaming_response_list(self, async_client: AsyncPostGrid) -> Non assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_delete(self, async_client: AsyncPostGrid) -> None: postcard = await async_client.print_mail.order_profiles.postcards.delete( @@ -469,7 +471,7 @@ async def test_method_delete(self, async_client: AsyncPostGrid) -> None: ) assert_matches_type(PostcardDeleteResponse, postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_delete(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.order_profiles.postcards.with_raw_response.delete( @@ -481,7 +483,7 @@ async def test_raw_response_delete(self, async_client: AsyncPostGrid) -> None: postcard = await response.parse() assert_matches_type(PostcardDeleteResponse, postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_delete(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.order_profiles.postcards.with_streaming_response.delete( @@ -495,7 +497,7 @@ async def test_streaming_response_delete(self, async_client: AsyncPostGrid) -> N assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_delete(self, async_client: AsyncPostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): diff --git a/tests/api_resources/print_mail/order_profiles/test_self_mailers.py b/tests/api_resources/print_mail/order_profiles/test_self_mailers.py index dcf58ec..006acbf 100644 --- a/tests/api_resources/print_mail/order_profiles/test_self_mailers.py +++ b/tests/api_resources/print_mail/order_profiles/test_self_mailers.py @@ -21,7 +21,7 @@ class TestSelfMailers: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create(self, client: PostGrid) -> None: self_mailer = client.print_mail.order_profiles.self_mailers.create( @@ -29,7 +29,7 @@ def test_method_create(self, client: PostGrid) -> None: ) assert_matches_type(SelfMailerProfile, self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create_with_all_params(self, client: PostGrid) -> None: self_mailer = client.print_mail.order_profiles.self_mailers.create( @@ -45,7 +45,7 @@ def test_method_create_with_all_params(self, client: PostGrid) -> None: ) assert_matches_type(SelfMailerProfile, self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create(self, client: PostGrid) -> None: response = client.print_mail.order_profiles.self_mailers.with_raw_response.create( @@ -57,7 +57,7 @@ def test_raw_response_create(self, client: PostGrid) -> None: self_mailer = response.parse() assert_matches_type(SelfMailerProfile, self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create(self, client: PostGrid) -> None: with client.print_mail.order_profiles.self_mailers.with_streaming_response.create( @@ -71,7 +71,7 @@ def test_streaming_response_create(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_retrieve(self, client: PostGrid) -> None: self_mailer = client.print_mail.order_profiles.self_mailers.retrieve( @@ -79,7 +79,7 @@ def test_method_retrieve(self, client: PostGrid) -> None: ) assert_matches_type(SelfMailerProfile, self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_retrieve_with_all_params(self, client: PostGrid) -> None: self_mailer = client.print_mail.order_profiles.self_mailers.retrieve( @@ -88,7 +88,7 @@ def test_method_retrieve_with_all_params(self, client: PostGrid) -> None: ) assert_matches_type(SelfMailerProfile, self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_retrieve(self, client: PostGrid) -> None: response = client.print_mail.order_profiles.self_mailers.with_raw_response.retrieve( @@ -100,7 +100,7 @@ def test_raw_response_retrieve(self, client: PostGrid) -> None: self_mailer = response.parse() assert_matches_type(SelfMailerProfile, self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_retrieve(self, client: PostGrid) -> None: with client.print_mail.order_profiles.self_mailers.with_streaming_response.retrieve( @@ -114,7 +114,7 @@ def test_streaming_response_retrieve(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_retrieve(self, client: PostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -122,7 +122,7 @@ def test_path_params_retrieve(self, client: PostGrid) -> None: id="", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_update(self, client: PostGrid) -> None: self_mailer = client.print_mail.order_profiles.self_mailers.update( @@ -131,7 +131,7 @@ def test_method_update(self, client: PostGrid) -> None: ) assert_matches_type(SelfMailerProfile, self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_update_with_all_params(self, client: PostGrid) -> None: self_mailer = client.print_mail.order_profiles.self_mailers.update( @@ -148,7 +148,7 @@ def test_method_update_with_all_params(self, client: PostGrid) -> None: ) assert_matches_type(SelfMailerProfile, self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_update(self, client: PostGrid) -> None: response = client.print_mail.order_profiles.self_mailers.with_raw_response.update( @@ -161,7 +161,7 @@ def test_raw_response_update(self, client: PostGrid) -> None: self_mailer = response.parse() assert_matches_type(SelfMailerProfile, self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_update(self, client: PostGrid) -> None: with client.print_mail.order_profiles.self_mailers.with_streaming_response.update( @@ -176,7 +176,7 @@ def test_streaming_response_update(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_update(self, client: PostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -185,13 +185,13 @@ def test_path_params_update(self, client: PostGrid) -> None: size="8.5x11_bifold", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list(self, client: PostGrid) -> None: self_mailer = client.print_mail.order_profiles.self_mailers.list() assert_matches_type(SyncSkipLimit[SelfMailerProfile], self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list_with_all_params(self, client: PostGrid) -> None: self_mailer = client.print_mail.order_profiles.self_mailers.list( @@ -201,7 +201,7 @@ def test_method_list_with_all_params(self, client: PostGrid) -> None: ) assert_matches_type(SyncSkipLimit[SelfMailerProfile], self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_list(self, client: PostGrid) -> None: response = client.print_mail.order_profiles.self_mailers.with_raw_response.list() @@ -211,7 +211,7 @@ def test_raw_response_list(self, client: PostGrid) -> None: self_mailer = response.parse() assert_matches_type(SyncSkipLimit[SelfMailerProfile], self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_list(self, client: PostGrid) -> None: with client.print_mail.order_profiles.self_mailers.with_streaming_response.list() as response: @@ -223,7 +223,7 @@ def test_streaming_response_list(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_delete(self, client: PostGrid) -> None: self_mailer = client.print_mail.order_profiles.self_mailers.delete( @@ -231,7 +231,7 @@ def test_method_delete(self, client: PostGrid) -> None: ) assert_matches_type(SelfMailerDeleteResponse, self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_delete(self, client: PostGrid) -> None: response = client.print_mail.order_profiles.self_mailers.with_raw_response.delete( @@ -243,7 +243,7 @@ def test_raw_response_delete(self, client: PostGrid) -> None: self_mailer = response.parse() assert_matches_type(SelfMailerDeleteResponse, self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_delete(self, client: PostGrid) -> None: with client.print_mail.order_profiles.self_mailers.with_streaming_response.delete( @@ -257,7 +257,7 @@ def test_streaming_response_delete(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_delete(self, client: PostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -267,9 +267,11 @@ def test_path_params_delete(self, client: PostGrid) -> None: class TestAsyncSelfMailers: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create(self, async_client: AsyncPostGrid) -> None: self_mailer = await async_client.print_mail.order_profiles.self_mailers.create( @@ -277,7 +279,7 @@ async def test_method_create(self, async_client: AsyncPostGrid) -> None: ) assert_matches_type(SelfMailerProfile, self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create_with_all_params(self, async_client: AsyncPostGrid) -> None: self_mailer = await async_client.print_mail.order_profiles.self_mailers.create( @@ -293,7 +295,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncPostGrid) ) assert_matches_type(SelfMailerProfile, self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.order_profiles.self_mailers.with_raw_response.create( @@ -305,7 +307,7 @@ async def test_raw_response_create(self, async_client: AsyncPostGrid) -> None: self_mailer = await response.parse() assert_matches_type(SelfMailerProfile, self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.order_profiles.self_mailers.with_streaming_response.create( @@ -319,7 +321,7 @@ async def test_streaming_response_create(self, async_client: AsyncPostGrid) -> N assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_retrieve(self, async_client: AsyncPostGrid) -> None: self_mailer = await async_client.print_mail.order_profiles.self_mailers.retrieve( @@ -327,7 +329,7 @@ async def test_method_retrieve(self, async_client: AsyncPostGrid) -> None: ) assert_matches_type(SelfMailerProfile, self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_retrieve_with_all_params(self, async_client: AsyncPostGrid) -> None: self_mailer = await async_client.print_mail.order_profiles.self_mailers.retrieve( @@ -336,7 +338,7 @@ async def test_method_retrieve_with_all_params(self, async_client: AsyncPostGrid ) assert_matches_type(SelfMailerProfile, self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_retrieve(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.order_profiles.self_mailers.with_raw_response.retrieve( @@ -348,7 +350,7 @@ async def test_raw_response_retrieve(self, async_client: AsyncPostGrid) -> None: self_mailer = await response.parse() assert_matches_type(SelfMailerProfile, self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.order_profiles.self_mailers.with_streaming_response.retrieve( @@ -362,7 +364,7 @@ async def test_streaming_response_retrieve(self, async_client: AsyncPostGrid) -> assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_retrieve(self, async_client: AsyncPostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -370,7 +372,7 @@ async def test_path_params_retrieve(self, async_client: AsyncPostGrid) -> None: id="", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_update(self, async_client: AsyncPostGrid) -> None: self_mailer = await async_client.print_mail.order_profiles.self_mailers.update( @@ -379,7 +381,7 @@ async def test_method_update(self, async_client: AsyncPostGrid) -> None: ) assert_matches_type(SelfMailerProfile, self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_update_with_all_params(self, async_client: AsyncPostGrid) -> None: self_mailer = await async_client.print_mail.order_profiles.self_mailers.update( @@ -396,7 +398,7 @@ async def test_method_update_with_all_params(self, async_client: AsyncPostGrid) ) assert_matches_type(SelfMailerProfile, self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_update(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.order_profiles.self_mailers.with_raw_response.update( @@ -409,7 +411,7 @@ async def test_raw_response_update(self, async_client: AsyncPostGrid) -> None: self_mailer = await response.parse() assert_matches_type(SelfMailerProfile, self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_update(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.order_profiles.self_mailers.with_streaming_response.update( @@ -424,7 +426,7 @@ async def test_streaming_response_update(self, async_client: AsyncPostGrid) -> N assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_update(self, async_client: AsyncPostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -433,13 +435,13 @@ async def test_path_params_update(self, async_client: AsyncPostGrid) -> None: size="8.5x11_bifold", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list(self, async_client: AsyncPostGrid) -> None: self_mailer = await async_client.print_mail.order_profiles.self_mailers.list() assert_matches_type(AsyncSkipLimit[SelfMailerProfile], self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list_with_all_params(self, async_client: AsyncPostGrid) -> None: self_mailer = await async_client.print_mail.order_profiles.self_mailers.list( @@ -449,7 +451,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncPostGrid) -> ) assert_matches_type(AsyncSkipLimit[SelfMailerProfile], self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_list(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.order_profiles.self_mailers.with_raw_response.list() @@ -459,7 +461,7 @@ async def test_raw_response_list(self, async_client: AsyncPostGrid) -> None: self_mailer = await response.parse() assert_matches_type(AsyncSkipLimit[SelfMailerProfile], self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_list(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.order_profiles.self_mailers.with_streaming_response.list() as response: @@ -471,7 +473,7 @@ async def test_streaming_response_list(self, async_client: AsyncPostGrid) -> Non assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_delete(self, async_client: AsyncPostGrid) -> None: self_mailer = await async_client.print_mail.order_profiles.self_mailers.delete( @@ -479,7 +481,7 @@ async def test_method_delete(self, async_client: AsyncPostGrid) -> None: ) assert_matches_type(SelfMailerDeleteResponse, self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_delete(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.order_profiles.self_mailers.with_raw_response.delete( @@ -491,7 +493,7 @@ async def test_raw_response_delete(self, async_client: AsyncPostGrid) -> None: self_mailer = await response.parse() assert_matches_type(SelfMailerDeleteResponse, self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_delete(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.order_profiles.self_mailers.with_streaming_response.delete( @@ -505,7 +507,7 @@ async def test_streaming_response_delete(self, async_client: AsyncPostGrid) -> N assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_delete(self, async_client: AsyncPostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): diff --git a/tests/api_resources/print_mail/reports/test_exports.py b/tests/api_resources/print_mail/reports/test_exports.py index 8ec95d4..2b22d9b 100644 --- a/tests/api_resources/print_mail/reports/test_exports.py +++ b/tests/api_resources/print_mail/reports/test_exports.py @@ -18,7 +18,7 @@ class TestExports: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create(self, client: PostGrid) -> None: export = client.print_mail.reports.exports.create( @@ -26,7 +26,7 @@ def test_method_create(self, client: PostGrid) -> None: ) assert_matches_type(ReportExport, export, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create_with_all_params(self, client: PostGrid) -> None: export = client.print_mail.reports.exports.create( @@ -37,7 +37,7 @@ def test_method_create_with_all_params(self, client: PostGrid) -> None: ) assert_matches_type(ReportExport, export, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create(self, client: PostGrid) -> None: response = client.print_mail.reports.exports.with_raw_response.create( @@ -49,7 +49,7 @@ def test_raw_response_create(self, client: PostGrid) -> None: export = response.parse() assert_matches_type(ReportExport, export, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create(self, client: PostGrid) -> None: with client.print_mail.reports.exports.with_streaming_response.create( @@ -63,7 +63,7 @@ def test_streaming_response_create(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_create(self, client: PostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `report_id` but received ''"): @@ -71,7 +71,7 @@ def test_path_params_create(self, client: PostGrid) -> None: report_id="", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_retrieve(self, client: PostGrid) -> None: export = client.print_mail.reports.exports.retrieve( @@ -80,7 +80,7 @@ def test_method_retrieve(self, client: PostGrid) -> None: ) assert_matches_type(ReportExport, export, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_retrieve(self, client: PostGrid) -> None: response = client.print_mail.reports.exports.with_raw_response.retrieve( @@ -93,7 +93,7 @@ def test_raw_response_retrieve(self, client: PostGrid) -> None: export = response.parse() assert_matches_type(ReportExport, export, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_retrieve(self, client: PostGrid) -> None: with client.print_mail.reports.exports.with_streaming_response.retrieve( @@ -108,7 +108,7 @@ def test_streaming_response_retrieve(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_retrieve(self, client: PostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `report_id` but received ''"): @@ -123,7 +123,7 @@ def test_path_params_retrieve(self, client: PostGrid) -> None: report_id="reportID", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_delete(self, client: PostGrid) -> None: export = client.print_mail.reports.exports.delete( @@ -132,7 +132,7 @@ def test_method_delete(self, client: PostGrid) -> None: ) assert_matches_type(DeletedResponse, export, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_delete(self, client: PostGrid) -> None: response = client.print_mail.reports.exports.with_raw_response.delete( @@ -145,7 +145,7 @@ def test_raw_response_delete(self, client: PostGrid) -> None: export = response.parse() assert_matches_type(DeletedResponse, export, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_delete(self, client: PostGrid) -> None: with client.print_mail.reports.exports.with_streaming_response.delete( @@ -160,7 +160,7 @@ def test_streaming_response_delete(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_delete(self, client: PostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `report_id` but received ''"): @@ -177,9 +177,11 @@ def test_path_params_delete(self, client: PostGrid) -> None: class TestAsyncExports: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create(self, async_client: AsyncPostGrid) -> None: export = await async_client.print_mail.reports.exports.create( @@ -187,7 +189,7 @@ async def test_method_create(self, async_client: AsyncPostGrid) -> None: ) assert_matches_type(ReportExport, export, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create_with_all_params(self, async_client: AsyncPostGrid) -> None: export = await async_client.print_mail.reports.exports.create( @@ -198,7 +200,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncPostGrid) ) assert_matches_type(ReportExport, export, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.reports.exports.with_raw_response.create( @@ -210,7 +212,7 @@ async def test_raw_response_create(self, async_client: AsyncPostGrid) -> None: export = await response.parse() assert_matches_type(ReportExport, export, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.reports.exports.with_streaming_response.create( @@ -224,7 +226,7 @@ async def test_streaming_response_create(self, async_client: AsyncPostGrid) -> N assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_create(self, async_client: AsyncPostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `report_id` but received ''"): @@ -232,7 +234,7 @@ async def test_path_params_create(self, async_client: AsyncPostGrid) -> None: report_id="", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_retrieve(self, async_client: AsyncPostGrid) -> None: export = await async_client.print_mail.reports.exports.retrieve( @@ -241,7 +243,7 @@ async def test_method_retrieve(self, async_client: AsyncPostGrid) -> None: ) assert_matches_type(ReportExport, export, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_retrieve(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.reports.exports.with_raw_response.retrieve( @@ -254,7 +256,7 @@ async def test_raw_response_retrieve(self, async_client: AsyncPostGrid) -> None: export = await response.parse() assert_matches_type(ReportExport, export, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.reports.exports.with_streaming_response.retrieve( @@ -269,7 +271,7 @@ async def test_streaming_response_retrieve(self, async_client: AsyncPostGrid) -> assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_retrieve(self, async_client: AsyncPostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `report_id` but received ''"): @@ -284,7 +286,7 @@ async def test_path_params_retrieve(self, async_client: AsyncPostGrid) -> None: report_id="reportID", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_delete(self, async_client: AsyncPostGrid) -> None: export = await async_client.print_mail.reports.exports.delete( @@ -293,7 +295,7 @@ async def test_method_delete(self, async_client: AsyncPostGrid) -> None: ) assert_matches_type(DeletedResponse, export, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_delete(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.reports.exports.with_raw_response.delete( @@ -306,7 +308,7 @@ async def test_raw_response_delete(self, async_client: AsyncPostGrid) -> None: export = await response.parse() assert_matches_type(DeletedResponse, export, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_delete(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.reports.exports.with_streaming_response.delete( @@ -321,7 +323,7 @@ async def test_streaming_response_delete(self, async_client: AsyncPostGrid) -> N assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_delete(self, async_client: AsyncPostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `report_id` but received ''"): diff --git a/tests/api_resources/print_mail/reports/test_samples.py b/tests/api_resources/print_mail/reports/test_samples.py index 4d9cb1c..50be716 100644 --- a/tests/api_resources/print_mail/reports/test_samples.py +++ b/tests/api_resources/print_mail/reports/test_samples.py @@ -17,7 +17,7 @@ class TestSamples: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create(self, client: PostGrid) -> None: sample = client.print_mail.reports.samples.create( @@ -25,7 +25,7 @@ def test_method_create(self, client: PostGrid) -> None: ) assert_matches_type(ReportSample, sample, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create_with_all_params(self, client: PostGrid) -> None: sample = client.print_mail.reports.samples.create( @@ -35,7 +35,7 @@ def test_method_create_with_all_params(self, client: PostGrid) -> None: ) assert_matches_type(ReportSample, sample, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create(self, client: PostGrid) -> None: response = client.print_mail.reports.samples.with_raw_response.create( @@ -47,7 +47,7 @@ def test_raw_response_create(self, client: PostGrid) -> None: sample = response.parse() assert_matches_type(ReportSample, sample, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create(self, client: PostGrid) -> None: with client.print_mail.reports.samples.with_streaming_response.create( @@ -61,7 +61,7 @@ def test_streaming_response_create(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_create(self, client: PostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -71,9 +71,11 @@ def test_path_params_create(self, client: PostGrid) -> None: class TestAsyncSamples: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create(self, async_client: AsyncPostGrid) -> None: sample = await async_client.print_mail.reports.samples.create( @@ -81,7 +83,7 @@ async def test_method_create(self, async_client: AsyncPostGrid) -> None: ) assert_matches_type(ReportSample, sample, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create_with_all_params(self, async_client: AsyncPostGrid) -> None: sample = await async_client.print_mail.reports.samples.create( @@ -91,7 +93,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncPostGrid) ) assert_matches_type(ReportSample, sample, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.reports.samples.with_raw_response.create( @@ -103,7 +105,7 @@ async def test_raw_response_create(self, async_client: AsyncPostGrid) -> None: sample = await response.parse() assert_matches_type(ReportSample, sample, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.reports.samples.with_streaming_response.create( @@ -117,7 +119,7 @@ async def test_streaming_response_create(self, async_client: AsyncPostGrid) -> N assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_create(self, async_client: AsyncPostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): diff --git a/tests/api_resources/print_mail/test_bank_accounts.py b/tests/api_resources/print_mail/test_bank_accounts.py index 1d2319a..1cff59f 100644 --- a/tests/api_resources/print_mail/test_bank_accounts.py +++ b/tests/api_resources/print_mail/test_bank_accounts.py @@ -21,7 +21,7 @@ class TestBankAccounts: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create_overload_1(self, client: PostGrid) -> None: bank_account = client.print_mail.bank_accounts.create( @@ -32,7 +32,7 @@ def test_method_create_overload_1(self, client: PostGrid) -> None: ) assert_matches_type(BankAccount, bank_account, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create_with_all_params_overload_1(self, client: PostGrid) -> None: bank_account = client.print_mail.bank_accounts.create( @@ -51,7 +51,7 @@ def test_method_create_with_all_params_overload_1(self, client: PostGrid) -> Non ) assert_matches_type(BankAccount, bank_account, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create_overload_1(self, client: PostGrid) -> None: response = client.print_mail.bank_accounts.with_raw_response.create( @@ -66,7 +66,7 @@ def test_raw_response_create_overload_1(self, client: PostGrid) -> None: bank_account = response.parse() assert_matches_type(BankAccount, bank_account, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create_overload_1(self, client: PostGrid) -> None: with client.print_mail.bank_accounts.with_streaming_response.create( @@ -83,7 +83,7 @@ def test_streaming_response_create_overload_1(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create_overload_2(self, client: PostGrid) -> None: bank_account = client.print_mail.bank_accounts.create( @@ -94,7 +94,7 @@ def test_method_create_overload_2(self, client: PostGrid) -> None: ) assert_matches_type(BankAccount, bank_account, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create_with_all_params_overload_2(self, client: PostGrid) -> None: bank_account = client.print_mail.bank_accounts.create( @@ -113,7 +113,7 @@ def test_method_create_with_all_params_overload_2(self, client: PostGrid) -> Non ) assert_matches_type(BankAccount, bank_account, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create_overload_2(self, client: PostGrid) -> None: response = client.print_mail.bank_accounts.with_raw_response.create( @@ -128,7 +128,7 @@ def test_raw_response_create_overload_2(self, client: PostGrid) -> None: bank_account = response.parse() assert_matches_type(BankAccount, bank_account, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create_overload_2(self, client: PostGrid) -> None: with client.print_mail.bank_accounts.with_streaming_response.create( @@ -145,7 +145,7 @@ def test_streaming_response_create_overload_2(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create_overload_3(self, client: PostGrid) -> None: bank_account = client.print_mail.bank_accounts.create( @@ -156,7 +156,7 @@ def test_method_create_overload_3(self, client: PostGrid) -> None: ) assert_matches_type(BankAccount, bank_account, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create_with_all_params_overload_3(self, client: PostGrid) -> None: bank_account = client.print_mail.bank_accounts.create( @@ -175,7 +175,7 @@ def test_method_create_with_all_params_overload_3(self, client: PostGrid) -> Non ) assert_matches_type(BankAccount, bank_account, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create_overload_3(self, client: PostGrid) -> None: response = client.print_mail.bank_accounts.with_raw_response.create( @@ -190,7 +190,7 @@ def test_raw_response_create_overload_3(self, client: PostGrid) -> None: bank_account = response.parse() assert_matches_type(BankAccount, bank_account, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create_overload_3(self, client: PostGrid) -> None: with client.print_mail.bank_accounts.with_streaming_response.create( @@ -207,7 +207,7 @@ def test_streaming_response_create_overload_3(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_retrieve(self, client: PostGrid) -> None: bank_account = client.print_mail.bank_accounts.retrieve( @@ -215,7 +215,7 @@ def test_method_retrieve(self, client: PostGrid) -> None: ) assert_matches_type(BankAccount, bank_account, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_retrieve(self, client: PostGrid) -> None: response = client.print_mail.bank_accounts.with_raw_response.retrieve( @@ -227,7 +227,7 @@ def test_raw_response_retrieve(self, client: PostGrid) -> None: bank_account = response.parse() assert_matches_type(BankAccount, bank_account, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_retrieve(self, client: PostGrid) -> None: with client.print_mail.bank_accounts.with_streaming_response.retrieve( @@ -241,7 +241,7 @@ def test_streaming_response_retrieve(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_retrieve(self, client: PostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -249,13 +249,13 @@ def test_path_params_retrieve(self, client: PostGrid) -> None: "", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list(self, client: PostGrid) -> None: bank_account = client.print_mail.bank_accounts.list() assert_matches_type(SyncSkipLimit[BankAccount], bank_account, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list_with_all_params(self, client: PostGrid) -> None: bank_account = client.print_mail.bank_accounts.list( @@ -265,7 +265,7 @@ def test_method_list_with_all_params(self, client: PostGrid) -> None: ) assert_matches_type(SyncSkipLimit[BankAccount], bank_account, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_list(self, client: PostGrid) -> None: response = client.print_mail.bank_accounts.with_raw_response.list() @@ -275,7 +275,7 @@ def test_raw_response_list(self, client: PostGrid) -> None: bank_account = response.parse() assert_matches_type(SyncSkipLimit[BankAccount], bank_account, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_list(self, client: PostGrid) -> None: with client.print_mail.bank_accounts.with_streaming_response.list() as response: @@ -287,7 +287,7 @@ def test_streaming_response_list(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_delete(self, client: PostGrid) -> None: bank_account = client.print_mail.bank_accounts.delete( @@ -295,7 +295,7 @@ def test_method_delete(self, client: PostGrid) -> None: ) assert_matches_type(BankAccountDeleteResponse, bank_account, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_delete(self, client: PostGrid) -> None: response = client.print_mail.bank_accounts.with_raw_response.delete( @@ -307,7 +307,7 @@ def test_raw_response_delete(self, client: PostGrid) -> None: bank_account = response.parse() assert_matches_type(BankAccountDeleteResponse, bank_account, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_delete(self, client: PostGrid) -> None: with client.print_mail.bank_accounts.with_streaming_response.delete( @@ -321,7 +321,7 @@ def test_streaming_response_delete(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_delete(self, client: PostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -331,9 +331,11 @@ def test_path_params_delete(self, client: PostGrid) -> None: class TestAsyncBankAccounts: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create_overload_1(self, async_client: AsyncPostGrid) -> None: bank_account = await async_client.print_mail.bank_accounts.create( @@ -344,7 +346,7 @@ async def test_method_create_overload_1(self, async_client: AsyncPostGrid) -> No ) assert_matches_type(BankAccount, bank_account, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create_with_all_params_overload_1(self, async_client: AsyncPostGrid) -> None: bank_account = await async_client.print_mail.bank_accounts.create( @@ -363,7 +365,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn ) assert_matches_type(BankAccount, bank_account, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create_overload_1(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.bank_accounts.with_raw_response.create( @@ -378,7 +380,7 @@ async def test_raw_response_create_overload_1(self, async_client: AsyncPostGrid) bank_account = await response.parse() assert_matches_type(BankAccount, bank_account, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create_overload_1(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.bank_accounts.with_streaming_response.create( @@ -395,7 +397,7 @@ async def test_streaming_response_create_overload_1(self, async_client: AsyncPos assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create_overload_2(self, async_client: AsyncPostGrid) -> None: bank_account = await async_client.print_mail.bank_accounts.create( @@ -406,7 +408,7 @@ async def test_method_create_overload_2(self, async_client: AsyncPostGrid) -> No ) assert_matches_type(BankAccount, bank_account, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create_with_all_params_overload_2(self, async_client: AsyncPostGrid) -> None: bank_account = await async_client.print_mail.bank_accounts.create( @@ -425,7 +427,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn ) assert_matches_type(BankAccount, bank_account, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create_overload_2(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.bank_accounts.with_raw_response.create( @@ -440,7 +442,7 @@ async def test_raw_response_create_overload_2(self, async_client: AsyncPostGrid) bank_account = await response.parse() assert_matches_type(BankAccount, bank_account, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create_overload_2(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.bank_accounts.with_streaming_response.create( @@ -457,7 +459,7 @@ async def test_streaming_response_create_overload_2(self, async_client: AsyncPos assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create_overload_3(self, async_client: AsyncPostGrid) -> None: bank_account = await async_client.print_mail.bank_accounts.create( @@ -468,7 +470,7 @@ async def test_method_create_overload_3(self, async_client: AsyncPostGrid) -> No ) assert_matches_type(BankAccount, bank_account, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create_with_all_params_overload_3(self, async_client: AsyncPostGrid) -> None: bank_account = await async_client.print_mail.bank_accounts.create( @@ -487,7 +489,7 @@ async def test_method_create_with_all_params_overload_3(self, async_client: Asyn ) assert_matches_type(BankAccount, bank_account, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create_overload_3(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.bank_accounts.with_raw_response.create( @@ -502,7 +504,7 @@ async def test_raw_response_create_overload_3(self, async_client: AsyncPostGrid) bank_account = await response.parse() assert_matches_type(BankAccount, bank_account, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create_overload_3(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.bank_accounts.with_streaming_response.create( @@ -519,7 +521,7 @@ async def test_streaming_response_create_overload_3(self, async_client: AsyncPos assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_retrieve(self, async_client: AsyncPostGrid) -> None: bank_account = await async_client.print_mail.bank_accounts.retrieve( @@ -527,7 +529,7 @@ async def test_method_retrieve(self, async_client: AsyncPostGrid) -> None: ) assert_matches_type(BankAccount, bank_account, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_retrieve(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.bank_accounts.with_raw_response.retrieve( @@ -539,7 +541,7 @@ async def test_raw_response_retrieve(self, async_client: AsyncPostGrid) -> None: bank_account = await response.parse() assert_matches_type(BankAccount, bank_account, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.bank_accounts.with_streaming_response.retrieve( @@ -553,7 +555,7 @@ async def test_streaming_response_retrieve(self, async_client: AsyncPostGrid) -> assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_retrieve(self, async_client: AsyncPostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -561,13 +563,13 @@ async def test_path_params_retrieve(self, async_client: AsyncPostGrid) -> None: "", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list(self, async_client: AsyncPostGrid) -> None: bank_account = await async_client.print_mail.bank_accounts.list() assert_matches_type(AsyncSkipLimit[BankAccount], bank_account, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list_with_all_params(self, async_client: AsyncPostGrid) -> None: bank_account = await async_client.print_mail.bank_accounts.list( @@ -577,7 +579,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncPostGrid) -> ) assert_matches_type(AsyncSkipLimit[BankAccount], bank_account, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_list(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.bank_accounts.with_raw_response.list() @@ -587,7 +589,7 @@ async def test_raw_response_list(self, async_client: AsyncPostGrid) -> None: bank_account = await response.parse() assert_matches_type(AsyncSkipLimit[BankAccount], bank_account, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_list(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.bank_accounts.with_streaming_response.list() as response: @@ -599,7 +601,7 @@ async def test_streaming_response_list(self, async_client: AsyncPostGrid) -> Non assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_delete(self, async_client: AsyncPostGrid) -> None: bank_account = await async_client.print_mail.bank_accounts.delete( @@ -607,7 +609,7 @@ async def test_method_delete(self, async_client: AsyncPostGrid) -> None: ) assert_matches_type(BankAccountDeleteResponse, bank_account, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_delete(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.bank_accounts.with_raw_response.delete( @@ -619,7 +621,7 @@ async def test_raw_response_delete(self, async_client: AsyncPostGrid) -> None: bank_account = await response.parse() assert_matches_type(BankAccountDeleteResponse, bank_account, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_delete(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.bank_accounts.with_streaming_response.delete( @@ -633,7 +635,7 @@ async def test_streaming_response_delete(self, async_client: AsyncPostGrid) -> N assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_delete(self, async_client: AsyncPostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): diff --git a/tests/api_resources/print_mail/test_boxes.py b/tests/api_resources/print_mail/test_boxes.py index 1d558a6..20e0f67 100644 --- a/tests/api_resources/print_mail/test_boxes.py +++ b/tests/api_resources/print_mail/test_boxes.py @@ -19,14 +19,14 @@ class TestBoxes: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create(self, client: PostGrid) -> None: box = client.print_mail.boxes.create( cheques=[ { "amount": 5000, - "bankAccount": "bank_abc", + "bank_account": "bank_abc", "number": 1042, "from": "contact_456", "to": "contact_123", @@ -37,19 +37,19 @@ def test_method_create(self, client: PostGrid) -> None: ) assert_matches_type(Box, box, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create_with_all_params(self, client: PostGrid) -> None: box = client.print_mail.boxes.create( cheques=[ { "amount": 5000, - "bankAccount": "bank_abc", + "bank_account": "bank_abc", "number": 1042, - "logoURL": "https://example.com", + "logo_url": "https://example.com", "memo": "memo", - "mergeVariables": {"foo": "bar"}, - "messageTemplate": "messageTemplate", + "merge_variables": {"foo": "bar"}, + "message_template": "messageTemplate", "from": "contact_456", "to": "contact_123", } @@ -64,14 +64,14 @@ def test_method_create_with_all_params(self, client: PostGrid) -> None: ) assert_matches_type(Box, box, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create(self, client: PostGrid) -> None: response = client.print_mail.boxes.with_raw_response.create( cheques=[ { "amount": 5000, - "bankAccount": "bank_abc", + "bank_account": "bank_abc", "number": 1042, "from": "contact_456", "to": "contact_123", @@ -86,14 +86,14 @@ def test_raw_response_create(self, client: PostGrid) -> None: box = response.parse() assert_matches_type(Box, box, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create(self, client: PostGrid) -> None: with client.print_mail.boxes.with_streaming_response.create( cheques=[ { "amount": 5000, - "bankAccount": "bank_abc", + "bank_account": "bank_abc", "number": 1042, "from": "contact_456", "to": "contact_123", @@ -110,7 +110,7 @@ def test_streaming_response_create(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_retrieve(self, client: PostGrid) -> None: box = client.print_mail.boxes.retrieve( @@ -118,7 +118,7 @@ def test_method_retrieve(self, client: PostGrid) -> None: ) assert_matches_type(Box, box, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_retrieve(self, client: PostGrid) -> None: response = client.print_mail.boxes.with_raw_response.retrieve( @@ -130,7 +130,7 @@ def test_raw_response_retrieve(self, client: PostGrid) -> None: box = response.parse() assert_matches_type(Box, box, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_retrieve(self, client: PostGrid) -> None: with client.print_mail.boxes.with_streaming_response.retrieve( @@ -144,7 +144,7 @@ def test_streaming_response_retrieve(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_retrieve(self, client: PostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -152,13 +152,13 @@ def test_path_params_retrieve(self, client: PostGrid) -> None: "", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list(self, client: PostGrid) -> None: box = client.print_mail.boxes.list() assert_matches_type(SyncSkipLimit[Box], box, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list_with_all_params(self, client: PostGrid) -> None: box = client.print_mail.boxes.list( @@ -168,7 +168,7 @@ def test_method_list_with_all_params(self, client: PostGrid) -> None: ) assert_matches_type(SyncSkipLimit[Box], box, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_list(self, client: PostGrid) -> None: response = client.print_mail.boxes.with_raw_response.list() @@ -178,7 +178,7 @@ def test_raw_response_list(self, client: PostGrid) -> None: box = response.parse() assert_matches_type(SyncSkipLimit[Box], box, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_list(self, client: PostGrid) -> None: with client.print_mail.boxes.with_streaming_response.list() as response: @@ -190,7 +190,7 @@ def test_streaming_response_list(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_delete(self, client: PostGrid) -> None: box = client.print_mail.boxes.delete( @@ -198,7 +198,7 @@ def test_method_delete(self, client: PostGrid) -> None: ) assert_matches_type(Box, box, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_delete(self, client: PostGrid) -> None: response = client.print_mail.boxes.with_raw_response.delete( @@ -210,7 +210,7 @@ def test_raw_response_delete(self, client: PostGrid) -> None: box = response.parse() assert_matches_type(Box, box, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_delete(self, client: PostGrid) -> None: with client.print_mail.boxes.with_streaming_response.delete( @@ -224,7 +224,7 @@ def test_streaming_response_delete(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_delete(self, client: PostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -234,16 +234,18 @@ def test_path_params_delete(self, client: PostGrid) -> None: class TestAsyncBoxes: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create(self, async_client: AsyncPostGrid) -> None: box = await async_client.print_mail.boxes.create( cheques=[ { "amount": 5000, - "bankAccount": "bank_abc", + "bank_account": "bank_abc", "number": 1042, "from": "contact_456", "to": "contact_123", @@ -254,19 +256,19 @@ async def test_method_create(self, async_client: AsyncPostGrid) -> None: ) assert_matches_type(Box, box, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create_with_all_params(self, async_client: AsyncPostGrid) -> None: box = await async_client.print_mail.boxes.create( cheques=[ { "amount": 5000, - "bankAccount": "bank_abc", + "bank_account": "bank_abc", "number": 1042, - "logoURL": "https://example.com", + "logo_url": "https://example.com", "memo": "memo", - "mergeVariables": {"foo": "bar"}, - "messageTemplate": "messageTemplate", + "merge_variables": {"foo": "bar"}, + "message_template": "messageTemplate", "from": "contact_456", "to": "contact_123", } @@ -281,14 +283,14 @@ async def test_method_create_with_all_params(self, async_client: AsyncPostGrid) ) assert_matches_type(Box, box, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.boxes.with_raw_response.create( cheques=[ { "amount": 5000, - "bankAccount": "bank_abc", + "bank_account": "bank_abc", "number": 1042, "from": "contact_456", "to": "contact_123", @@ -303,14 +305,14 @@ async def test_raw_response_create(self, async_client: AsyncPostGrid) -> None: box = await response.parse() assert_matches_type(Box, box, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.boxes.with_streaming_response.create( cheques=[ { "amount": 5000, - "bankAccount": "bank_abc", + "bank_account": "bank_abc", "number": 1042, "from": "contact_456", "to": "contact_123", @@ -327,7 +329,7 @@ async def test_streaming_response_create(self, async_client: AsyncPostGrid) -> N assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_retrieve(self, async_client: AsyncPostGrid) -> None: box = await async_client.print_mail.boxes.retrieve( @@ -335,7 +337,7 @@ async def test_method_retrieve(self, async_client: AsyncPostGrid) -> None: ) assert_matches_type(Box, box, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_retrieve(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.boxes.with_raw_response.retrieve( @@ -347,7 +349,7 @@ async def test_raw_response_retrieve(self, async_client: AsyncPostGrid) -> None: box = await response.parse() assert_matches_type(Box, box, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.boxes.with_streaming_response.retrieve( @@ -361,7 +363,7 @@ async def test_streaming_response_retrieve(self, async_client: AsyncPostGrid) -> assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_retrieve(self, async_client: AsyncPostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -369,13 +371,13 @@ async def test_path_params_retrieve(self, async_client: AsyncPostGrid) -> None: "", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list(self, async_client: AsyncPostGrid) -> None: box = await async_client.print_mail.boxes.list() assert_matches_type(AsyncSkipLimit[Box], box, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list_with_all_params(self, async_client: AsyncPostGrid) -> None: box = await async_client.print_mail.boxes.list( @@ -385,7 +387,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncPostGrid) -> ) assert_matches_type(AsyncSkipLimit[Box], box, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_list(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.boxes.with_raw_response.list() @@ -395,7 +397,7 @@ async def test_raw_response_list(self, async_client: AsyncPostGrid) -> None: box = await response.parse() assert_matches_type(AsyncSkipLimit[Box], box, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_list(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.boxes.with_streaming_response.list() as response: @@ -407,7 +409,7 @@ async def test_streaming_response_list(self, async_client: AsyncPostGrid) -> Non assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_delete(self, async_client: AsyncPostGrid) -> None: box = await async_client.print_mail.boxes.delete( @@ -415,7 +417,7 @@ async def test_method_delete(self, async_client: AsyncPostGrid) -> None: ) assert_matches_type(Box, box, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_delete(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.boxes.with_raw_response.delete( @@ -427,7 +429,7 @@ async def test_raw_response_delete(self, async_client: AsyncPostGrid) -> None: box = await response.parse() assert_matches_type(Box, box, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_delete(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.boxes.with_streaming_response.delete( @@ -441,7 +443,7 @@ async def test_streaming_response_delete(self, async_client: AsyncPostGrid) -> N assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_delete(self, async_client: AsyncPostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): diff --git a/tests/api_resources/print_mail/test_campaigns.py b/tests/api_resources/print_mail/test_campaigns.py index 2aa6325..1c88737 100644 --- a/tests/api_resources/print_mail/test_campaigns.py +++ b/tests/api_resources/print_mail/test_campaigns.py @@ -22,7 +22,7 @@ class TestCampaigns: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create(self, client: PostGrid) -> None: campaign = client.print_mail.campaigns.create( @@ -30,7 +30,7 @@ def test_method_create(self, client: PostGrid) -> None: ) assert_matches_type(Campaign, campaign, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create_with_all_params(self, client: PostGrid) -> None: campaign = client.print_mail.campaigns.create( @@ -47,7 +47,7 @@ def test_method_create_with_all_params(self, client: PostGrid) -> None: ) assert_matches_type(Campaign, campaign, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create(self, client: PostGrid) -> None: response = client.print_mail.campaigns.with_raw_response.create( @@ -59,7 +59,7 @@ def test_raw_response_create(self, client: PostGrid) -> None: campaign = response.parse() assert_matches_type(Campaign, campaign, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create(self, client: PostGrid) -> None: with client.print_mail.campaigns.with_streaming_response.create( @@ -73,7 +73,7 @@ def test_streaming_response_create(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_retrieve(self, client: PostGrid) -> None: campaign = client.print_mail.campaigns.retrieve( @@ -81,7 +81,7 @@ def test_method_retrieve(self, client: PostGrid) -> None: ) assert_matches_type(Campaign, campaign, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_retrieve(self, client: PostGrid) -> None: response = client.print_mail.campaigns.with_raw_response.retrieve( @@ -93,7 +93,7 @@ def test_raw_response_retrieve(self, client: PostGrid) -> None: campaign = response.parse() assert_matches_type(Campaign, campaign, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_retrieve(self, client: PostGrid) -> None: with client.print_mail.campaigns.with_streaming_response.retrieve( @@ -107,7 +107,7 @@ def test_streaming_response_retrieve(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_retrieve(self, client: PostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -115,7 +115,7 @@ def test_path_params_retrieve(self, client: PostGrid) -> None: "", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_update(self, client: PostGrid) -> None: campaign = client.print_mail.campaigns.update( @@ -123,7 +123,7 @@ def test_method_update(self, client: PostGrid) -> None: ) assert_matches_type(Campaign, campaign, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_update_with_all_params(self, client: PostGrid) -> None: campaign = client.print_mail.campaigns.update( @@ -139,7 +139,7 @@ def test_method_update_with_all_params(self, client: PostGrid) -> None: ) assert_matches_type(Campaign, campaign, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_update(self, client: PostGrid) -> None: response = client.print_mail.campaigns.with_raw_response.update( @@ -151,7 +151,7 @@ def test_raw_response_update(self, client: PostGrid) -> None: campaign = response.parse() assert_matches_type(Campaign, campaign, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_update(self, client: PostGrid) -> None: with client.print_mail.campaigns.with_streaming_response.update( @@ -165,7 +165,7 @@ def test_streaming_response_update(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_update(self, client: PostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -173,13 +173,13 @@ def test_path_params_update(self, client: PostGrid) -> None: id="", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list(self, client: PostGrid) -> None: campaign = client.print_mail.campaigns.list() assert_matches_type(SyncSkipLimit[Campaign], campaign, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list_with_all_params(self, client: PostGrid) -> None: campaign = client.print_mail.campaigns.list( @@ -189,7 +189,7 @@ def test_method_list_with_all_params(self, client: PostGrid) -> None: ) assert_matches_type(SyncSkipLimit[Campaign], campaign, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_list(self, client: PostGrid) -> None: response = client.print_mail.campaigns.with_raw_response.list() @@ -199,7 +199,7 @@ def test_raw_response_list(self, client: PostGrid) -> None: campaign = response.parse() assert_matches_type(SyncSkipLimit[Campaign], campaign, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_list(self, client: PostGrid) -> None: with client.print_mail.campaigns.with_streaming_response.list() as response: @@ -211,7 +211,7 @@ def test_streaming_response_list(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_delete(self, client: PostGrid) -> None: campaign = client.print_mail.campaigns.delete( @@ -219,7 +219,7 @@ def test_method_delete(self, client: PostGrid) -> None: ) assert_matches_type(CampaignDeleteResponse, campaign, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_delete(self, client: PostGrid) -> None: response = client.print_mail.campaigns.with_raw_response.delete( @@ -231,7 +231,7 @@ def test_raw_response_delete(self, client: PostGrid) -> None: campaign = response.parse() assert_matches_type(CampaignDeleteResponse, campaign, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_delete(self, client: PostGrid) -> None: with client.print_mail.campaigns.with_streaming_response.delete( @@ -245,7 +245,7 @@ def test_streaming_response_delete(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_delete(self, client: PostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -253,7 +253,7 @@ def test_path_params_delete(self, client: PostGrid) -> None: "", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_send(self, client: PostGrid) -> None: campaign = client.print_mail.campaigns.send( @@ -261,7 +261,7 @@ def test_method_send(self, client: PostGrid) -> None: ) assert_matches_type(Campaign, campaign, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_send_with_all_params(self, client: PostGrid) -> None: campaign = client.print_mail.campaigns.send( @@ -270,7 +270,7 @@ def test_method_send_with_all_params(self, client: PostGrid) -> None: ) assert_matches_type(Campaign, campaign, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_send(self, client: PostGrid) -> None: response = client.print_mail.campaigns.with_raw_response.send( @@ -282,7 +282,7 @@ def test_raw_response_send(self, client: PostGrid) -> None: campaign = response.parse() assert_matches_type(Campaign, campaign, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_send(self, client: PostGrid) -> None: with client.print_mail.campaigns.with_streaming_response.send( @@ -296,7 +296,7 @@ def test_streaming_response_send(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_send(self, client: PostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -306,9 +306,11 @@ def test_path_params_send(self, client: PostGrid) -> None: class TestAsyncCampaigns: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create(self, async_client: AsyncPostGrid) -> None: campaign = await async_client.print_mail.campaigns.create( @@ -316,7 +318,7 @@ async def test_method_create(self, async_client: AsyncPostGrid) -> None: ) assert_matches_type(Campaign, campaign, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create_with_all_params(self, async_client: AsyncPostGrid) -> None: campaign = await async_client.print_mail.campaigns.create( @@ -333,7 +335,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncPostGrid) ) assert_matches_type(Campaign, campaign, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.campaigns.with_raw_response.create( @@ -345,7 +347,7 @@ async def test_raw_response_create(self, async_client: AsyncPostGrid) -> None: campaign = await response.parse() assert_matches_type(Campaign, campaign, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.campaigns.with_streaming_response.create( @@ -359,7 +361,7 @@ async def test_streaming_response_create(self, async_client: AsyncPostGrid) -> N assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_retrieve(self, async_client: AsyncPostGrid) -> None: campaign = await async_client.print_mail.campaigns.retrieve( @@ -367,7 +369,7 @@ async def test_method_retrieve(self, async_client: AsyncPostGrid) -> None: ) assert_matches_type(Campaign, campaign, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_retrieve(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.campaigns.with_raw_response.retrieve( @@ -379,7 +381,7 @@ async def test_raw_response_retrieve(self, async_client: AsyncPostGrid) -> None: campaign = await response.parse() assert_matches_type(Campaign, campaign, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.campaigns.with_streaming_response.retrieve( @@ -393,7 +395,7 @@ async def test_streaming_response_retrieve(self, async_client: AsyncPostGrid) -> assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_retrieve(self, async_client: AsyncPostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -401,7 +403,7 @@ async def test_path_params_retrieve(self, async_client: AsyncPostGrid) -> None: "", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_update(self, async_client: AsyncPostGrid) -> None: campaign = await async_client.print_mail.campaigns.update( @@ -409,7 +411,7 @@ async def test_method_update(self, async_client: AsyncPostGrid) -> None: ) assert_matches_type(Campaign, campaign, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_update_with_all_params(self, async_client: AsyncPostGrid) -> None: campaign = await async_client.print_mail.campaigns.update( @@ -425,7 +427,7 @@ async def test_method_update_with_all_params(self, async_client: AsyncPostGrid) ) assert_matches_type(Campaign, campaign, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_update(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.campaigns.with_raw_response.update( @@ -437,7 +439,7 @@ async def test_raw_response_update(self, async_client: AsyncPostGrid) -> None: campaign = await response.parse() assert_matches_type(Campaign, campaign, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_update(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.campaigns.with_streaming_response.update( @@ -451,7 +453,7 @@ async def test_streaming_response_update(self, async_client: AsyncPostGrid) -> N assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_update(self, async_client: AsyncPostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -459,13 +461,13 @@ async def test_path_params_update(self, async_client: AsyncPostGrid) -> None: id="", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list(self, async_client: AsyncPostGrid) -> None: campaign = await async_client.print_mail.campaigns.list() assert_matches_type(AsyncSkipLimit[Campaign], campaign, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list_with_all_params(self, async_client: AsyncPostGrid) -> None: campaign = await async_client.print_mail.campaigns.list( @@ -475,7 +477,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncPostGrid) -> ) assert_matches_type(AsyncSkipLimit[Campaign], campaign, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_list(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.campaigns.with_raw_response.list() @@ -485,7 +487,7 @@ async def test_raw_response_list(self, async_client: AsyncPostGrid) -> None: campaign = await response.parse() assert_matches_type(AsyncSkipLimit[Campaign], campaign, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_list(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.campaigns.with_streaming_response.list() as response: @@ -497,7 +499,7 @@ async def test_streaming_response_list(self, async_client: AsyncPostGrid) -> Non assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_delete(self, async_client: AsyncPostGrid) -> None: campaign = await async_client.print_mail.campaigns.delete( @@ -505,7 +507,7 @@ async def test_method_delete(self, async_client: AsyncPostGrid) -> None: ) assert_matches_type(CampaignDeleteResponse, campaign, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_delete(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.campaigns.with_raw_response.delete( @@ -517,7 +519,7 @@ async def test_raw_response_delete(self, async_client: AsyncPostGrid) -> None: campaign = await response.parse() assert_matches_type(CampaignDeleteResponse, campaign, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_delete(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.campaigns.with_streaming_response.delete( @@ -531,7 +533,7 @@ async def test_streaming_response_delete(self, async_client: AsyncPostGrid) -> N assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_delete(self, async_client: AsyncPostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -539,7 +541,7 @@ async def test_path_params_delete(self, async_client: AsyncPostGrid) -> None: "", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_send(self, async_client: AsyncPostGrid) -> None: campaign = await async_client.print_mail.campaigns.send( @@ -547,7 +549,7 @@ async def test_method_send(self, async_client: AsyncPostGrid) -> None: ) assert_matches_type(Campaign, campaign, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_send_with_all_params(self, async_client: AsyncPostGrid) -> None: campaign = await async_client.print_mail.campaigns.send( @@ -556,7 +558,7 @@ async def test_method_send_with_all_params(self, async_client: AsyncPostGrid) -> ) assert_matches_type(Campaign, campaign, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_send(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.campaigns.with_raw_response.send( @@ -568,7 +570,7 @@ async def test_raw_response_send(self, async_client: AsyncPostGrid) -> None: campaign = await response.parse() assert_matches_type(Campaign, campaign, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_send(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.campaigns.with_streaming_response.send( @@ -582,7 +584,7 @@ async def test_streaming_response_send(self, async_client: AsyncPostGrid) -> Non assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_send(self, async_client: AsyncPostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): diff --git a/tests/api_resources/print_mail/test_cheques.py b/tests/api_resources/print_mail/test_cheques.py index cecd773..380a667 100644 --- a/tests/api_resources/print_mail/test_cheques.py +++ b/tests/api_resources/print_mail/test_cheques.py @@ -22,7 +22,7 @@ class TestCheques: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create(self, client: PostGrid) -> None: cheque = client.print_mail.cheques.create( @@ -33,7 +33,7 @@ def test_method_create(self, client: PostGrid) -> None: ) assert_matches_type(Cheque, cheque, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create_with_all_params(self, client: PostGrid) -> None: cheque = client.print_mail.cheques.create( @@ -75,7 +75,7 @@ def test_method_create_with_all_params(self, client: PostGrid) -> None: ) assert_matches_type(Cheque, cheque, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create(self, client: PostGrid) -> None: response = client.print_mail.cheques.with_raw_response.create( @@ -90,7 +90,7 @@ def test_raw_response_create(self, client: PostGrid) -> None: cheque = response.parse() assert_matches_type(Cheque, cheque, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create(self, client: PostGrid) -> None: with client.print_mail.cheques.with_streaming_response.create( @@ -107,7 +107,7 @@ def test_streaming_response_create(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_retrieve(self, client: PostGrid) -> None: cheque = client.print_mail.cheques.retrieve( @@ -115,7 +115,7 @@ def test_method_retrieve(self, client: PostGrid) -> None: ) assert_matches_type(Cheque, cheque, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_retrieve(self, client: PostGrid) -> None: response = client.print_mail.cheques.with_raw_response.retrieve( @@ -127,7 +127,7 @@ def test_raw_response_retrieve(self, client: PostGrid) -> None: cheque = response.parse() assert_matches_type(Cheque, cheque, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_retrieve(self, client: PostGrid) -> None: with client.print_mail.cheques.with_streaming_response.retrieve( @@ -141,7 +141,7 @@ def test_streaming_response_retrieve(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_retrieve(self, client: PostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -149,13 +149,13 @@ def test_path_params_retrieve(self, client: PostGrid) -> None: "", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list(self, client: PostGrid) -> None: cheque = client.print_mail.cheques.list() assert_matches_type(SyncSkipLimit[Cheque], cheque, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list_with_all_params(self, client: PostGrid) -> None: cheque = client.print_mail.cheques.list( @@ -165,7 +165,7 @@ def test_method_list_with_all_params(self, client: PostGrid) -> None: ) assert_matches_type(SyncSkipLimit[Cheque], cheque, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_list(self, client: PostGrid) -> None: response = client.print_mail.cheques.with_raw_response.list() @@ -175,7 +175,7 @@ def test_raw_response_list(self, client: PostGrid) -> None: cheque = response.parse() assert_matches_type(SyncSkipLimit[Cheque], cheque, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_list(self, client: PostGrid) -> None: with client.print_mail.cheques.with_streaming_response.list() as response: @@ -187,7 +187,7 @@ def test_streaming_response_list(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_delete(self, client: PostGrid) -> None: cheque = client.print_mail.cheques.delete( @@ -195,7 +195,7 @@ def test_method_delete(self, client: PostGrid) -> None: ) assert_matches_type(Cheque, cheque, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_delete(self, client: PostGrid) -> None: response = client.print_mail.cheques.with_raw_response.delete( @@ -207,7 +207,7 @@ def test_raw_response_delete(self, client: PostGrid) -> None: cheque = response.parse() assert_matches_type(Cheque, cheque, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_delete(self, client: PostGrid) -> None: with client.print_mail.cheques.with_streaming_response.delete( @@ -221,7 +221,7 @@ def test_streaming_response_delete(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_delete(self, client: PostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -229,7 +229,7 @@ def test_path_params_delete(self, client: PostGrid) -> None: "", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_retrieve_url(self, client: PostGrid) -> None: cheque = client.print_mail.cheques.retrieve_url( @@ -237,7 +237,7 @@ def test_method_retrieve_url(self, client: PostGrid) -> None: ) assert_matches_type(ChequeRetrieveURLResponse, cheque, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_retrieve_url(self, client: PostGrid) -> None: response = client.print_mail.cheques.with_raw_response.retrieve_url( @@ -249,7 +249,7 @@ def test_raw_response_retrieve_url(self, client: PostGrid) -> None: cheque = response.parse() assert_matches_type(ChequeRetrieveURLResponse, cheque, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_retrieve_url(self, client: PostGrid) -> None: with client.print_mail.cheques.with_streaming_response.retrieve_url( @@ -263,7 +263,7 @@ def test_streaming_response_retrieve_url(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_retrieve_url(self, client: PostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -271,7 +271,7 @@ def test_path_params_retrieve_url(self, client: PostGrid) -> None: "", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_retrieve_with_deposit_ready_pdf(self, client: PostGrid) -> None: cheque = client.print_mail.cheques.retrieve_with_deposit_ready_pdf( @@ -279,7 +279,7 @@ def test_method_retrieve_with_deposit_ready_pdf(self, client: PostGrid) -> None: ) assert_matches_type(Cheque, cheque, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_retrieve_with_deposit_ready_pdf(self, client: PostGrid) -> None: response = client.print_mail.cheques.with_raw_response.retrieve_with_deposit_ready_pdf( @@ -291,7 +291,7 @@ def test_raw_response_retrieve_with_deposit_ready_pdf(self, client: PostGrid) -> cheque = response.parse() assert_matches_type(Cheque, cheque, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_retrieve_with_deposit_ready_pdf(self, client: PostGrid) -> None: with client.print_mail.cheques.with_streaming_response.retrieve_with_deposit_ready_pdf( @@ -305,7 +305,7 @@ def test_streaming_response_retrieve_with_deposit_ready_pdf(self, client: PostGr assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_retrieve_with_deposit_ready_pdf(self, client: PostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -315,9 +315,11 @@ def test_path_params_retrieve_with_deposit_ready_pdf(self, client: PostGrid) -> class TestAsyncCheques: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create(self, async_client: AsyncPostGrid) -> None: cheque = await async_client.print_mail.cheques.create( @@ -328,7 +330,7 @@ async def test_method_create(self, async_client: AsyncPostGrid) -> None: ) assert_matches_type(Cheque, cheque, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create_with_all_params(self, async_client: AsyncPostGrid) -> None: cheque = await async_client.print_mail.cheques.create( @@ -370,7 +372,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncPostGrid) ) assert_matches_type(Cheque, cheque, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.cheques.with_raw_response.create( @@ -385,7 +387,7 @@ async def test_raw_response_create(self, async_client: AsyncPostGrid) -> None: cheque = await response.parse() assert_matches_type(Cheque, cheque, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.cheques.with_streaming_response.create( @@ -402,7 +404,7 @@ async def test_streaming_response_create(self, async_client: AsyncPostGrid) -> N assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_retrieve(self, async_client: AsyncPostGrid) -> None: cheque = await async_client.print_mail.cheques.retrieve( @@ -410,7 +412,7 @@ async def test_method_retrieve(self, async_client: AsyncPostGrid) -> None: ) assert_matches_type(Cheque, cheque, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_retrieve(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.cheques.with_raw_response.retrieve( @@ -422,7 +424,7 @@ async def test_raw_response_retrieve(self, async_client: AsyncPostGrid) -> None: cheque = await response.parse() assert_matches_type(Cheque, cheque, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.cheques.with_streaming_response.retrieve( @@ -436,7 +438,7 @@ async def test_streaming_response_retrieve(self, async_client: AsyncPostGrid) -> assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_retrieve(self, async_client: AsyncPostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -444,13 +446,13 @@ async def test_path_params_retrieve(self, async_client: AsyncPostGrid) -> None: "", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list(self, async_client: AsyncPostGrid) -> None: cheque = await async_client.print_mail.cheques.list() assert_matches_type(AsyncSkipLimit[Cheque], cheque, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list_with_all_params(self, async_client: AsyncPostGrid) -> None: cheque = await async_client.print_mail.cheques.list( @@ -460,7 +462,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncPostGrid) -> ) assert_matches_type(AsyncSkipLimit[Cheque], cheque, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_list(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.cheques.with_raw_response.list() @@ -470,7 +472,7 @@ async def test_raw_response_list(self, async_client: AsyncPostGrid) -> None: cheque = await response.parse() assert_matches_type(AsyncSkipLimit[Cheque], cheque, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_list(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.cheques.with_streaming_response.list() as response: @@ -482,7 +484,7 @@ async def test_streaming_response_list(self, async_client: AsyncPostGrid) -> Non assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_delete(self, async_client: AsyncPostGrid) -> None: cheque = await async_client.print_mail.cheques.delete( @@ -490,7 +492,7 @@ async def test_method_delete(self, async_client: AsyncPostGrid) -> None: ) assert_matches_type(Cheque, cheque, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_delete(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.cheques.with_raw_response.delete( @@ -502,7 +504,7 @@ async def test_raw_response_delete(self, async_client: AsyncPostGrid) -> None: cheque = await response.parse() assert_matches_type(Cheque, cheque, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_delete(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.cheques.with_streaming_response.delete( @@ -516,7 +518,7 @@ async def test_streaming_response_delete(self, async_client: AsyncPostGrid) -> N assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_delete(self, async_client: AsyncPostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -524,7 +526,7 @@ async def test_path_params_delete(self, async_client: AsyncPostGrid) -> None: "", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_retrieve_url(self, async_client: AsyncPostGrid) -> None: cheque = await async_client.print_mail.cheques.retrieve_url( @@ -532,7 +534,7 @@ async def test_method_retrieve_url(self, async_client: AsyncPostGrid) -> None: ) assert_matches_type(ChequeRetrieveURLResponse, cheque, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_retrieve_url(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.cheques.with_raw_response.retrieve_url( @@ -544,7 +546,7 @@ async def test_raw_response_retrieve_url(self, async_client: AsyncPostGrid) -> N cheque = await response.parse() assert_matches_type(ChequeRetrieveURLResponse, cheque, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_retrieve_url(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.cheques.with_streaming_response.retrieve_url( @@ -558,7 +560,7 @@ async def test_streaming_response_retrieve_url(self, async_client: AsyncPostGrid assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_retrieve_url(self, async_client: AsyncPostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -566,7 +568,7 @@ async def test_path_params_retrieve_url(self, async_client: AsyncPostGrid) -> No "", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_retrieve_with_deposit_ready_pdf(self, async_client: AsyncPostGrid) -> None: cheque = await async_client.print_mail.cheques.retrieve_with_deposit_ready_pdf( @@ -574,7 +576,7 @@ async def test_method_retrieve_with_deposit_ready_pdf(self, async_client: AsyncP ) assert_matches_type(Cheque, cheque, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_retrieve_with_deposit_ready_pdf(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.cheques.with_raw_response.retrieve_with_deposit_ready_pdf( @@ -586,7 +588,7 @@ async def test_raw_response_retrieve_with_deposit_ready_pdf(self, async_client: cheque = await response.parse() assert_matches_type(Cheque, cheque, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_retrieve_with_deposit_ready_pdf(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.cheques.with_streaming_response.retrieve_with_deposit_ready_pdf( @@ -600,7 +602,7 @@ async def test_streaming_response_retrieve_with_deposit_ready_pdf(self, async_cl assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_retrieve_with_deposit_ready_pdf(self, async_client: AsyncPostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): diff --git a/tests/api_resources/print_mail/test_contacts.py b/tests/api_resources/print_mail/test_contacts.py index 54ef305..3b96c00 100644 --- a/tests/api_resources/print_mail/test_contacts.py +++ b/tests/api_resources/print_mail/test_contacts.py @@ -18,7 +18,7 @@ class TestContacts: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create_overload_1(self, client: PostGrid) -> None: contact = client.print_mail.contacts.create( @@ -28,7 +28,7 @@ def test_method_create_overload_1(self, client: PostGrid) -> None: ) assert_matches_type(Contact, contact, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create_with_all_params_overload_1(self, client: PostGrid) -> None: contact = client.print_mail.contacts.create( @@ -51,7 +51,7 @@ def test_method_create_with_all_params_overload_1(self, client: PostGrid) -> Non ) assert_matches_type(Contact, contact, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create_overload_1(self, client: PostGrid) -> None: response = client.print_mail.contacts.with_raw_response.create( @@ -65,7 +65,7 @@ def test_raw_response_create_overload_1(self, client: PostGrid) -> None: contact = response.parse() assert_matches_type(Contact, contact, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create_overload_1(self, client: PostGrid) -> None: with client.print_mail.contacts.with_streaming_response.create( @@ -81,7 +81,7 @@ def test_streaming_response_create_overload_1(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create_overload_2(self, client: PostGrid) -> None: contact = client.print_mail.contacts.create( @@ -91,7 +91,7 @@ def test_method_create_overload_2(self, client: PostGrid) -> None: ) assert_matches_type(Contact, contact, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create_with_all_params_overload_2(self, client: PostGrid) -> None: contact = client.print_mail.contacts.create( @@ -114,7 +114,7 @@ def test_method_create_with_all_params_overload_2(self, client: PostGrid) -> Non ) assert_matches_type(Contact, contact, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create_overload_2(self, client: PostGrid) -> None: response = client.print_mail.contacts.with_raw_response.create( @@ -128,7 +128,7 @@ def test_raw_response_create_overload_2(self, client: PostGrid) -> None: contact = response.parse() assert_matches_type(Contact, contact, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create_overload_2(self, client: PostGrid) -> None: with client.print_mail.contacts.with_streaming_response.create( @@ -144,7 +144,7 @@ def test_streaming_response_create_overload_2(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_retrieve(self, client: PostGrid) -> None: contact = client.print_mail.contacts.retrieve( @@ -152,7 +152,7 @@ def test_method_retrieve(self, client: PostGrid) -> None: ) assert_matches_type(Contact, contact, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_retrieve(self, client: PostGrid) -> None: response = client.print_mail.contacts.with_raw_response.retrieve( @@ -164,7 +164,7 @@ def test_raw_response_retrieve(self, client: PostGrid) -> None: contact = response.parse() assert_matches_type(Contact, contact, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_retrieve(self, client: PostGrid) -> None: with client.print_mail.contacts.with_streaming_response.retrieve( @@ -178,7 +178,7 @@ def test_streaming_response_retrieve(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_retrieve(self, client: PostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -186,13 +186,13 @@ def test_path_params_retrieve(self, client: PostGrid) -> None: "", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list(self, client: PostGrid) -> None: contact = client.print_mail.contacts.list() assert_matches_type(SyncSkipLimit[Contact], contact, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list_with_all_params(self, client: PostGrid) -> None: contact = client.print_mail.contacts.list( @@ -202,7 +202,7 @@ def test_method_list_with_all_params(self, client: PostGrid) -> None: ) assert_matches_type(SyncSkipLimit[Contact], contact, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_list(self, client: PostGrid) -> None: response = client.print_mail.contacts.with_raw_response.list() @@ -212,7 +212,7 @@ def test_raw_response_list(self, client: PostGrid) -> None: contact = response.parse() assert_matches_type(SyncSkipLimit[Contact], contact, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_list(self, client: PostGrid) -> None: with client.print_mail.contacts.with_streaming_response.list() as response: @@ -224,7 +224,7 @@ def test_streaming_response_list(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_delete(self, client: PostGrid) -> None: contact = client.print_mail.contacts.delete( @@ -232,7 +232,7 @@ def test_method_delete(self, client: PostGrid) -> None: ) assert_matches_type(ContactDeleteResponse, contact, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_delete(self, client: PostGrid) -> None: response = client.print_mail.contacts.with_raw_response.delete( @@ -244,7 +244,7 @@ def test_raw_response_delete(self, client: PostGrid) -> None: contact = response.parse() assert_matches_type(ContactDeleteResponse, contact, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_delete(self, client: PostGrid) -> None: with client.print_mail.contacts.with_streaming_response.delete( @@ -258,7 +258,7 @@ def test_streaming_response_delete(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_delete(self, client: PostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -268,9 +268,11 @@ def test_path_params_delete(self, client: PostGrid) -> None: class TestAsyncContacts: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create_overload_1(self, async_client: AsyncPostGrid) -> None: contact = await async_client.print_mail.contacts.create( @@ -280,7 +282,7 @@ async def test_method_create_overload_1(self, async_client: AsyncPostGrid) -> No ) assert_matches_type(Contact, contact, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create_with_all_params_overload_1(self, async_client: AsyncPostGrid) -> None: contact = await async_client.print_mail.contacts.create( @@ -303,7 +305,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn ) assert_matches_type(Contact, contact, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create_overload_1(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.contacts.with_raw_response.create( @@ -317,7 +319,7 @@ async def test_raw_response_create_overload_1(self, async_client: AsyncPostGrid) contact = await response.parse() assert_matches_type(Contact, contact, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create_overload_1(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.contacts.with_streaming_response.create( @@ -333,7 +335,7 @@ async def test_streaming_response_create_overload_1(self, async_client: AsyncPos assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create_overload_2(self, async_client: AsyncPostGrid) -> None: contact = await async_client.print_mail.contacts.create( @@ -343,7 +345,7 @@ async def test_method_create_overload_2(self, async_client: AsyncPostGrid) -> No ) assert_matches_type(Contact, contact, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create_with_all_params_overload_2(self, async_client: AsyncPostGrid) -> None: contact = await async_client.print_mail.contacts.create( @@ -366,7 +368,7 @@ async def test_method_create_with_all_params_overload_2(self, async_client: Asyn ) assert_matches_type(Contact, contact, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create_overload_2(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.contacts.with_raw_response.create( @@ -380,7 +382,7 @@ async def test_raw_response_create_overload_2(self, async_client: AsyncPostGrid) contact = await response.parse() assert_matches_type(Contact, contact, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create_overload_2(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.contacts.with_streaming_response.create( @@ -396,7 +398,7 @@ async def test_streaming_response_create_overload_2(self, async_client: AsyncPos assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_retrieve(self, async_client: AsyncPostGrid) -> None: contact = await async_client.print_mail.contacts.retrieve( @@ -404,7 +406,7 @@ async def test_method_retrieve(self, async_client: AsyncPostGrid) -> None: ) assert_matches_type(Contact, contact, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_retrieve(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.contacts.with_raw_response.retrieve( @@ -416,7 +418,7 @@ async def test_raw_response_retrieve(self, async_client: AsyncPostGrid) -> None: contact = await response.parse() assert_matches_type(Contact, contact, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.contacts.with_streaming_response.retrieve( @@ -430,7 +432,7 @@ async def test_streaming_response_retrieve(self, async_client: AsyncPostGrid) -> assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_retrieve(self, async_client: AsyncPostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -438,13 +440,13 @@ async def test_path_params_retrieve(self, async_client: AsyncPostGrid) -> None: "", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list(self, async_client: AsyncPostGrid) -> None: contact = await async_client.print_mail.contacts.list() assert_matches_type(AsyncSkipLimit[Contact], contact, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list_with_all_params(self, async_client: AsyncPostGrid) -> None: contact = await async_client.print_mail.contacts.list( @@ -454,7 +456,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncPostGrid) -> ) assert_matches_type(AsyncSkipLimit[Contact], contact, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_list(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.contacts.with_raw_response.list() @@ -464,7 +466,7 @@ async def test_raw_response_list(self, async_client: AsyncPostGrid) -> None: contact = await response.parse() assert_matches_type(AsyncSkipLimit[Contact], contact, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_list(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.contacts.with_streaming_response.list() as response: @@ -476,7 +478,7 @@ async def test_streaming_response_list(self, async_client: AsyncPostGrid) -> Non assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_delete(self, async_client: AsyncPostGrid) -> None: contact = await async_client.print_mail.contacts.delete( @@ -484,7 +486,7 @@ async def test_method_delete(self, async_client: AsyncPostGrid) -> None: ) assert_matches_type(ContactDeleteResponse, contact, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_delete(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.contacts.with_raw_response.delete( @@ -496,7 +498,7 @@ async def test_raw_response_delete(self, async_client: AsyncPostGrid) -> None: contact = await response.parse() assert_matches_type(ContactDeleteResponse, contact, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_delete(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.contacts.with_streaming_response.delete( @@ -510,7 +512,7 @@ async def test_streaming_response_delete(self, async_client: AsyncPostGrid) -> N assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_delete(self, async_client: AsyncPostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): diff --git a/tests/api_resources/print_mail/test_letters.py b/tests/api_resources/print_mail/test_letters.py index e56cfad..6341c56 100644 --- a/tests/api_resources/print_mail/test_letters.py +++ b/tests/api_resources/print_mail/test_letters.py @@ -22,7 +22,7 @@ class TestLetters: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create_overload_1(self, client: PostGrid) -> None: letter = client.print_mail.letters.create( @@ -40,7 +40,7 @@ def test_method_create_overload_1(self, client: PostGrid) -> None: ) assert_matches_type(Letter, letter, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create_with_all_params_overload_1(self, client: PostGrid) -> None: letter = client.print_mail.letters.create( @@ -115,7 +115,7 @@ def test_method_create_with_all_params_overload_1(self, client: PostGrid) -> Non ) assert_matches_type(Letter, letter, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create_overload_1(self, client: PostGrid) -> None: response = client.print_mail.letters.with_raw_response.create( @@ -137,7 +137,7 @@ def test_raw_response_create_overload_1(self, client: PostGrid) -> None: letter = response.parse() assert_matches_type(Letter, letter, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create_overload_1(self, client: PostGrid) -> None: with client.print_mail.letters.with_streaming_response.create( @@ -161,7 +161,7 @@ def test_streaming_response_create_overload_1(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create_overload_2(self, client: PostGrid) -> None: letter = client.print_mail.letters.create( @@ -169,7 +169,7 @@ def test_method_create_overload_2(self, client: PostGrid) -> None: ) assert_matches_type(Letter, letter, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create_overload_2(self, client: PostGrid) -> None: response = client.print_mail.letters.with_raw_response.create( @@ -181,7 +181,7 @@ def test_raw_response_create_overload_2(self, client: PostGrid) -> None: letter = response.parse() assert_matches_type(Letter, letter, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create_overload_2(self, client: PostGrid) -> None: with client.print_mail.letters.with_streaming_response.create( @@ -195,7 +195,7 @@ def test_streaming_response_create_overload_2(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create_overload_3(self, client: PostGrid) -> None: letter = client.print_mail.letters.create( @@ -213,7 +213,7 @@ def test_method_create_overload_3(self, client: PostGrid) -> None: ) assert_matches_type(Letter, letter, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create_with_all_params_overload_3(self, client: PostGrid) -> None: letter = client.print_mail.letters.create( @@ -288,7 +288,7 @@ def test_method_create_with_all_params_overload_3(self, client: PostGrid) -> Non ) assert_matches_type(Letter, letter, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create_overload_3(self, client: PostGrid) -> None: response = client.print_mail.letters.with_raw_response.create( @@ -310,7 +310,7 @@ def test_raw_response_create_overload_3(self, client: PostGrid) -> None: letter = response.parse() assert_matches_type(Letter, letter, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create_overload_3(self, client: PostGrid) -> None: with client.print_mail.letters.with_streaming_response.create( @@ -334,7 +334,7 @@ def test_streaming_response_create_overload_3(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_retrieve(self, client: PostGrid) -> None: letter = client.print_mail.letters.retrieve( @@ -342,7 +342,7 @@ def test_method_retrieve(self, client: PostGrid) -> None: ) assert_matches_type(Letter, letter, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_retrieve(self, client: PostGrid) -> None: response = client.print_mail.letters.with_raw_response.retrieve( @@ -354,7 +354,7 @@ def test_raw_response_retrieve(self, client: PostGrid) -> None: letter = response.parse() assert_matches_type(Letter, letter, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_retrieve(self, client: PostGrid) -> None: with client.print_mail.letters.with_streaming_response.retrieve( @@ -368,7 +368,7 @@ def test_streaming_response_retrieve(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_retrieve(self, client: PostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -376,13 +376,13 @@ def test_path_params_retrieve(self, client: PostGrid) -> None: "", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list(self, client: PostGrid) -> None: letter = client.print_mail.letters.list() assert_matches_type(SyncSkipLimit[Letter], letter, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list_with_all_params(self, client: PostGrid) -> None: letter = client.print_mail.letters.list( @@ -392,7 +392,7 @@ def test_method_list_with_all_params(self, client: PostGrid) -> None: ) assert_matches_type(SyncSkipLimit[Letter], letter, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_list(self, client: PostGrid) -> None: response = client.print_mail.letters.with_raw_response.list() @@ -402,7 +402,7 @@ def test_raw_response_list(self, client: PostGrid) -> None: letter = response.parse() assert_matches_type(SyncSkipLimit[Letter], letter, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_list(self, client: PostGrid) -> None: with client.print_mail.letters.with_streaming_response.list() as response: @@ -414,7 +414,7 @@ def test_streaming_response_list(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_delete(self, client: PostGrid) -> None: letter = client.print_mail.letters.delete( @@ -422,7 +422,7 @@ def test_method_delete(self, client: PostGrid) -> None: ) assert_matches_type(Letter, letter, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_delete(self, client: PostGrid) -> None: response = client.print_mail.letters.with_raw_response.delete( @@ -434,7 +434,7 @@ def test_raw_response_delete(self, client: PostGrid) -> None: letter = response.parse() assert_matches_type(Letter, letter, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_delete(self, client: PostGrid) -> None: with client.print_mail.letters.with_streaming_response.delete( @@ -448,7 +448,7 @@ def test_streaming_response_delete(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_delete(self, client: PostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -456,7 +456,7 @@ def test_path_params_delete(self, client: PostGrid) -> None: "", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_retrieve_url(self, client: PostGrid) -> None: letter = client.print_mail.letters.retrieve_url( @@ -464,7 +464,7 @@ def test_method_retrieve_url(self, client: PostGrid) -> None: ) assert_matches_type(LetterRetrieveURLResponse, letter, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_retrieve_url(self, client: PostGrid) -> None: response = client.print_mail.letters.with_raw_response.retrieve_url( @@ -476,7 +476,7 @@ def test_raw_response_retrieve_url(self, client: PostGrid) -> None: letter = response.parse() assert_matches_type(LetterRetrieveURLResponse, letter, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_retrieve_url(self, client: PostGrid) -> None: with client.print_mail.letters.with_streaming_response.retrieve_url( @@ -490,7 +490,7 @@ def test_streaming_response_retrieve_url(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_retrieve_url(self, client: PostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -500,9 +500,11 @@ def test_path_params_retrieve_url(self, client: PostGrid) -> None: class TestAsyncLetters: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create_overload_1(self, async_client: AsyncPostGrid) -> None: letter = await async_client.print_mail.letters.create( @@ -520,7 +522,7 @@ async def test_method_create_overload_1(self, async_client: AsyncPostGrid) -> No ) assert_matches_type(Letter, letter, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create_with_all_params_overload_1(self, async_client: AsyncPostGrid) -> None: letter = await async_client.print_mail.letters.create( @@ -595,7 +597,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn ) assert_matches_type(Letter, letter, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create_overload_1(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.letters.with_raw_response.create( @@ -617,7 +619,7 @@ async def test_raw_response_create_overload_1(self, async_client: AsyncPostGrid) letter = await response.parse() assert_matches_type(Letter, letter, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create_overload_1(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.letters.with_streaming_response.create( @@ -641,7 +643,7 @@ async def test_streaming_response_create_overload_1(self, async_client: AsyncPos assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create_overload_2(self, async_client: AsyncPostGrid) -> None: letter = await async_client.print_mail.letters.create( @@ -649,7 +651,7 @@ async def test_method_create_overload_2(self, async_client: AsyncPostGrid) -> No ) assert_matches_type(Letter, letter, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create_overload_2(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.letters.with_raw_response.create( @@ -661,7 +663,7 @@ async def test_raw_response_create_overload_2(self, async_client: AsyncPostGrid) letter = await response.parse() assert_matches_type(Letter, letter, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create_overload_2(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.letters.with_streaming_response.create( @@ -675,7 +677,7 @@ async def test_streaming_response_create_overload_2(self, async_client: AsyncPos assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create_overload_3(self, async_client: AsyncPostGrid) -> None: letter = await async_client.print_mail.letters.create( @@ -693,7 +695,7 @@ async def test_method_create_overload_3(self, async_client: AsyncPostGrid) -> No ) assert_matches_type(Letter, letter, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create_with_all_params_overload_3(self, async_client: AsyncPostGrid) -> None: letter = await async_client.print_mail.letters.create( @@ -768,7 +770,7 @@ async def test_method_create_with_all_params_overload_3(self, async_client: Asyn ) assert_matches_type(Letter, letter, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create_overload_3(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.letters.with_raw_response.create( @@ -790,7 +792,7 @@ async def test_raw_response_create_overload_3(self, async_client: AsyncPostGrid) letter = await response.parse() assert_matches_type(Letter, letter, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create_overload_3(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.letters.with_streaming_response.create( @@ -814,7 +816,7 @@ async def test_streaming_response_create_overload_3(self, async_client: AsyncPos assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_retrieve(self, async_client: AsyncPostGrid) -> None: letter = await async_client.print_mail.letters.retrieve( @@ -822,7 +824,7 @@ async def test_method_retrieve(self, async_client: AsyncPostGrid) -> None: ) assert_matches_type(Letter, letter, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_retrieve(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.letters.with_raw_response.retrieve( @@ -834,7 +836,7 @@ async def test_raw_response_retrieve(self, async_client: AsyncPostGrid) -> None: letter = await response.parse() assert_matches_type(Letter, letter, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.letters.with_streaming_response.retrieve( @@ -848,7 +850,7 @@ async def test_streaming_response_retrieve(self, async_client: AsyncPostGrid) -> assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_retrieve(self, async_client: AsyncPostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -856,13 +858,13 @@ async def test_path_params_retrieve(self, async_client: AsyncPostGrid) -> None: "", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list(self, async_client: AsyncPostGrid) -> None: letter = await async_client.print_mail.letters.list() assert_matches_type(AsyncSkipLimit[Letter], letter, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list_with_all_params(self, async_client: AsyncPostGrid) -> None: letter = await async_client.print_mail.letters.list( @@ -872,7 +874,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncPostGrid) -> ) assert_matches_type(AsyncSkipLimit[Letter], letter, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_list(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.letters.with_raw_response.list() @@ -882,7 +884,7 @@ async def test_raw_response_list(self, async_client: AsyncPostGrid) -> None: letter = await response.parse() assert_matches_type(AsyncSkipLimit[Letter], letter, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_list(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.letters.with_streaming_response.list() as response: @@ -894,7 +896,7 @@ async def test_streaming_response_list(self, async_client: AsyncPostGrid) -> Non assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_delete(self, async_client: AsyncPostGrid) -> None: letter = await async_client.print_mail.letters.delete( @@ -902,7 +904,7 @@ async def test_method_delete(self, async_client: AsyncPostGrid) -> None: ) assert_matches_type(Letter, letter, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_delete(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.letters.with_raw_response.delete( @@ -914,7 +916,7 @@ async def test_raw_response_delete(self, async_client: AsyncPostGrid) -> None: letter = await response.parse() assert_matches_type(Letter, letter, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_delete(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.letters.with_streaming_response.delete( @@ -928,7 +930,7 @@ async def test_streaming_response_delete(self, async_client: AsyncPostGrid) -> N assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_delete(self, async_client: AsyncPostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -936,7 +938,7 @@ async def test_path_params_delete(self, async_client: AsyncPostGrid) -> None: "", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_retrieve_url(self, async_client: AsyncPostGrid) -> None: letter = await async_client.print_mail.letters.retrieve_url( @@ -944,7 +946,7 @@ async def test_method_retrieve_url(self, async_client: AsyncPostGrid) -> None: ) assert_matches_type(LetterRetrieveURLResponse, letter, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_retrieve_url(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.letters.with_raw_response.retrieve_url( @@ -956,7 +958,7 @@ async def test_raw_response_retrieve_url(self, async_client: AsyncPostGrid) -> N letter = await response.parse() assert_matches_type(LetterRetrieveURLResponse, letter, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_retrieve_url(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.letters.with_streaming_response.retrieve_url( @@ -970,7 +972,7 @@ async def test_streaming_response_retrieve_url(self, async_client: AsyncPostGrid assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_retrieve_url(self, async_client: AsyncPostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): diff --git a/tests/api_resources/print_mail/test_mailing_list_imports.py b/tests/api_resources/print_mail/test_mailing_list_imports.py index 6fe9f0c..a110593 100644 --- a/tests/api_resources/print_mail/test_mailing_list_imports.py +++ b/tests/api_resources/print_mail/test_mailing_list_imports.py @@ -21,7 +21,7 @@ class TestMailingListImports: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create(self, client: PostGrid) -> None: mailing_list_import = client.print_mail.mailing_list_imports.create( @@ -41,7 +41,7 @@ def test_method_create(self, client: PostGrid) -> None: ) assert_matches_type(MailingListImportResponse, mailing_list_import, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create_with_all_params(self, client: PostGrid) -> None: mailing_list_import = client.print_mail.mailing_list_imports.create( @@ -67,7 +67,7 @@ def test_method_create_with_all_params(self, client: PostGrid) -> None: ) assert_matches_type(MailingListImportResponse, mailing_list_import, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create(self, client: PostGrid) -> None: response = client.print_mail.mailing_list_imports.with_raw_response.create( @@ -91,7 +91,7 @@ def test_raw_response_create(self, client: PostGrid) -> None: mailing_list_import = response.parse() assert_matches_type(MailingListImportResponse, mailing_list_import, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create(self, client: PostGrid) -> None: with client.print_mail.mailing_list_imports.with_streaming_response.create( @@ -117,7 +117,7 @@ def test_streaming_response_create(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_retrieve(self, client: PostGrid) -> None: mailing_list_import = client.print_mail.mailing_list_imports.retrieve( @@ -125,7 +125,7 @@ def test_method_retrieve(self, client: PostGrid) -> None: ) assert_matches_type(MailingListImportResponse, mailing_list_import, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_retrieve(self, client: PostGrid) -> None: response = client.print_mail.mailing_list_imports.with_raw_response.retrieve( @@ -137,7 +137,7 @@ def test_raw_response_retrieve(self, client: PostGrid) -> None: mailing_list_import = response.parse() assert_matches_type(MailingListImportResponse, mailing_list_import, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_retrieve(self, client: PostGrid) -> None: with client.print_mail.mailing_list_imports.with_streaming_response.retrieve( @@ -151,7 +151,7 @@ def test_streaming_response_retrieve(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_retrieve(self, client: PostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -159,7 +159,7 @@ def test_path_params_retrieve(self, client: PostGrid) -> None: "", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_update(self, client: PostGrid) -> None: mailing_list_import = client.print_mail.mailing_list_imports.update( @@ -167,7 +167,7 @@ def test_method_update(self, client: PostGrid) -> None: ) assert_matches_type(MailingListImportResponse, mailing_list_import, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_update_with_all_params(self, client: PostGrid) -> None: mailing_list_import = client.print_mail.mailing_list_imports.update( @@ -177,7 +177,7 @@ def test_method_update_with_all_params(self, client: PostGrid) -> None: ) assert_matches_type(MailingListImportResponse, mailing_list_import, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_update(self, client: PostGrid) -> None: response = client.print_mail.mailing_list_imports.with_raw_response.update( @@ -189,7 +189,7 @@ def test_raw_response_update(self, client: PostGrid) -> None: mailing_list_import = response.parse() assert_matches_type(MailingListImportResponse, mailing_list_import, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_update(self, client: PostGrid) -> None: with client.print_mail.mailing_list_imports.with_streaming_response.update( @@ -203,7 +203,7 @@ def test_streaming_response_update(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_update(self, client: PostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -211,13 +211,13 @@ def test_path_params_update(self, client: PostGrid) -> None: id="", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list(self, client: PostGrid) -> None: mailing_list_import = client.print_mail.mailing_list_imports.list() assert_matches_type(SyncSkipLimit[MailingListImportResponse], mailing_list_import, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list_with_all_params(self, client: PostGrid) -> None: mailing_list_import = client.print_mail.mailing_list_imports.list( @@ -227,7 +227,7 @@ def test_method_list_with_all_params(self, client: PostGrid) -> None: ) assert_matches_type(SyncSkipLimit[MailingListImportResponse], mailing_list_import, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_list(self, client: PostGrid) -> None: response = client.print_mail.mailing_list_imports.with_raw_response.list() @@ -237,7 +237,7 @@ def test_raw_response_list(self, client: PostGrid) -> None: mailing_list_import = response.parse() assert_matches_type(SyncSkipLimit[MailingListImportResponse], mailing_list_import, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_list(self, client: PostGrid) -> None: with client.print_mail.mailing_list_imports.with_streaming_response.list() as response: @@ -249,7 +249,7 @@ def test_streaming_response_list(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_delete(self, client: PostGrid) -> None: mailing_list_import = client.print_mail.mailing_list_imports.delete( @@ -257,7 +257,7 @@ def test_method_delete(self, client: PostGrid) -> None: ) assert_matches_type(MailingListImportDeleteResponse, mailing_list_import, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_delete(self, client: PostGrid) -> None: response = client.print_mail.mailing_list_imports.with_raw_response.delete( @@ -269,7 +269,7 @@ def test_raw_response_delete(self, client: PostGrid) -> None: mailing_list_import = response.parse() assert_matches_type(MailingListImportDeleteResponse, mailing_list_import, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_delete(self, client: PostGrid) -> None: with client.print_mail.mailing_list_imports.with_streaming_response.delete( @@ -283,7 +283,7 @@ def test_streaming_response_delete(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_delete(self, client: PostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -293,9 +293,11 @@ def test_path_params_delete(self, client: PostGrid) -> None: class TestAsyncMailingListImports: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create(self, async_client: AsyncPostGrid) -> None: mailing_list_import = await async_client.print_mail.mailing_list_imports.create( @@ -315,7 +317,7 @@ async def test_method_create(self, async_client: AsyncPostGrid) -> None: ) assert_matches_type(MailingListImportResponse, mailing_list_import, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create_with_all_params(self, async_client: AsyncPostGrid) -> None: mailing_list_import = await async_client.print_mail.mailing_list_imports.create( @@ -341,7 +343,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncPostGrid) ) assert_matches_type(MailingListImportResponse, mailing_list_import, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.mailing_list_imports.with_raw_response.create( @@ -365,7 +367,7 @@ async def test_raw_response_create(self, async_client: AsyncPostGrid) -> None: mailing_list_import = await response.parse() assert_matches_type(MailingListImportResponse, mailing_list_import, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.mailing_list_imports.with_streaming_response.create( @@ -391,7 +393,7 @@ async def test_streaming_response_create(self, async_client: AsyncPostGrid) -> N assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_retrieve(self, async_client: AsyncPostGrid) -> None: mailing_list_import = await async_client.print_mail.mailing_list_imports.retrieve( @@ -399,7 +401,7 @@ async def test_method_retrieve(self, async_client: AsyncPostGrid) -> None: ) assert_matches_type(MailingListImportResponse, mailing_list_import, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_retrieve(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.mailing_list_imports.with_raw_response.retrieve( @@ -411,7 +413,7 @@ async def test_raw_response_retrieve(self, async_client: AsyncPostGrid) -> None: mailing_list_import = await response.parse() assert_matches_type(MailingListImportResponse, mailing_list_import, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.mailing_list_imports.with_streaming_response.retrieve( @@ -425,7 +427,7 @@ async def test_streaming_response_retrieve(self, async_client: AsyncPostGrid) -> assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_retrieve(self, async_client: AsyncPostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -433,7 +435,7 @@ async def test_path_params_retrieve(self, async_client: AsyncPostGrid) -> None: "", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_update(self, async_client: AsyncPostGrid) -> None: mailing_list_import = await async_client.print_mail.mailing_list_imports.update( @@ -441,7 +443,7 @@ async def test_method_update(self, async_client: AsyncPostGrid) -> None: ) assert_matches_type(MailingListImportResponse, mailing_list_import, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_update_with_all_params(self, async_client: AsyncPostGrid) -> None: mailing_list_import = await async_client.print_mail.mailing_list_imports.update( @@ -451,7 +453,7 @@ async def test_method_update_with_all_params(self, async_client: AsyncPostGrid) ) assert_matches_type(MailingListImportResponse, mailing_list_import, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_update(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.mailing_list_imports.with_raw_response.update( @@ -463,7 +465,7 @@ async def test_raw_response_update(self, async_client: AsyncPostGrid) -> None: mailing_list_import = await response.parse() assert_matches_type(MailingListImportResponse, mailing_list_import, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_update(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.mailing_list_imports.with_streaming_response.update( @@ -477,7 +479,7 @@ async def test_streaming_response_update(self, async_client: AsyncPostGrid) -> N assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_update(self, async_client: AsyncPostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -485,13 +487,13 @@ async def test_path_params_update(self, async_client: AsyncPostGrid) -> None: id="", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list(self, async_client: AsyncPostGrid) -> None: mailing_list_import = await async_client.print_mail.mailing_list_imports.list() assert_matches_type(AsyncSkipLimit[MailingListImportResponse], mailing_list_import, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list_with_all_params(self, async_client: AsyncPostGrid) -> None: mailing_list_import = await async_client.print_mail.mailing_list_imports.list( @@ -501,7 +503,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncPostGrid) -> ) assert_matches_type(AsyncSkipLimit[MailingListImportResponse], mailing_list_import, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_list(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.mailing_list_imports.with_raw_response.list() @@ -511,7 +513,7 @@ async def test_raw_response_list(self, async_client: AsyncPostGrid) -> None: mailing_list_import = await response.parse() assert_matches_type(AsyncSkipLimit[MailingListImportResponse], mailing_list_import, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_list(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.mailing_list_imports.with_streaming_response.list() as response: @@ -523,7 +525,7 @@ async def test_streaming_response_list(self, async_client: AsyncPostGrid) -> Non assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_delete(self, async_client: AsyncPostGrid) -> None: mailing_list_import = await async_client.print_mail.mailing_list_imports.delete( @@ -531,7 +533,7 @@ async def test_method_delete(self, async_client: AsyncPostGrid) -> None: ) assert_matches_type(MailingListImportDeleteResponse, mailing_list_import, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_delete(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.mailing_list_imports.with_raw_response.delete( @@ -543,7 +545,7 @@ async def test_raw_response_delete(self, async_client: AsyncPostGrid) -> None: mailing_list_import = await response.parse() assert_matches_type(MailingListImportDeleteResponse, mailing_list_import, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_delete(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.mailing_list_imports.with_streaming_response.delete( @@ -557,7 +559,7 @@ async def test_streaming_response_delete(self, async_client: AsyncPostGrid) -> N assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_delete(self, async_client: AsyncPostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): diff --git a/tests/api_resources/print_mail/test_mailing_lists.py b/tests/api_resources/print_mail/test_mailing_lists.py index 62cafea..f01d576 100644 --- a/tests/api_resources/print_mail/test_mailing_lists.py +++ b/tests/api_resources/print_mail/test_mailing_lists.py @@ -22,13 +22,13 @@ class TestMailingLists: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create(self, client: PostGrid) -> None: mailing_list = client.print_mail.mailing_lists.create() assert_matches_type(MailingList, mailing_list, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create_with_all_params(self, client: PostGrid) -> None: mailing_list = client.print_mail.mailing_lists.create( @@ -38,7 +38,7 @@ def test_method_create_with_all_params(self, client: PostGrid) -> None: ) assert_matches_type(MailingList, mailing_list, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create(self, client: PostGrid) -> None: response = client.print_mail.mailing_lists.with_raw_response.create() @@ -48,7 +48,7 @@ def test_raw_response_create(self, client: PostGrid) -> None: mailing_list = response.parse() assert_matches_type(MailingList, mailing_list, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create(self, client: PostGrid) -> None: with client.print_mail.mailing_lists.with_streaming_response.create() as response: @@ -60,7 +60,7 @@ def test_streaming_response_create(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_retrieve(self, client: PostGrid) -> None: mailing_list = client.print_mail.mailing_lists.retrieve( @@ -68,7 +68,7 @@ def test_method_retrieve(self, client: PostGrid) -> None: ) assert_matches_type(MailingList, mailing_list, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_retrieve(self, client: PostGrid) -> None: response = client.print_mail.mailing_lists.with_raw_response.retrieve( @@ -80,7 +80,7 @@ def test_raw_response_retrieve(self, client: PostGrid) -> None: mailing_list = response.parse() assert_matches_type(MailingList, mailing_list, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_retrieve(self, client: PostGrid) -> None: with client.print_mail.mailing_lists.with_streaming_response.retrieve( @@ -94,7 +94,7 @@ def test_streaming_response_retrieve(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_retrieve(self, client: PostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -102,7 +102,7 @@ def test_path_params_retrieve(self, client: PostGrid) -> None: "", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_update(self, client: PostGrid) -> None: mailing_list = client.print_mail.mailing_lists.update( @@ -110,7 +110,7 @@ def test_method_update(self, client: PostGrid) -> None: ) assert_matches_type(MailingListUpdate, mailing_list, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_update_with_all_params(self, client: PostGrid) -> None: mailing_list = client.print_mail.mailing_lists.update( @@ -120,7 +120,7 @@ def test_method_update_with_all_params(self, client: PostGrid) -> None: ) assert_matches_type(MailingListUpdate, mailing_list, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_update(self, client: PostGrid) -> None: response = client.print_mail.mailing_lists.with_raw_response.update( @@ -132,7 +132,7 @@ def test_raw_response_update(self, client: PostGrid) -> None: mailing_list = response.parse() assert_matches_type(MailingListUpdate, mailing_list, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_update(self, client: PostGrid) -> None: with client.print_mail.mailing_lists.with_streaming_response.update( @@ -146,7 +146,7 @@ def test_streaming_response_update(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_update(self, client: PostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -154,13 +154,13 @@ def test_path_params_update(self, client: PostGrid) -> None: id="", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list(self, client: PostGrid) -> None: mailing_list = client.print_mail.mailing_lists.list() assert_matches_type(SyncSkipLimit[MailingList], mailing_list, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list_with_all_params(self, client: PostGrid) -> None: mailing_list = client.print_mail.mailing_lists.list( @@ -170,7 +170,7 @@ def test_method_list_with_all_params(self, client: PostGrid) -> None: ) assert_matches_type(SyncSkipLimit[MailingList], mailing_list, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_list(self, client: PostGrid) -> None: response = client.print_mail.mailing_lists.with_raw_response.list() @@ -180,7 +180,7 @@ def test_raw_response_list(self, client: PostGrid) -> None: mailing_list = response.parse() assert_matches_type(SyncSkipLimit[MailingList], mailing_list, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_list(self, client: PostGrid) -> None: with client.print_mail.mailing_lists.with_streaming_response.list() as response: @@ -192,7 +192,7 @@ def test_streaming_response_list(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_delete(self, client: PostGrid) -> None: mailing_list = client.print_mail.mailing_lists.delete( @@ -200,7 +200,7 @@ def test_method_delete(self, client: PostGrid) -> None: ) assert_matches_type(MailingListDeleteResponse, mailing_list, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_delete(self, client: PostGrid) -> None: response = client.print_mail.mailing_lists.with_raw_response.delete( @@ -212,7 +212,7 @@ def test_raw_response_delete(self, client: PostGrid) -> None: mailing_list = response.parse() assert_matches_type(MailingListDeleteResponse, mailing_list, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_delete(self, client: PostGrid) -> None: with client.print_mail.mailing_lists.with_streaming_response.delete( @@ -226,7 +226,7 @@ def test_streaming_response_delete(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_delete(self, client: PostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -234,7 +234,7 @@ def test_path_params_delete(self, client: PostGrid) -> None: "", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_jobs(self, client: PostGrid) -> None: mailing_list = client.print_mail.mailing_lists.jobs( @@ -242,7 +242,7 @@ def test_method_jobs(self, client: PostGrid) -> None: ) assert_matches_type(MailingList, mailing_list, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_jobs_with_all_params(self, client: PostGrid) -> None: mailing_list = client.print_mail.mailing_lists.jobs( @@ -254,7 +254,7 @@ def test_method_jobs_with_all_params(self, client: PostGrid) -> None: ) assert_matches_type(MailingList, mailing_list, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_jobs(self, client: PostGrid) -> None: response = client.print_mail.mailing_lists.with_raw_response.jobs( @@ -266,7 +266,7 @@ def test_raw_response_jobs(self, client: PostGrid) -> None: mailing_list = response.parse() assert_matches_type(MailingList, mailing_list, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_jobs(self, client: PostGrid) -> None: with client.print_mail.mailing_lists.with_streaming_response.jobs( @@ -280,7 +280,7 @@ def test_streaming_response_jobs(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_jobs(self, client: PostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -290,15 +290,17 @@ def test_path_params_jobs(self, client: PostGrid) -> None: class TestAsyncMailingLists: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create(self, async_client: AsyncPostGrid) -> None: mailing_list = await async_client.print_mail.mailing_lists.create() assert_matches_type(MailingList, mailing_list, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create_with_all_params(self, async_client: AsyncPostGrid) -> None: mailing_list = await async_client.print_mail.mailing_lists.create( @@ -308,7 +310,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncPostGrid) ) assert_matches_type(MailingList, mailing_list, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.mailing_lists.with_raw_response.create() @@ -318,7 +320,7 @@ async def test_raw_response_create(self, async_client: AsyncPostGrid) -> None: mailing_list = await response.parse() assert_matches_type(MailingList, mailing_list, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.mailing_lists.with_streaming_response.create() as response: @@ -330,7 +332,7 @@ async def test_streaming_response_create(self, async_client: AsyncPostGrid) -> N assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_retrieve(self, async_client: AsyncPostGrid) -> None: mailing_list = await async_client.print_mail.mailing_lists.retrieve( @@ -338,7 +340,7 @@ async def test_method_retrieve(self, async_client: AsyncPostGrid) -> None: ) assert_matches_type(MailingList, mailing_list, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_retrieve(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.mailing_lists.with_raw_response.retrieve( @@ -350,7 +352,7 @@ async def test_raw_response_retrieve(self, async_client: AsyncPostGrid) -> None: mailing_list = await response.parse() assert_matches_type(MailingList, mailing_list, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.mailing_lists.with_streaming_response.retrieve( @@ -364,7 +366,7 @@ async def test_streaming_response_retrieve(self, async_client: AsyncPostGrid) -> assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_retrieve(self, async_client: AsyncPostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -372,7 +374,7 @@ async def test_path_params_retrieve(self, async_client: AsyncPostGrid) -> None: "", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_update(self, async_client: AsyncPostGrid) -> None: mailing_list = await async_client.print_mail.mailing_lists.update( @@ -380,7 +382,7 @@ async def test_method_update(self, async_client: AsyncPostGrid) -> None: ) assert_matches_type(MailingListUpdate, mailing_list, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_update_with_all_params(self, async_client: AsyncPostGrid) -> None: mailing_list = await async_client.print_mail.mailing_lists.update( @@ -390,7 +392,7 @@ async def test_method_update_with_all_params(self, async_client: AsyncPostGrid) ) assert_matches_type(MailingListUpdate, mailing_list, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_update(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.mailing_lists.with_raw_response.update( @@ -402,7 +404,7 @@ async def test_raw_response_update(self, async_client: AsyncPostGrid) -> None: mailing_list = await response.parse() assert_matches_type(MailingListUpdate, mailing_list, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_update(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.mailing_lists.with_streaming_response.update( @@ -416,7 +418,7 @@ async def test_streaming_response_update(self, async_client: AsyncPostGrid) -> N assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_update(self, async_client: AsyncPostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -424,13 +426,13 @@ async def test_path_params_update(self, async_client: AsyncPostGrid) -> None: id="", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list(self, async_client: AsyncPostGrid) -> None: mailing_list = await async_client.print_mail.mailing_lists.list() assert_matches_type(AsyncSkipLimit[MailingList], mailing_list, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list_with_all_params(self, async_client: AsyncPostGrid) -> None: mailing_list = await async_client.print_mail.mailing_lists.list( @@ -440,7 +442,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncPostGrid) -> ) assert_matches_type(AsyncSkipLimit[MailingList], mailing_list, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_list(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.mailing_lists.with_raw_response.list() @@ -450,7 +452,7 @@ async def test_raw_response_list(self, async_client: AsyncPostGrid) -> None: mailing_list = await response.parse() assert_matches_type(AsyncSkipLimit[MailingList], mailing_list, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_list(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.mailing_lists.with_streaming_response.list() as response: @@ -462,7 +464,7 @@ async def test_streaming_response_list(self, async_client: AsyncPostGrid) -> Non assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_delete(self, async_client: AsyncPostGrid) -> None: mailing_list = await async_client.print_mail.mailing_lists.delete( @@ -470,7 +472,7 @@ async def test_method_delete(self, async_client: AsyncPostGrid) -> None: ) assert_matches_type(MailingListDeleteResponse, mailing_list, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_delete(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.mailing_lists.with_raw_response.delete( @@ -482,7 +484,7 @@ async def test_raw_response_delete(self, async_client: AsyncPostGrid) -> None: mailing_list = await response.parse() assert_matches_type(MailingListDeleteResponse, mailing_list, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_delete(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.mailing_lists.with_streaming_response.delete( @@ -496,7 +498,7 @@ async def test_streaming_response_delete(self, async_client: AsyncPostGrid) -> N assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_delete(self, async_client: AsyncPostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -504,7 +506,7 @@ async def test_path_params_delete(self, async_client: AsyncPostGrid) -> None: "", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_jobs(self, async_client: AsyncPostGrid) -> None: mailing_list = await async_client.print_mail.mailing_lists.jobs( @@ -512,7 +514,7 @@ async def test_method_jobs(self, async_client: AsyncPostGrid) -> None: ) assert_matches_type(MailingList, mailing_list, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_jobs_with_all_params(self, async_client: AsyncPostGrid) -> None: mailing_list = await async_client.print_mail.mailing_lists.jobs( @@ -524,7 +526,7 @@ async def test_method_jobs_with_all_params(self, async_client: AsyncPostGrid) -> ) assert_matches_type(MailingList, mailing_list, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_jobs(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.mailing_lists.with_raw_response.jobs( @@ -536,7 +538,7 @@ async def test_raw_response_jobs(self, async_client: AsyncPostGrid) -> None: mailing_list = await response.parse() assert_matches_type(MailingList, mailing_list, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_jobs(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.mailing_lists.with_streaming_response.jobs( @@ -550,7 +552,7 @@ async def test_streaming_response_jobs(self, async_client: AsyncPostGrid) -> Non assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_jobs(self, async_client: AsyncPostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): diff --git a/tests/api_resources/print_mail/test_postcards.py b/tests/api_resources/print_mail/test_postcards.py index 90e4685..b4d3690 100644 --- a/tests/api_resources/print_mail/test_postcards.py +++ b/tests/api_resources/print_mail/test_postcards.py @@ -22,7 +22,7 @@ class TestPostcards: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create_overload_1(self, client: PostGrid) -> None: postcard = client.print_mail.postcards.create( @@ -37,7 +37,7 @@ def test_method_create_overload_1(self, client: PostGrid) -> None: ) assert_matches_type(Postcard, postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create_with_all_params_overload_1(self, client: PostGrid) -> None: postcard = client.print_mail.postcards.create( @@ -88,7 +88,7 @@ def test_method_create_with_all_params_overload_1(self, client: PostGrid) -> Non ) assert_matches_type(Postcard, postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create_overload_1(self, client: PostGrid) -> None: response = client.print_mail.postcards.with_raw_response.create( @@ -107,7 +107,7 @@ def test_raw_response_create_overload_1(self, client: PostGrid) -> None: postcard = response.parse() assert_matches_type(Postcard, postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create_overload_1(self, client: PostGrid) -> None: with client.print_mail.postcards.with_streaming_response.create( @@ -128,7 +128,7 @@ def test_streaming_response_create_overload_1(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create_overload_2(self, client: PostGrid) -> None: postcard = client.print_mail.postcards.create( @@ -137,7 +137,7 @@ def test_method_create_overload_2(self, client: PostGrid) -> None: ) assert_matches_type(Postcard, postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create_overload_2(self, client: PostGrid) -> None: response = client.print_mail.postcards.with_raw_response.create( @@ -150,7 +150,7 @@ def test_raw_response_create_overload_2(self, client: PostGrid) -> None: postcard = response.parse() assert_matches_type(Postcard, postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create_overload_2(self, client: PostGrid) -> None: with client.print_mail.postcards.with_streaming_response.create( @@ -165,7 +165,7 @@ def test_streaming_response_create_overload_2(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create_overload_3(self, client: PostGrid) -> None: postcard = client.print_mail.postcards.create( @@ -179,7 +179,7 @@ def test_method_create_overload_3(self, client: PostGrid) -> None: ) assert_matches_type(Postcard, postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create_with_all_params_overload_3(self, client: PostGrid) -> None: postcard = client.print_mail.postcards.create( @@ -229,7 +229,7 @@ def test_method_create_with_all_params_overload_3(self, client: PostGrid) -> Non ) assert_matches_type(Postcard, postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create_overload_3(self, client: PostGrid) -> None: response = client.print_mail.postcards.with_raw_response.create( @@ -247,7 +247,7 @@ def test_raw_response_create_overload_3(self, client: PostGrid) -> None: postcard = response.parse() assert_matches_type(Postcard, postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create_overload_3(self, client: PostGrid) -> None: with client.print_mail.postcards.with_streaming_response.create( @@ -267,7 +267,7 @@ def test_streaming_response_create_overload_3(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create_overload_4(self, client: PostGrid) -> None: postcard = client.print_mail.postcards.create( @@ -281,7 +281,7 @@ def test_method_create_overload_4(self, client: PostGrid) -> None: ) assert_matches_type(Postcard, postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create_with_all_params_overload_4(self, client: PostGrid) -> None: postcard = client.print_mail.postcards.create( @@ -331,7 +331,7 @@ def test_method_create_with_all_params_overload_4(self, client: PostGrid) -> Non ) assert_matches_type(Postcard, postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create_overload_4(self, client: PostGrid) -> None: response = client.print_mail.postcards.with_raw_response.create( @@ -349,7 +349,7 @@ def test_raw_response_create_overload_4(self, client: PostGrid) -> None: postcard = response.parse() assert_matches_type(Postcard, postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create_overload_4(self, client: PostGrid) -> None: with client.print_mail.postcards.with_streaming_response.create( @@ -369,7 +369,7 @@ def test_streaming_response_create_overload_4(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_retrieve(self, client: PostGrid) -> None: postcard = client.print_mail.postcards.retrieve( @@ -377,7 +377,7 @@ def test_method_retrieve(self, client: PostGrid) -> None: ) assert_matches_type(Postcard, postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_retrieve(self, client: PostGrid) -> None: response = client.print_mail.postcards.with_raw_response.retrieve( @@ -389,7 +389,7 @@ def test_raw_response_retrieve(self, client: PostGrid) -> None: postcard = response.parse() assert_matches_type(Postcard, postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_retrieve(self, client: PostGrid) -> None: with client.print_mail.postcards.with_streaming_response.retrieve( @@ -403,7 +403,7 @@ def test_streaming_response_retrieve(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_retrieve(self, client: PostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -411,13 +411,13 @@ def test_path_params_retrieve(self, client: PostGrid) -> None: "", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list(self, client: PostGrid) -> None: postcard = client.print_mail.postcards.list() assert_matches_type(SyncSkipLimit[Postcard], postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list_with_all_params(self, client: PostGrid) -> None: postcard = client.print_mail.postcards.list( @@ -427,7 +427,7 @@ def test_method_list_with_all_params(self, client: PostGrid) -> None: ) assert_matches_type(SyncSkipLimit[Postcard], postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_list(self, client: PostGrid) -> None: response = client.print_mail.postcards.with_raw_response.list() @@ -437,7 +437,7 @@ def test_raw_response_list(self, client: PostGrid) -> None: postcard = response.parse() assert_matches_type(SyncSkipLimit[Postcard], postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_list(self, client: PostGrid) -> None: with client.print_mail.postcards.with_streaming_response.list() as response: @@ -449,7 +449,7 @@ def test_streaming_response_list(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_delete(self, client: PostGrid) -> None: postcard = client.print_mail.postcards.delete( @@ -457,7 +457,7 @@ def test_method_delete(self, client: PostGrid) -> None: ) assert_matches_type(Postcard, postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_delete(self, client: PostGrid) -> None: response = client.print_mail.postcards.with_raw_response.delete( @@ -469,7 +469,7 @@ def test_raw_response_delete(self, client: PostGrid) -> None: postcard = response.parse() assert_matches_type(Postcard, postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_delete(self, client: PostGrid) -> None: with client.print_mail.postcards.with_streaming_response.delete( @@ -483,7 +483,7 @@ def test_streaming_response_delete(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_delete(self, client: PostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -491,7 +491,7 @@ def test_path_params_delete(self, client: PostGrid) -> None: "", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_retrieve_url(self, client: PostGrid) -> None: postcard = client.print_mail.postcards.retrieve_url( @@ -499,7 +499,7 @@ def test_method_retrieve_url(self, client: PostGrid) -> None: ) assert_matches_type(PostcardRetrieveURLResponse, postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_retrieve_url(self, client: PostGrid) -> None: response = client.print_mail.postcards.with_raw_response.retrieve_url( @@ -511,7 +511,7 @@ def test_raw_response_retrieve_url(self, client: PostGrid) -> None: postcard = response.parse() assert_matches_type(PostcardRetrieveURLResponse, postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_retrieve_url(self, client: PostGrid) -> None: with client.print_mail.postcards.with_streaming_response.retrieve_url( @@ -525,7 +525,7 @@ def test_streaming_response_retrieve_url(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_retrieve_url(self, client: PostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -535,9 +535,11 @@ def test_path_params_retrieve_url(self, client: PostGrid) -> None: class TestAsyncPostcards: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create_overload_1(self, async_client: AsyncPostGrid) -> None: postcard = await async_client.print_mail.postcards.create( @@ -552,7 +554,7 @@ async def test_method_create_overload_1(self, async_client: AsyncPostGrid) -> No ) assert_matches_type(Postcard, postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create_with_all_params_overload_1(self, async_client: AsyncPostGrid) -> None: postcard = await async_client.print_mail.postcards.create( @@ -603,7 +605,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn ) assert_matches_type(Postcard, postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create_overload_1(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.postcards.with_raw_response.create( @@ -622,7 +624,7 @@ async def test_raw_response_create_overload_1(self, async_client: AsyncPostGrid) postcard = await response.parse() assert_matches_type(Postcard, postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create_overload_1(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.postcards.with_streaming_response.create( @@ -643,7 +645,7 @@ async def test_streaming_response_create_overload_1(self, async_client: AsyncPos assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create_overload_2(self, async_client: AsyncPostGrid) -> None: postcard = await async_client.print_mail.postcards.create( @@ -652,7 +654,7 @@ async def test_method_create_overload_2(self, async_client: AsyncPostGrid) -> No ) assert_matches_type(Postcard, postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create_overload_2(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.postcards.with_raw_response.create( @@ -665,7 +667,7 @@ async def test_raw_response_create_overload_2(self, async_client: AsyncPostGrid) postcard = await response.parse() assert_matches_type(Postcard, postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create_overload_2(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.postcards.with_streaming_response.create( @@ -680,7 +682,7 @@ async def test_streaming_response_create_overload_2(self, async_client: AsyncPos assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create_overload_3(self, async_client: AsyncPostGrid) -> None: postcard = await async_client.print_mail.postcards.create( @@ -694,7 +696,7 @@ async def test_method_create_overload_3(self, async_client: AsyncPostGrid) -> No ) assert_matches_type(Postcard, postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create_with_all_params_overload_3(self, async_client: AsyncPostGrid) -> None: postcard = await async_client.print_mail.postcards.create( @@ -744,7 +746,7 @@ async def test_method_create_with_all_params_overload_3(self, async_client: Asyn ) assert_matches_type(Postcard, postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create_overload_3(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.postcards.with_raw_response.create( @@ -762,7 +764,7 @@ async def test_raw_response_create_overload_3(self, async_client: AsyncPostGrid) postcard = await response.parse() assert_matches_type(Postcard, postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create_overload_3(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.postcards.with_streaming_response.create( @@ -782,7 +784,7 @@ async def test_streaming_response_create_overload_3(self, async_client: AsyncPos assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create_overload_4(self, async_client: AsyncPostGrid) -> None: postcard = await async_client.print_mail.postcards.create( @@ -796,7 +798,7 @@ async def test_method_create_overload_4(self, async_client: AsyncPostGrid) -> No ) assert_matches_type(Postcard, postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create_with_all_params_overload_4(self, async_client: AsyncPostGrid) -> None: postcard = await async_client.print_mail.postcards.create( @@ -846,7 +848,7 @@ async def test_method_create_with_all_params_overload_4(self, async_client: Asyn ) assert_matches_type(Postcard, postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create_overload_4(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.postcards.with_raw_response.create( @@ -864,7 +866,7 @@ async def test_raw_response_create_overload_4(self, async_client: AsyncPostGrid) postcard = await response.parse() assert_matches_type(Postcard, postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create_overload_4(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.postcards.with_streaming_response.create( @@ -884,7 +886,7 @@ async def test_streaming_response_create_overload_4(self, async_client: AsyncPos assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_retrieve(self, async_client: AsyncPostGrid) -> None: postcard = await async_client.print_mail.postcards.retrieve( @@ -892,7 +894,7 @@ async def test_method_retrieve(self, async_client: AsyncPostGrid) -> None: ) assert_matches_type(Postcard, postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_retrieve(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.postcards.with_raw_response.retrieve( @@ -904,7 +906,7 @@ async def test_raw_response_retrieve(self, async_client: AsyncPostGrid) -> None: postcard = await response.parse() assert_matches_type(Postcard, postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.postcards.with_streaming_response.retrieve( @@ -918,7 +920,7 @@ async def test_streaming_response_retrieve(self, async_client: AsyncPostGrid) -> assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_retrieve(self, async_client: AsyncPostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -926,13 +928,13 @@ async def test_path_params_retrieve(self, async_client: AsyncPostGrid) -> None: "", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list(self, async_client: AsyncPostGrid) -> None: postcard = await async_client.print_mail.postcards.list() assert_matches_type(AsyncSkipLimit[Postcard], postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list_with_all_params(self, async_client: AsyncPostGrid) -> None: postcard = await async_client.print_mail.postcards.list( @@ -942,7 +944,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncPostGrid) -> ) assert_matches_type(AsyncSkipLimit[Postcard], postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_list(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.postcards.with_raw_response.list() @@ -952,7 +954,7 @@ async def test_raw_response_list(self, async_client: AsyncPostGrid) -> None: postcard = await response.parse() assert_matches_type(AsyncSkipLimit[Postcard], postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_list(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.postcards.with_streaming_response.list() as response: @@ -964,7 +966,7 @@ async def test_streaming_response_list(self, async_client: AsyncPostGrid) -> Non assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_delete(self, async_client: AsyncPostGrid) -> None: postcard = await async_client.print_mail.postcards.delete( @@ -972,7 +974,7 @@ async def test_method_delete(self, async_client: AsyncPostGrid) -> None: ) assert_matches_type(Postcard, postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_delete(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.postcards.with_raw_response.delete( @@ -984,7 +986,7 @@ async def test_raw_response_delete(self, async_client: AsyncPostGrid) -> None: postcard = await response.parse() assert_matches_type(Postcard, postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_delete(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.postcards.with_streaming_response.delete( @@ -998,7 +1000,7 @@ async def test_streaming_response_delete(self, async_client: AsyncPostGrid) -> N assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_delete(self, async_client: AsyncPostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -1006,7 +1008,7 @@ async def test_path_params_delete(self, async_client: AsyncPostGrid) -> None: "", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_retrieve_url(self, async_client: AsyncPostGrid) -> None: postcard = await async_client.print_mail.postcards.retrieve_url( @@ -1014,7 +1016,7 @@ async def test_method_retrieve_url(self, async_client: AsyncPostGrid) -> None: ) assert_matches_type(PostcardRetrieveURLResponse, postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_retrieve_url(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.postcards.with_raw_response.retrieve_url( @@ -1026,7 +1028,7 @@ async def test_raw_response_retrieve_url(self, async_client: AsyncPostGrid) -> N postcard = await response.parse() assert_matches_type(PostcardRetrieveURLResponse, postcard, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_retrieve_url(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.postcards.with_streaming_response.retrieve_url( @@ -1040,7 +1042,7 @@ async def test_streaming_response_retrieve_url(self, async_client: AsyncPostGrid assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_retrieve_url(self, async_client: AsyncPostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): diff --git a/tests/api_resources/print_mail/test_reports.py b/tests/api_resources/print_mail/test_reports.py index 5b6e854..9a51971 100644 --- a/tests/api_resources/print_mail/test_reports.py +++ b/tests/api_resources/print_mail/test_reports.py @@ -22,7 +22,7 @@ class TestReports: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create(self, client: PostGrid) -> None: report = client.print_mail.reports.create( @@ -30,7 +30,7 @@ def test_method_create(self, client: PostGrid) -> None: ) assert_matches_type(Report, report, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create_with_all_params(self, client: PostGrid) -> None: report = client.print_mail.reports.create( @@ -40,7 +40,7 @@ def test_method_create_with_all_params(self, client: PostGrid) -> None: ) assert_matches_type(Report, report, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create(self, client: PostGrid) -> None: response = client.print_mail.reports.with_raw_response.create( @@ -52,7 +52,7 @@ def test_raw_response_create(self, client: PostGrid) -> None: report = response.parse() assert_matches_type(Report, report, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create(self, client: PostGrid) -> None: with client.print_mail.reports.with_streaming_response.create( @@ -66,7 +66,7 @@ def test_streaming_response_create(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_retrieve(self, client: PostGrid) -> None: report = client.print_mail.reports.retrieve( @@ -74,7 +74,7 @@ def test_method_retrieve(self, client: PostGrid) -> None: ) assert_matches_type(Report, report, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_retrieve(self, client: PostGrid) -> None: response = client.print_mail.reports.with_raw_response.retrieve( @@ -86,7 +86,7 @@ def test_raw_response_retrieve(self, client: PostGrid) -> None: report = response.parse() assert_matches_type(Report, report, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_retrieve(self, client: PostGrid) -> None: with client.print_mail.reports.with_streaming_response.retrieve( @@ -100,7 +100,7 @@ def test_streaming_response_retrieve(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_retrieve(self, client: PostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -108,7 +108,7 @@ def test_path_params_retrieve(self, client: PostGrid) -> None: "", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_update(self, client: PostGrid) -> None: report = client.print_mail.reports.update( @@ -116,7 +116,7 @@ def test_method_update(self, client: PostGrid) -> None: ) assert_matches_type(Report, report, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_update_with_all_params(self, client: PostGrid) -> None: report = client.print_mail.reports.update( @@ -127,7 +127,7 @@ def test_method_update_with_all_params(self, client: PostGrid) -> None: ) assert_matches_type(Report, report, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_update(self, client: PostGrid) -> None: response = client.print_mail.reports.with_raw_response.update( @@ -139,7 +139,7 @@ def test_raw_response_update(self, client: PostGrid) -> None: report = response.parse() assert_matches_type(Report, report, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_update(self, client: PostGrid) -> None: with client.print_mail.reports.with_streaming_response.update( @@ -153,7 +153,7 @@ def test_streaming_response_update(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_update(self, client: PostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -161,13 +161,13 @@ def test_path_params_update(self, client: PostGrid) -> None: id="", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list(self, client: PostGrid) -> None: report = client.print_mail.reports.list() assert_matches_type(SyncSkipLimit[Report], report, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list_with_all_params(self, client: PostGrid) -> None: report = client.print_mail.reports.list( @@ -177,7 +177,7 @@ def test_method_list_with_all_params(self, client: PostGrid) -> None: ) assert_matches_type(SyncSkipLimit[Report], report, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_list(self, client: PostGrid) -> None: response = client.print_mail.reports.with_raw_response.list() @@ -187,7 +187,7 @@ def test_raw_response_list(self, client: PostGrid) -> None: report = response.parse() assert_matches_type(SyncSkipLimit[Report], report, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_list(self, client: PostGrid) -> None: with client.print_mail.reports.with_streaming_response.list() as response: @@ -199,7 +199,7 @@ def test_streaming_response_list(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_delete(self, client: PostGrid) -> None: report = client.print_mail.reports.delete( @@ -207,7 +207,7 @@ def test_method_delete(self, client: PostGrid) -> None: ) assert_matches_type(DeletedResponse, report, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_delete(self, client: PostGrid) -> None: response = client.print_mail.reports.with_raw_response.delete( @@ -219,7 +219,7 @@ def test_raw_response_delete(self, client: PostGrid) -> None: report = response.parse() assert_matches_type(DeletedResponse, report, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_delete(self, client: PostGrid) -> None: with client.print_mail.reports.with_streaming_response.delete( @@ -233,7 +233,7 @@ def test_streaming_response_delete(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_delete(self, client: PostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -241,7 +241,7 @@ def test_path_params_delete(self, client: PostGrid) -> None: "", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_sample(self, client: PostGrid) -> None: report = client.print_mail.reports.sample( @@ -249,7 +249,7 @@ def test_method_sample(self, client: PostGrid) -> None: ) assert_matches_type(ReportSample, report, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_sample_with_all_params(self, client: PostGrid) -> None: report = client.print_mail.reports.sample( @@ -259,7 +259,7 @@ def test_method_sample_with_all_params(self, client: PostGrid) -> None: ) assert_matches_type(ReportSample, report, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_sample(self, client: PostGrid) -> None: response = client.print_mail.reports.with_raw_response.sample( @@ -271,7 +271,7 @@ def test_raw_response_sample(self, client: PostGrid) -> None: report = response.parse() assert_matches_type(ReportSample, report, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_sample(self, client: PostGrid) -> None: with client.print_mail.reports.with_streaming_response.sample( @@ -287,9 +287,11 @@ def test_streaming_response_sample(self, client: PostGrid) -> None: class TestAsyncReports: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create(self, async_client: AsyncPostGrid) -> None: report = await async_client.print_mail.reports.create( @@ -297,7 +299,7 @@ async def test_method_create(self, async_client: AsyncPostGrid) -> None: ) assert_matches_type(Report, report, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create_with_all_params(self, async_client: AsyncPostGrid) -> None: report = await async_client.print_mail.reports.create( @@ -307,7 +309,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncPostGrid) ) assert_matches_type(Report, report, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.reports.with_raw_response.create( @@ -319,7 +321,7 @@ async def test_raw_response_create(self, async_client: AsyncPostGrid) -> None: report = await response.parse() assert_matches_type(Report, report, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.reports.with_streaming_response.create( @@ -333,7 +335,7 @@ async def test_streaming_response_create(self, async_client: AsyncPostGrid) -> N assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_retrieve(self, async_client: AsyncPostGrid) -> None: report = await async_client.print_mail.reports.retrieve( @@ -341,7 +343,7 @@ async def test_method_retrieve(self, async_client: AsyncPostGrid) -> None: ) assert_matches_type(Report, report, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_retrieve(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.reports.with_raw_response.retrieve( @@ -353,7 +355,7 @@ async def test_raw_response_retrieve(self, async_client: AsyncPostGrid) -> None: report = await response.parse() assert_matches_type(Report, report, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.reports.with_streaming_response.retrieve( @@ -367,7 +369,7 @@ async def test_streaming_response_retrieve(self, async_client: AsyncPostGrid) -> assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_retrieve(self, async_client: AsyncPostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -375,7 +377,7 @@ async def test_path_params_retrieve(self, async_client: AsyncPostGrid) -> None: "", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_update(self, async_client: AsyncPostGrid) -> None: report = await async_client.print_mail.reports.update( @@ -383,7 +385,7 @@ async def test_method_update(self, async_client: AsyncPostGrid) -> None: ) assert_matches_type(Report, report, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_update_with_all_params(self, async_client: AsyncPostGrid) -> None: report = await async_client.print_mail.reports.update( @@ -394,7 +396,7 @@ async def test_method_update_with_all_params(self, async_client: AsyncPostGrid) ) assert_matches_type(Report, report, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_update(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.reports.with_raw_response.update( @@ -406,7 +408,7 @@ async def test_raw_response_update(self, async_client: AsyncPostGrid) -> None: report = await response.parse() assert_matches_type(Report, report, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_update(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.reports.with_streaming_response.update( @@ -420,7 +422,7 @@ async def test_streaming_response_update(self, async_client: AsyncPostGrid) -> N assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_update(self, async_client: AsyncPostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -428,13 +430,13 @@ async def test_path_params_update(self, async_client: AsyncPostGrid) -> None: id="", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list(self, async_client: AsyncPostGrid) -> None: report = await async_client.print_mail.reports.list() assert_matches_type(AsyncSkipLimit[Report], report, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list_with_all_params(self, async_client: AsyncPostGrid) -> None: report = await async_client.print_mail.reports.list( @@ -444,7 +446,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncPostGrid) -> ) assert_matches_type(AsyncSkipLimit[Report], report, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_list(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.reports.with_raw_response.list() @@ -454,7 +456,7 @@ async def test_raw_response_list(self, async_client: AsyncPostGrid) -> None: report = await response.parse() assert_matches_type(AsyncSkipLimit[Report], report, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_list(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.reports.with_streaming_response.list() as response: @@ -466,7 +468,7 @@ async def test_streaming_response_list(self, async_client: AsyncPostGrid) -> Non assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_delete(self, async_client: AsyncPostGrid) -> None: report = await async_client.print_mail.reports.delete( @@ -474,7 +476,7 @@ async def test_method_delete(self, async_client: AsyncPostGrid) -> None: ) assert_matches_type(DeletedResponse, report, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_delete(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.reports.with_raw_response.delete( @@ -486,7 +488,7 @@ async def test_raw_response_delete(self, async_client: AsyncPostGrid) -> None: report = await response.parse() assert_matches_type(DeletedResponse, report, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_delete(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.reports.with_streaming_response.delete( @@ -500,7 +502,7 @@ async def test_streaming_response_delete(self, async_client: AsyncPostGrid) -> N assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_delete(self, async_client: AsyncPostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -508,7 +510,7 @@ async def test_path_params_delete(self, async_client: AsyncPostGrid) -> None: "", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_sample(self, async_client: AsyncPostGrid) -> None: report = await async_client.print_mail.reports.sample( @@ -516,7 +518,7 @@ async def test_method_sample(self, async_client: AsyncPostGrid) -> None: ) assert_matches_type(ReportSample, report, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_sample_with_all_params(self, async_client: AsyncPostGrid) -> None: report = await async_client.print_mail.reports.sample( @@ -526,7 +528,7 @@ async def test_method_sample_with_all_params(self, async_client: AsyncPostGrid) ) assert_matches_type(ReportSample, report, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_sample(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.reports.with_raw_response.sample( @@ -538,7 +540,7 @@ async def test_raw_response_sample(self, async_client: AsyncPostGrid) -> None: report = await response.parse() assert_matches_type(ReportSample, report, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_sample(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.reports.with_streaming_response.sample( diff --git a/tests/api_resources/print_mail/test_self_mailers.py b/tests/api_resources/print_mail/test_self_mailers.py index cf4dfd9..2e400f3 100644 --- a/tests/api_resources/print_mail/test_self_mailers.py +++ b/tests/api_resources/print_mail/test_self_mailers.py @@ -22,7 +22,7 @@ class TestSelfMailers: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create_overload_1(self, client: PostGrid) -> None: self_mailer = client.print_mail.self_mailers.create( @@ -42,7 +42,7 @@ def test_method_create_overload_1(self, client: PostGrid) -> None: ) assert_matches_type(SelfMailer, self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create_with_all_params_overload_1(self, client: PostGrid) -> None: self_mailer = client.print_mail.self_mailers.create( @@ -93,7 +93,7 @@ def test_method_create_with_all_params_overload_1(self, client: PostGrid) -> Non ) assert_matches_type(SelfMailer, self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create_overload_1(self, client: PostGrid) -> None: response = client.print_mail.self_mailers.with_raw_response.create( @@ -117,7 +117,7 @@ def test_raw_response_create_overload_1(self, client: PostGrid) -> None: self_mailer = response.parse() assert_matches_type(SelfMailer, self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create_overload_1(self, client: PostGrid) -> None: with client.print_mail.self_mailers.with_streaming_response.create( @@ -143,7 +143,7 @@ def test_streaming_response_create_overload_1(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create_overload_2(self, client: PostGrid) -> None: self_mailer = client.print_mail.self_mailers.create( @@ -152,7 +152,7 @@ def test_method_create_overload_2(self, client: PostGrid) -> None: ) assert_matches_type(SelfMailer, self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create_overload_2(self, client: PostGrid) -> None: response = client.print_mail.self_mailers.with_raw_response.create( @@ -165,7 +165,7 @@ def test_raw_response_create_overload_2(self, client: PostGrid) -> None: self_mailer = response.parse() assert_matches_type(SelfMailer, self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create_overload_2(self, client: PostGrid) -> None: with client.print_mail.self_mailers.with_streaming_response.create( @@ -180,7 +180,7 @@ def test_streaming_response_create_overload_2(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create_overload_3(self, client: PostGrid) -> None: self_mailer = client.print_mail.self_mailers.create( @@ -199,7 +199,7 @@ def test_method_create_overload_3(self, client: PostGrid) -> None: ) assert_matches_type(SelfMailer, self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create_with_all_params_overload_3(self, client: PostGrid) -> None: self_mailer = client.print_mail.self_mailers.create( @@ -249,7 +249,7 @@ def test_method_create_with_all_params_overload_3(self, client: PostGrid) -> Non ) assert_matches_type(SelfMailer, self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create_overload_3(self, client: PostGrid) -> None: response = client.print_mail.self_mailers.with_raw_response.create( @@ -272,7 +272,7 @@ def test_raw_response_create_overload_3(self, client: PostGrid) -> None: self_mailer = response.parse() assert_matches_type(SelfMailer, self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create_overload_3(self, client: PostGrid) -> None: with client.print_mail.self_mailers.with_streaming_response.create( @@ -297,7 +297,7 @@ def test_streaming_response_create_overload_3(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create_overload_4(self, client: PostGrid) -> None: self_mailer = client.print_mail.self_mailers.create( @@ -316,7 +316,7 @@ def test_method_create_overload_4(self, client: PostGrid) -> None: ) assert_matches_type(SelfMailer, self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create_with_all_params_overload_4(self, client: PostGrid) -> None: self_mailer = client.print_mail.self_mailers.create( @@ -366,7 +366,7 @@ def test_method_create_with_all_params_overload_4(self, client: PostGrid) -> Non ) assert_matches_type(SelfMailer, self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create_overload_4(self, client: PostGrid) -> None: response = client.print_mail.self_mailers.with_raw_response.create( @@ -389,7 +389,7 @@ def test_raw_response_create_overload_4(self, client: PostGrid) -> None: self_mailer = response.parse() assert_matches_type(SelfMailer, self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create_overload_4(self, client: PostGrid) -> None: with client.print_mail.self_mailers.with_streaming_response.create( @@ -414,7 +414,7 @@ def test_streaming_response_create_overload_4(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_retrieve(self, client: PostGrid) -> None: self_mailer = client.print_mail.self_mailers.retrieve( @@ -422,7 +422,7 @@ def test_method_retrieve(self, client: PostGrid) -> None: ) assert_matches_type(SelfMailer, self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_retrieve(self, client: PostGrid) -> None: response = client.print_mail.self_mailers.with_raw_response.retrieve( @@ -434,7 +434,7 @@ def test_raw_response_retrieve(self, client: PostGrid) -> None: self_mailer = response.parse() assert_matches_type(SelfMailer, self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_retrieve(self, client: PostGrid) -> None: with client.print_mail.self_mailers.with_streaming_response.retrieve( @@ -448,7 +448,7 @@ def test_streaming_response_retrieve(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_retrieve(self, client: PostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -456,13 +456,13 @@ def test_path_params_retrieve(self, client: PostGrid) -> None: "", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list(self, client: PostGrid) -> None: self_mailer = client.print_mail.self_mailers.list() assert_matches_type(SyncSkipLimit[SelfMailer], self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list_with_all_params(self, client: PostGrid) -> None: self_mailer = client.print_mail.self_mailers.list( @@ -472,7 +472,7 @@ def test_method_list_with_all_params(self, client: PostGrid) -> None: ) assert_matches_type(SyncSkipLimit[SelfMailer], self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_list(self, client: PostGrid) -> None: response = client.print_mail.self_mailers.with_raw_response.list() @@ -482,7 +482,7 @@ def test_raw_response_list(self, client: PostGrid) -> None: self_mailer = response.parse() assert_matches_type(SyncSkipLimit[SelfMailer], self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_list(self, client: PostGrid) -> None: with client.print_mail.self_mailers.with_streaming_response.list() as response: @@ -494,7 +494,7 @@ def test_streaming_response_list(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_delete(self, client: PostGrid) -> None: self_mailer = client.print_mail.self_mailers.delete( @@ -502,7 +502,7 @@ def test_method_delete(self, client: PostGrid) -> None: ) assert_matches_type(SelfMailer, self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_delete(self, client: PostGrid) -> None: response = client.print_mail.self_mailers.with_raw_response.delete( @@ -514,7 +514,7 @@ def test_raw_response_delete(self, client: PostGrid) -> None: self_mailer = response.parse() assert_matches_type(SelfMailer, self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_delete(self, client: PostGrid) -> None: with client.print_mail.self_mailers.with_streaming_response.delete( @@ -528,7 +528,7 @@ def test_streaming_response_delete(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_delete(self, client: PostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -536,7 +536,7 @@ def test_path_params_delete(self, client: PostGrid) -> None: "", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_retrieve_url(self, client: PostGrid) -> None: self_mailer = client.print_mail.self_mailers.retrieve_url( @@ -544,7 +544,7 @@ def test_method_retrieve_url(self, client: PostGrid) -> None: ) assert_matches_type(SelfMailerRetrieveURLResponse, self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_retrieve_url(self, client: PostGrid) -> None: response = client.print_mail.self_mailers.with_raw_response.retrieve_url( @@ -556,7 +556,7 @@ def test_raw_response_retrieve_url(self, client: PostGrid) -> None: self_mailer = response.parse() assert_matches_type(SelfMailerRetrieveURLResponse, self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_retrieve_url(self, client: PostGrid) -> None: with client.print_mail.self_mailers.with_streaming_response.retrieve_url( @@ -570,7 +570,7 @@ def test_streaming_response_retrieve_url(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_retrieve_url(self, client: PostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -580,9 +580,11 @@ def test_path_params_retrieve_url(self, client: PostGrid) -> None: class TestAsyncSelfMailers: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create_overload_1(self, async_client: AsyncPostGrid) -> None: self_mailer = await async_client.print_mail.self_mailers.create( @@ -602,7 +604,7 @@ async def test_method_create_overload_1(self, async_client: AsyncPostGrid) -> No ) assert_matches_type(SelfMailer, self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create_with_all_params_overload_1(self, async_client: AsyncPostGrid) -> None: self_mailer = await async_client.print_mail.self_mailers.create( @@ -653,7 +655,7 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn ) assert_matches_type(SelfMailer, self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create_overload_1(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.self_mailers.with_raw_response.create( @@ -677,7 +679,7 @@ async def test_raw_response_create_overload_1(self, async_client: AsyncPostGrid) self_mailer = await response.parse() assert_matches_type(SelfMailer, self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create_overload_1(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.self_mailers.with_streaming_response.create( @@ -703,7 +705,7 @@ async def test_streaming_response_create_overload_1(self, async_client: AsyncPos assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create_overload_2(self, async_client: AsyncPostGrid) -> None: self_mailer = await async_client.print_mail.self_mailers.create( @@ -712,7 +714,7 @@ async def test_method_create_overload_2(self, async_client: AsyncPostGrid) -> No ) assert_matches_type(SelfMailer, self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create_overload_2(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.self_mailers.with_raw_response.create( @@ -725,7 +727,7 @@ async def test_raw_response_create_overload_2(self, async_client: AsyncPostGrid) self_mailer = await response.parse() assert_matches_type(SelfMailer, self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create_overload_2(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.self_mailers.with_streaming_response.create( @@ -740,7 +742,7 @@ async def test_streaming_response_create_overload_2(self, async_client: AsyncPos assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create_overload_3(self, async_client: AsyncPostGrid) -> None: self_mailer = await async_client.print_mail.self_mailers.create( @@ -759,7 +761,7 @@ async def test_method_create_overload_3(self, async_client: AsyncPostGrid) -> No ) assert_matches_type(SelfMailer, self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create_with_all_params_overload_3(self, async_client: AsyncPostGrid) -> None: self_mailer = await async_client.print_mail.self_mailers.create( @@ -809,7 +811,7 @@ async def test_method_create_with_all_params_overload_3(self, async_client: Asyn ) assert_matches_type(SelfMailer, self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create_overload_3(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.self_mailers.with_raw_response.create( @@ -832,7 +834,7 @@ async def test_raw_response_create_overload_3(self, async_client: AsyncPostGrid) self_mailer = await response.parse() assert_matches_type(SelfMailer, self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create_overload_3(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.self_mailers.with_streaming_response.create( @@ -857,7 +859,7 @@ async def test_streaming_response_create_overload_3(self, async_client: AsyncPos assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create_overload_4(self, async_client: AsyncPostGrid) -> None: self_mailer = await async_client.print_mail.self_mailers.create( @@ -876,7 +878,7 @@ async def test_method_create_overload_4(self, async_client: AsyncPostGrid) -> No ) assert_matches_type(SelfMailer, self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create_with_all_params_overload_4(self, async_client: AsyncPostGrid) -> None: self_mailer = await async_client.print_mail.self_mailers.create( @@ -926,7 +928,7 @@ async def test_method_create_with_all_params_overload_4(self, async_client: Asyn ) assert_matches_type(SelfMailer, self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create_overload_4(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.self_mailers.with_raw_response.create( @@ -949,7 +951,7 @@ async def test_raw_response_create_overload_4(self, async_client: AsyncPostGrid) self_mailer = await response.parse() assert_matches_type(SelfMailer, self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create_overload_4(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.self_mailers.with_streaming_response.create( @@ -974,7 +976,7 @@ async def test_streaming_response_create_overload_4(self, async_client: AsyncPos assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_retrieve(self, async_client: AsyncPostGrid) -> None: self_mailer = await async_client.print_mail.self_mailers.retrieve( @@ -982,7 +984,7 @@ async def test_method_retrieve(self, async_client: AsyncPostGrid) -> None: ) assert_matches_type(SelfMailer, self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_retrieve(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.self_mailers.with_raw_response.retrieve( @@ -994,7 +996,7 @@ async def test_raw_response_retrieve(self, async_client: AsyncPostGrid) -> None: self_mailer = await response.parse() assert_matches_type(SelfMailer, self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.self_mailers.with_streaming_response.retrieve( @@ -1008,7 +1010,7 @@ async def test_streaming_response_retrieve(self, async_client: AsyncPostGrid) -> assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_retrieve(self, async_client: AsyncPostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -1016,13 +1018,13 @@ async def test_path_params_retrieve(self, async_client: AsyncPostGrid) -> None: "", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list(self, async_client: AsyncPostGrid) -> None: self_mailer = await async_client.print_mail.self_mailers.list() assert_matches_type(AsyncSkipLimit[SelfMailer], self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list_with_all_params(self, async_client: AsyncPostGrid) -> None: self_mailer = await async_client.print_mail.self_mailers.list( @@ -1032,7 +1034,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncPostGrid) -> ) assert_matches_type(AsyncSkipLimit[SelfMailer], self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_list(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.self_mailers.with_raw_response.list() @@ -1042,7 +1044,7 @@ async def test_raw_response_list(self, async_client: AsyncPostGrid) -> None: self_mailer = await response.parse() assert_matches_type(AsyncSkipLimit[SelfMailer], self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_list(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.self_mailers.with_streaming_response.list() as response: @@ -1054,7 +1056,7 @@ async def test_streaming_response_list(self, async_client: AsyncPostGrid) -> Non assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_delete(self, async_client: AsyncPostGrid) -> None: self_mailer = await async_client.print_mail.self_mailers.delete( @@ -1062,7 +1064,7 @@ async def test_method_delete(self, async_client: AsyncPostGrid) -> None: ) assert_matches_type(SelfMailer, self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_delete(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.self_mailers.with_raw_response.delete( @@ -1074,7 +1076,7 @@ async def test_raw_response_delete(self, async_client: AsyncPostGrid) -> None: self_mailer = await response.parse() assert_matches_type(SelfMailer, self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_delete(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.self_mailers.with_streaming_response.delete( @@ -1088,7 +1090,7 @@ async def test_streaming_response_delete(self, async_client: AsyncPostGrid) -> N assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_delete(self, async_client: AsyncPostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -1096,7 +1098,7 @@ async def test_path_params_delete(self, async_client: AsyncPostGrid) -> None: "", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_retrieve_url(self, async_client: AsyncPostGrid) -> None: self_mailer = await async_client.print_mail.self_mailers.retrieve_url( @@ -1104,7 +1106,7 @@ async def test_method_retrieve_url(self, async_client: AsyncPostGrid) -> None: ) assert_matches_type(SelfMailerRetrieveURLResponse, self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_retrieve_url(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.self_mailers.with_raw_response.retrieve_url( @@ -1116,7 +1118,7 @@ async def test_raw_response_retrieve_url(self, async_client: AsyncPostGrid) -> N self_mailer = await response.parse() assert_matches_type(SelfMailerRetrieveURLResponse, self_mailer, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_retrieve_url(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.self_mailers.with_streaming_response.retrieve_url( @@ -1130,7 +1132,7 @@ async def test_streaming_response_retrieve_url(self, async_client: AsyncPostGrid assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_retrieve_url(self, async_client: AsyncPostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): diff --git a/tests/api_resources/print_mail/test_sub_organizations.py b/tests/api_resources/print_mail/test_sub_organizations.py index 7a90184..53726d9 100644 --- a/tests/api_resources/print_mail/test_sub_organizations.py +++ b/tests/api_resources/print_mail/test_sub_organizations.py @@ -22,7 +22,7 @@ class TestSubOrganizations: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_retrieve(self, client: PostGrid) -> None: sub_organization = client.print_mail.sub_organizations.retrieve( @@ -30,7 +30,7 @@ def test_method_retrieve(self, client: PostGrid) -> None: ) assert_matches_type(SubOrganization, sub_organization, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_retrieve(self, client: PostGrid) -> None: response = client.print_mail.sub_organizations.with_raw_response.retrieve( @@ -42,7 +42,7 @@ def test_raw_response_retrieve(self, client: PostGrid) -> None: sub_organization = response.parse() assert_matches_type(SubOrganization, sub_organization, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_retrieve(self, client: PostGrid) -> None: with client.print_mail.sub_organizations.with_streaming_response.retrieve( @@ -56,7 +56,7 @@ def test_streaming_response_retrieve(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_retrieve(self, client: PostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -64,7 +64,7 @@ def test_path_params_retrieve(self, client: PostGrid) -> None: "", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_update(self, client: PostGrid) -> None: sub_organization = client.print_mail.sub_organizations.update( @@ -76,7 +76,7 @@ def test_method_update(self, client: PostGrid) -> None: ) assert_matches_type(SubOrganizationUpdateResponse, sub_organization, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_update_with_all_params(self, client: PostGrid) -> None: sub_organization = client.print_mail.sub_organizations.update( @@ -89,7 +89,7 @@ def test_method_update_with_all_params(self, client: PostGrid) -> None: ) assert_matches_type(SubOrganizationUpdateResponse, sub_organization, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_update(self, client: PostGrid) -> None: response = client.print_mail.sub_organizations.with_raw_response.update( @@ -105,7 +105,7 @@ def test_raw_response_update(self, client: PostGrid) -> None: sub_organization = response.parse() assert_matches_type(SubOrganizationUpdateResponse, sub_organization, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_update(self, client: PostGrid) -> None: with client.print_mail.sub_organizations.with_streaming_response.update( @@ -123,13 +123,13 @@ def test_streaming_response_update(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list(self, client: PostGrid) -> None: sub_organization = client.print_mail.sub_organizations.list() assert_matches_type(SyncSkipLimit[SubOrganization], sub_organization, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list_with_all_params(self, client: PostGrid) -> None: sub_organization = client.print_mail.sub_organizations.list( @@ -139,7 +139,7 @@ def test_method_list_with_all_params(self, client: PostGrid) -> None: ) assert_matches_type(SyncSkipLimit[SubOrganization], sub_organization, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_list(self, client: PostGrid) -> None: response = client.print_mail.sub_organizations.with_raw_response.list() @@ -149,7 +149,7 @@ def test_raw_response_list(self, client: PostGrid) -> None: sub_organization = response.parse() assert_matches_type(SyncSkipLimit[SubOrganization], sub_organization, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_list(self, client: PostGrid) -> None: with client.print_mail.sub_organizations.with_streaming_response.list() as response: @@ -161,7 +161,7 @@ def test_streaming_response_list(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_retrieve_users(self, client: PostGrid) -> None: sub_organization = client.print_mail.sub_organizations.retrieve_users( @@ -169,7 +169,7 @@ def test_method_retrieve_users(self, client: PostGrid) -> None: ) assert_matches_type(SubOrganizationRetrieveUsersResponse, sub_organization, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_retrieve_users_with_all_params(self, client: PostGrid) -> None: sub_organization = client.print_mail.sub_organizations.retrieve_users( @@ -180,7 +180,7 @@ def test_method_retrieve_users_with_all_params(self, client: PostGrid) -> None: ) assert_matches_type(SubOrganizationRetrieveUsersResponse, sub_organization, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_retrieve_users(self, client: PostGrid) -> None: response = client.print_mail.sub_organizations.with_raw_response.retrieve_users( @@ -192,7 +192,7 @@ def test_raw_response_retrieve_users(self, client: PostGrid) -> None: sub_organization = response.parse() assert_matches_type(SubOrganizationRetrieveUsersResponse, sub_organization, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_retrieve_users(self, client: PostGrid) -> None: with client.print_mail.sub_organizations.with_streaming_response.retrieve_users( @@ -206,7 +206,7 @@ def test_streaming_response_retrieve_users(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_retrieve_users(self, client: PostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -216,9 +216,11 @@ def test_path_params_retrieve_users(self, client: PostGrid) -> None: class TestAsyncSubOrganizations: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_retrieve(self, async_client: AsyncPostGrid) -> None: sub_organization = await async_client.print_mail.sub_organizations.retrieve( @@ -226,7 +228,7 @@ async def test_method_retrieve(self, async_client: AsyncPostGrid) -> None: ) assert_matches_type(SubOrganization, sub_organization, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_retrieve(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.sub_organizations.with_raw_response.retrieve( @@ -238,7 +240,7 @@ async def test_raw_response_retrieve(self, async_client: AsyncPostGrid) -> None: sub_organization = await response.parse() assert_matches_type(SubOrganization, sub_organization, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.sub_organizations.with_streaming_response.retrieve( @@ -252,7 +254,7 @@ async def test_streaming_response_retrieve(self, async_client: AsyncPostGrid) -> assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_retrieve(self, async_client: AsyncPostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -260,7 +262,7 @@ async def test_path_params_retrieve(self, async_client: AsyncPostGrid) -> None: "", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_update(self, async_client: AsyncPostGrid) -> None: sub_organization = await async_client.print_mail.sub_organizations.update( @@ -272,7 +274,7 @@ async def test_method_update(self, async_client: AsyncPostGrid) -> None: ) assert_matches_type(SubOrganizationUpdateResponse, sub_organization, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_update_with_all_params(self, async_client: AsyncPostGrid) -> None: sub_organization = await async_client.print_mail.sub_organizations.update( @@ -285,7 +287,7 @@ async def test_method_update_with_all_params(self, async_client: AsyncPostGrid) ) assert_matches_type(SubOrganizationUpdateResponse, sub_organization, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_update(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.sub_organizations.with_raw_response.update( @@ -301,7 +303,7 @@ async def test_raw_response_update(self, async_client: AsyncPostGrid) -> None: sub_organization = await response.parse() assert_matches_type(SubOrganizationUpdateResponse, sub_organization, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_update(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.sub_organizations.with_streaming_response.update( @@ -319,13 +321,13 @@ async def test_streaming_response_update(self, async_client: AsyncPostGrid) -> N assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list(self, async_client: AsyncPostGrid) -> None: sub_organization = await async_client.print_mail.sub_organizations.list() assert_matches_type(AsyncSkipLimit[SubOrganization], sub_organization, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list_with_all_params(self, async_client: AsyncPostGrid) -> None: sub_organization = await async_client.print_mail.sub_organizations.list( @@ -335,7 +337,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncPostGrid) -> ) assert_matches_type(AsyncSkipLimit[SubOrganization], sub_organization, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_list(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.sub_organizations.with_raw_response.list() @@ -345,7 +347,7 @@ async def test_raw_response_list(self, async_client: AsyncPostGrid) -> None: sub_organization = await response.parse() assert_matches_type(AsyncSkipLimit[SubOrganization], sub_organization, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_list(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.sub_organizations.with_streaming_response.list() as response: @@ -357,7 +359,7 @@ async def test_streaming_response_list(self, async_client: AsyncPostGrid) -> Non assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_retrieve_users(self, async_client: AsyncPostGrid) -> None: sub_organization = await async_client.print_mail.sub_organizations.retrieve_users( @@ -365,7 +367,7 @@ async def test_method_retrieve_users(self, async_client: AsyncPostGrid) -> None: ) assert_matches_type(SubOrganizationRetrieveUsersResponse, sub_organization, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_retrieve_users_with_all_params(self, async_client: AsyncPostGrid) -> None: sub_organization = await async_client.print_mail.sub_organizations.retrieve_users( @@ -376,7 +378,7 @@ async def test_method_retrieve_users_with_all_params(self, async_client: AsyncPo ) assert_matches_type(SubOrganizationRetrieveUsersResponse, sub_organization, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_retrieve_users(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.sub_organizations.with_raw_response.retrieve_users( @@ -388,7 +390,7 @@ async def test_raw_response_retrieve_users(self, async_client: AsyncPostGrid) -> sub_organization = await response.parse() assert_matches_type(SubOrganizationRetrieveUsersResponse, sub_organization, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_retrieve_users(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.sub_organizations.with_streaming_response.retrieve_users( @@ -402,7 +404,7 @@ async def test_streaming_response_retrieve_users(self, async_client: AsyncPostGr assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_retrieve_users(self, async_client: AsyncPostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): diff --git a/tests/api_resources/print_mail/test_templates.py b/tests/api_resources/print_mail/test_templates.py index d486ace..858c2fd 100644 --- a/tests/api_resources/print_mail/test_templates.py +++ b/tests/api_resources/print_mail/test_templates.py @@ -21,13 +21,13 @@ class TestTemplates: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create(self, client: PostGrid) -> None: template = client.print_mail.templates.create() assert_matches_type(Template, template, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_create_with_all_params(self, client: PostGrid) -> None: template = client.print_mail.templates.create( @@ -37,7 +37,7 @@ def test_method_create_with_all_params(self, client: PostGrid) -> None: ) assert_matches_type(Template, template, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_create(self, client: PostGrid) -> None: response = client.print_mail.templates.with_raw_response.create() @@ -47,7 +47,7 @@ def test_raw_response_create(self, client: PostGrid) -> None: template = response.parse() assert_matches_type(Template, template, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_create(self, client: PostGrid) -> None: with client.print_mail.templates.with_streaming_response.create() as response: @@ -59,7 +59,7 @@ def test_streaming_response_create(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_retrieve(self, client: PostGrid) -> None: template = client.print_mail.templates.retrieve( @@ -67,7 +67,7 @@ def test_method_retrieve(self, client: PostGrid) -> None: ) assert_matches_type(Template, template, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_retrieve(self, client: PostGrid) -> None: response = client.print_mail.templates.with_raw_response.retrieve( @@ -79,7 +79,7 @@ def test_raw_response_retrieve(self, client: PostGrid) -> None: template = response.parse() assert_matches_type(Template, template, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_retrieve(self, client: PostGrid) -> None: with client.print_mail.templates.with_streaming_response.retrieve( @@ -93,7 +93,7 @@ def test_streaming_response_retrieve(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_retrieve(self, client: PostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -101,7 +101,7 @@ def test_path_params_retrieve(self, client: PostGrid) -> None: "", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_update(self, client: PostGrid) -> None: template = client.print_mail.templates.update( @@ -109,7 +109,7 @@ def test_method_update(self, client: PostGrid) -> None: ) assert_matches_type(Template, template, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_update_with_all_params(self, client: PostGrid) -> None: template = client.print_mail.templates.update( @@ -120,7 +120,7 @@ def test_method_update_with_all_params(self, client: PostGrid) -> None: ) assert_matches_type(Template, template, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_update(self, client: PostGrid) -> None: response = client.print_mail.templates.with_raw_response.update( @@ -132,7 +132,7 @@ def test_raw_response_update(self, client: PostGrid) -> None: template = response.parse() assert_matches_type(Template, template, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_update(self, client: PostGrid) -> None: with client.print_mail.templates.with_streaming_response.update( @@ -146,7 +146,7 @@ def test_streaming_response_update(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_update(self, client: PostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -154,13 +154,13 @@ def test_path_params_update(self, client: PostGrid) -> None: id="", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list(self, client: PostGrid) -> None: template = client.print_mail.templates.list() assert_matches_type(SyncSkipLimit[Template], template, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_list_with_all_params(self, client: PostGrid) -> None: template = client.print_mail.templates.list( @@ -170,7 +170,7 @@ def test_method_list_with_all_params(self, client: PostGrid) -> None: ) assert_matches_type(SyncSkipLimit[Template], template, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_list(self, client: PostGrid) -> None: response = client.print_mail.templates.with_raw_response.list() @@ -180,7 +180,7 @@ def test_raw_response_list(self, client: PostGrid) -> None: template = response.parse() assert_matches_type(SyncSkipLimit[Template], template, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_list(self, client: PostGrid) -> None: with client.print_mail.templates.with_streaming_response.list() as response: @@ -192,7 +192,7 @@ def test_streaming_response_list(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_delete(self, client: PostGrid) -> None: template = client.print_mail.templates.delete( @@ -200,7 +200,7 @@ def test_method_delete(self, client: PostGrid) -> None: ) assert_matches_type(TemplateDeleteResponse, template, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_delete(self, client: PostGrid) -> None: response = client.print_mail.templates.with_raw_response.delete( @@ -212,7 +212,7 @@ def test_raw_response_delete(self, client: PostGrid) -> None: template = response.parse() assert_matches_type(TemplateDeleteResponse, template, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_delete(self, client: PostGrid) -> None: with client.print_mail.templates.with_streaming_response.delete( @@ -226,7 +226,7 @@ def test_streaming_response_delete(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_path_params_delete(self, client: PostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -236,15 +236,17 @@ def test_path_params_delete(self, client: PostGrid) -> None: class TestAsyncTemplates: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create(self, async_client: AsyncPostGrid) -> None: template = await async_client.print_mail.templates.create() assert_matches_type(Template, template, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_create_with_all_params(self, async_client: AsyncPostGrid) -> None: template = await async_client.print_mail.templates.create( @@ -254,7 +256,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncPostGrid) ) assert_matches_type(Template, template, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_create(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.templates.with_raw_response.create() @@ -264,7 +266,7 @@ async def test_raw_response_create(self, async_client: AsyncPostGrid) -> None: template = await response.parse() assert_matches_type(Template, template, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_create(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.templates.with_streaming_response.create() as response: @@ -276,7 +278,7 @@ async def test_streaming_response_create(self, async_client: AsyncPostGrid) -> N assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_retrieve(self, async_client: AsyncPostGrid) -> None: template = await async_client.print_mail.templates.retrieve( @@ -284,7 +286,7 @@ async def test_method_retrieve(self, async_client: AsyncPostGrid) -> None: ) assert_matches_type(Template, template, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_retrieve(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.templates.with_raw_response.retrieve( @@ -296,7 +298,7 @@ async def test_raw_response_retrieve(self, async_client: AsyncPostGrid) -> None: template = await response.parse() assert_matches_type(Template, template, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.templates.with_streaming_response.retrieve( @@ -310,7 +312,7 @@ async def test_streaming_response_retrieve(self, async_client: AsyncPostGrid) -> assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_retrieve(self, async_client: AsyncPostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -318,7 +320,7 @@ async def test_path_params_retrieve(self, async_client: AsyncPostGrid) -> None: "", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_update(self, async_client: AsyncPostGrid) -> None: template = await async_client.print_mail.templates.update( @@ -326,7 +328,7 @@ async def test_method_update(self, async_client: AsyncPostGrid) -> None: ) assert_matches_type(Template, template, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_update_with_all_params(self, async_client: AsyncPostGrid) -> None: template = await async_client.print_mail.templates.update( @@ -337,7 +339,7 @@ async def test_method_update_with_all_params(self, async_client: AsyncPostGrid) ) assert_matches_type(Template, template, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_update(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.templates.with_raw_response.update( @@ -349,7 +351,7 @@ async def test_raw_response_update(self, async_client: AsyncPostGrid) -> None: template = await response.parse() assert_matches_type(Template, template, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_update(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.templates.with_streaming_response.update( @@ -363,7 +365,7 @@ async def test_streaming_response_update(self, async_client: AsyncPostGrid) -> N assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_update(self, async_client: AsyncPostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): @@ -371,13 +373,13 @@ async def test_path_params_update(self, async_client: AsyncPostGrid) -> None: id="", ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list(self, async_client: AsyncPostGrid) -> None: template = await async_client.print_mail.templates.list() assert_matches_type(AsyncSkipLimit[Template], template, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_list_with_all_params(self, async_client: AsyncPostGrid) -> None: template = await async_client.print_mail.templates.list( @@ -387,7 +389,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncPostGrid) -> ) assert_matches_type(AsyncSkipLimit[Template], template, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_list(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.templates.with_raw_response.list() @@ -397,7 +399,7 @@ async def test_raw_response_list(self, async_client: AsyncPostGrid) -> None: template = await response.parse() assert_matches_type(AsyncSkipLimit[Template], template, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_list(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.templates.with_streaming_response.list() as response: @@ -409,7 +411,7 @@ async def test_streaming_response_list(self, async_client: AsyncPostGrid) -> Non assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_delete(self, async_client: AsyncPostGrid) -> None: template = await async_client.print_mail.templates.delete( @@ -417,7 +419,7 @@ async def test_method_delete(self, async_client: AsyncPostGrid) -> None: ) assert_matches_type(TemplateDeleteResponse, template, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_delete(self, async_client: AsyncPostGrid) -> None: response = await async_client.print_mail.templates.with_raw_response.delete( @@ -429,7 +431,7 @@ async def test_raw_response_delete(self, async_client: AsyncPostGrid) -> None: template = await response.parse() assert_matches_type(TemplateDeleteResponse, template, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_delete(self, async_client: AsyncPostGrid) -> None: async with async_client.print_mail.templates.with_streaming_response.delete( @@ -443,7 +445,7 @@ async def test_streaming_response_delete(self, async_client: AsyncPostGrid) -> N assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_path_params_delete(self, async_client: AsyncPostGrid) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): diff --git a/tests/api_resources/test_address_verification.py b/tests/api_resources/test_address_verification.py index ea166f9..a753480 100644 --- a/tests/api_resources/test_address_verification.py +++ b/tests/api_resources/test_address_verification.py @@ -17,7 +17,7 @@ class TestAddressVerification: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_verify_overload_1(self, client: PostGrid) -> None: address_verification = client.address_verification.verify( @@ -25,7 +25,7 @@ def test_method_verify_overload_1(self, client: PostGrid) -> None: ) assert_matches_type(AddressVerificationVerifyResponse, address_verification, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_verify_with_all_params_overload_1(self, client: PostGrid) -> None: address_verification = client.address_verification.verify( @@ -36,7 +36,7 @@ def test_method_verify_with_all_params_overload_1(self, client: PostGrid) -> Non ) assert_matches_type(AddressVerificationVerifyResponse, address_verification, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_verify_overload_1(self, client: PostGrid) -> None: response = client.address_verification.with_raw_response.verify( @@ -48,7 +48,7 @@ def test_raw_response_verify_overload_1(self, client: PostGrid) -> None: address_verification = response.parse() assert_matches_type(AddressVerificationVerifyResponse, address_verification, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_verify_overload_1(self, client: PostGrid) -> None: with client.address_verification.with_streaming_response.verify( @@ -62,7 +62,7 @@ def test_streaming_response_verify_overload_1(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_verify_overload_2(self, client: PostGrid) -> None: address_verification = client.address_verification.verify( @@ -76,7 +76,7 @@ def test_method_verify_overload_2(self, client: PostGrid) -> None: ) assert_matches_type(AddressVerificationVerifyResponse, address_verification, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_verify_with_all_params_overload_2(self, client: PostGrid) -> None: address_verification = client.address_verification.verify( @@ -95,7 +95,7 @@ def test_method_verify_with_all_params_overload_2(self, client: PostGrid) -> Non ) assert_matches_type(AddressVerificationVerifyResponse, address_verification, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_verify_overload_2(self, client: PostGrid) -> None: response = client.address_verification.with_raw_response.verify( @@ -113,7 +113,7 @@ def test_raw_response_verify_overload_2(self, client: PostGrid) -> None: address_verification = response.parse() assert_matches_type(AddressVerificationVerifyResponse, address_verification, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_verify_overload_2(self, client: PostGrid) -> None: with client.address_verification.with_streaming_response.verify( @@ -135,9 +135,11 @@ def test_streaming_response_verify_overload_2(self, client: PostGrid) -> None: class TestAsyncAddressVerification: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_verify_overload_1(self, async_client: AsyncPostGrid) -> None: address_verification = await async_client.address_verification.verify( @@ -145,7 +147,7 @@ async def test_method_verify_overload_1(self, async_client: AsyncPostGrid) -> No ) assert_matches_type(AddressVerificationVerifyResponse, address_verification, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_verify_with_all_params_overload_1(self, async_client: AsyncPostGrid) -> None: address_verification = await async_client.address_verification.verify( @@ -156,7 +158,7 @@ async def test_method_verify_with_all_params_overload_1(self, async_client: Asyn ) assert_matches_type(AddressVerificationVerifyResponse, address_verification, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_verify_overload_1(self, async_client: AsyncPostGrid) -> None: response = await async_client.address_verification.with_raw_response.verify( @@ -168,7 +170,7 @@ async def test_raw_response_verify_overload_1(self, async_client: AsyncPostGrid) address_verification = await response.parse() assert_matches_type(AddressVerificationVerifyResponse, address_verification, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_verify_overload_1(self, async_client: AsyncPostGrid) -> None: async with async_client.address_verification.with_streaming_response.verify( @@ -182,7 +184,7 @@ async def test_streaming_response_verify_overload_1(self, async_client: AsyncPos assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_verify_overload_2(self, async_client: AsyncPostGrid) -> None: address_verification = await async_client.address_verification.verify( @@ -196,7 +198,7 @@ async def test_method_verify_overload_2(self, async_client: AsyncPostGrid) -> No ) assert_matches_type(AddressVerificationVerifyResponse, address_verification, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_verify_with_all_params_overload_2(self, async_client: AsyncPostGrid) -> None: address_verification = await async_client.address_verification.verify( @@ -215,7 +217,7 @@ async def test_method_verify_with_all_params_overload_2(self, async_client: Asyn ) assert_matches_type(AddressVerificationVerifyResponse, address_verification, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_verify_overload_2(self, async_client: AsyncPostGrid) -> None: response = await async_client.address_verification.with_raw_response.verify( @@ -233,7 +235,7 @@ async def test_raw_response_verify_overload_2(self, async_client: AsyncPostGrid) address_verification = await response.parse() assert_matches_type(AddressVerificationVerifyResponse, address_verification, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_verify_overload_2(self, async_client: AsyncPostGrid) -> None: async with async_client.address_verification.with_streaming_response.verify( diff --git a/tests/api_resources/test_intl_address_verification.py b/tests/api_resources/test_intl_address_verification.py index c0c800f..704b4ec 100644 --- a/tests/api_resources/test_intl_address_verification.py +++ b/tests/api_resources/test_intl_address_verification.py @@ -17,7 +17,7 @@ class TestIntlAddressVerification: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_verify_overload_1(self, client: PostGrid) -> None: intl_address_verification = client.intl_address_verification.verify( @@ -30,7 +30,7 @@ def test_method_verify_overload_1(self, client: PostGrid) -> None: ) assert_matches_type(IntlAddressVerificationVerifyResponse, intl_address_verification, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_verify_with_all_params_overload_1(self, client: PostGrid) -> None: intl_address_verification = client.intl_address_verification.verify( @@ -50,7 +50,7 @@ def test_method_verify_with_all_params_overload_1(self, client: PostGrid) -> Non ) assert_matches_type(IntlAddressVerificationVerifyResponse, intl_address_verification, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_verify_overload_1(self, client: PostGrid) -> None: response = client.intl_address_verification.with_raw_response.verify( @@ -67,7 +67,7 @@ def test_raw_response_verify_overload_1(self, client: PostGrid) -> None: intl_address_verification = response.parse() assert_matches_type(IntlAddressVerificationVerifyResponse, intl_address_verification, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_verify_overload_1(self, client: PostGrid) -> None: with client.intl_address_verification.with_streaming_response.verify( @@ -86,7 +86,7 @@ def test_streaming_response_verify_overload_1(self, client: PostGrid) -> None: assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_verify_overload_2(self, client: PostGrid) -> None: intl_address_verification = client.intl_address_verification.verify( @@ -94,7 +94,7 @@ def test_method_verify_overload_2(self, client: PostGrid) -> None: ) assert_matches_type(IntlAddressVerificationVerifyResponse, intl_address_verification, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_method_verify_with_all_params_overload_2(self, client: PostGrid) -> None: intl_address_verification = client.intl_address_verification.verify( @@ -105,7 +105,7 @@ def test_method_verify_with_all_params_overload_2(self, client: PostGrid) -> Non ) assert_matches_type(IntlAddressVerificationVerifyResponse, intl_address_verification, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_raw_response_verify_overload_2(self, client: PostGrid) -> None: response = client.intl_address_verification.with_raw_response.verify( @@ -117,7 +117,7 @@ def test_raw_response_verify_overload_2(self, client: PostGrid) -> None: intl_address_verification = response.parse() assert_matches_type(IntlAddressVerificationVerifyResponse, intl_address_verification, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize def test_streaming_response_verify_overload_2(self, client: PostGrid) -> None: with client.intl_address_verification.with_streaming_response.verify( @@ -133,9 +133,11 @@ def test_streaming_response_verify_overload_2(self, client: PostGrid) -> None: class TestAsyncIntlAddressVerification: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_verify_overload_1(self, async_client: AsyncPostGrid) -> None: intl_address_verification = await async_client.intl_address_verification.verify( @@ -148,7 +150,7 @@ async def test_method_verify_overload_1(self, async_client: AsyncPostGrid) -> No ) assert_matches_type(IntlAddressVerificationVerifyResponse, intl_address_verification, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_verify_with_all_params_overload_1(self, async_client: AsyncPostGrid) -> None: intl_address_verification = await async_client.intl_address_verification.verify( @@ -168,7 +170,7 @@ async def test_method_verify_with_all_params_overload_1(self, async_client: Asyn ) assert_matches_type(IntlAddressVerificationVerifyResponse, intl_address_verification, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_verify_overload_1(self, async_client: AsyncPostGrid) -> None: response = await async_client.intl_address_verification.with_raw_response.verify( @@ -185,7 +187,7 @@ async def test_raw_response_verify_overload_1(self, async_client: AsyncPostGrid) intl_address_verification = await response.parse() assert_matches_type(IntlAddressVerificationVerifyResponse, intl_address_verification, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_verify_overload_1(self, async_client: AsyncPostGrid) -> None: async with async_client.intl_address_verification.with_streaming_response.verify( @@ -204,7 +206,7 @@ async def test_streaming_response_verify_overload_1(self, async_client: AsyncPos assert cast(Any, response.is_closed) is True - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_verify_overload_2(self, async_client: AsyncPostGrid) -> None: intl_address_verification = await async_client.intl_address_verification.verify( @@ -212,7 +214,7 @@ async def test_method_verify_overload_2(self, async_client: AsyncPostGrid) -> No ) assert_matches_type(IntlAddressVerificationVerifyResponse, intl_address_verification, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_method_verify_with_all_params_overload_2(self, async_client: AsyncPostGrid) -> None: intl_address_verification = await async_client.intl_address_verification.verify( @@ -223,7 +225,7 @@ async def test_method_verify_with_all_params_overload_2(self, async_client: Asyn ) assert_matches_type(IntlAddressVerificationVerifyResponse, intl_address_verification, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_raw_response_verify_overload_2(self, async_client: AsyncPostGrid) -> None: response = await async_client.intl_address_verification.with_raw_response.verify( @@ -235,7 +237,7 @@ async def test_raw_response_verify_overload_2(self, async_client: AsyncPostGrid) intl_address_verification = await response.parse() assert_matches_type(IntlAddressVerificationVerifyResponse, intl_address_verification, path=["response"]) - @pytest.mark.skip() + @pytest.mark.skip(reason="Prism tests are disabled") @parametrize async def test_streaming_response_verify_overload_2(self, async_client: AsyncPostGrid) -> None: async with async_client.intl_address_verification.with_streaming_response.verify( diff --git a/tests/conftest.py b/tests/conftest.py index 07a401f..153d2cd 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,16 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + from __future__ import annotations import os import logging from typing import TYPE_CHECKING, Iterator, AsyncIterator +import httpx import pytest from pytest_asyncio import is_async_test -from postgrid import PostGrid, AsyncPostGrid +from postgrid import PostGrid, AsyncPostGrid, DefaultAioHttpClient +from postgrid._utils import is_dict if TYPE_CHECKING: - from _pytest.fixtures import FixtureRequest + from _pytest.fixtures import FixtureRequest # pyright: ignore[reportPrivateImportUsage] pytest.register_assert_rewrite("tests.utils") @@ -25,6 +29,19 @@ def pytest_collection_modifyitems(items: list[pytest.Function]) -> None: for async_test in pytest_asyncio_tests: async_test.add_marker(session_scope_marker, append=False) + # We skip tests that use both the aiohttp client and respx_mock as respx_mock + # doesn't support custom transports. + for item in items: + if "async_client" not in item.fixturenames or "respx_mock" not in item.fixturenames: + continue + + if not hasattr(item, "callspec"): + continue + + async_client_param = item.callspec.params.get("async_client") + if is_dict(async_client_param) and async_client_param.get("http_client") == "aiohttp": + item.add_marker(pytest.mark.skip(reason="aiohttp client is not compatible with respx_mock")) + base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -49,14 +66,29 @@ def client(request: FixtureRequest) -> Iterator[PostGrid]: @pytest.fixture(scope="session") async def async_client(request: FixtureRequest) -> AsyncIterator[AsyncPostGrid]: - strict = getattr(request, "param", True) - if not isinstance(strict, bool): - raise TypeError(f"Unexpected fixture parameter type {type(strict)}, expected {bool}") + param = getattr(request, "param", True) + + # defaults + strict = True + http_client: None | httpx.AsyncClient = None + + if isinstance(param, bool): + strict = param + elif is_dict(param): + strict = param.get("strict", True) + assert isinstance(strict, bool) + + http_client_type = param.get("http_client", "httpx") + if http_client_type == "aiohttp": + http_client = DefaultAioHttpClient() + else: + raise TypeError(f"Unexpected fixture parameter type {type(param)}, expected bool or dict") async with AsyncPostGrid( base_url=base_url, address_verification_api_key=address_verification_api_key, print_mail_api_key=print_mail_api_key, _strict_response_validation=strict, + http_client=http_client, ) as client: yield client diff --git a/tests/test_client.py b/tests/test_client.py index da88f4f..937363b 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -6,13 +6,10 @@ import os import sys import json -import time import asyncio import inspect -import subprocess import tracemalloc from typing import Any, Union, cast -from textwrap import dedent from unittest import mock from typing_extensions import Literal @@ -23,17 +20,19 @@ from postgrid import PostGrid, AsyncPostGrid, APIResponseValidationError from postgrid._types import Omit -from postgrid._utils import maybe_transform +from postgrid._utils import asyncify from postgrid._models import BaseModel, FinalRequestOptions -from postgrid._constants import RAW_RESPONSE_HEADER from postgrid._exceptions import APIStatusError, APITimeoutError, APIResponseValidationError from postgrid._base_client import ( DEFAULT_TIMEOUT, HTTPX_DEFAULT_TIMEOUT, BaseClient, + OtherPlatform, + DefaultHttpxClient, + DefaultAsyncHttpxClient, + get_platform, make_request_options, ) -from postgrid.types.address_verification_verify_params import StandardFreeformAddressInput from .utils import update_env @@ -61,60 +60,53 @@ def _get_open_connections(client: PostGrid | AsyncPostGrid) -> int: class TestPostGrid: - client = PostGrid( - base_url=base_url, - address_verification_api_key=address_verification_api_key, - print_mail_api_key=print_mail_api_key, - _strict_response_validation=True, - ) - @pytest.mark.respx(base_url=base_url) - def test_raw_response(self, respx_mock: MockRouter) -> None: + def test_raw_response(self, respx_mock: MockRouter, client: PostGrid) -> None: respx_mock.post("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) - response = self.client.post("/foo", cast_to=httpx.Response) + response = client.post("/foo", cast_to=httpx.Response) assert response.status_code == 200 assert isinstance(response, httpx.Response) assert response.json() == {"foo": "bar"} @pytest.mark.respx(base_url=base_url) - def test_raw_response_for_binary(self, respx_mock: MockRouter) -> None: + def test_raw_response_for_binary(self, respx_mock: MockRouter, client: PostGrid) -> None: respx_mock.post("/foo").mock( return_value=httpx.Response(200, headers={"Content-Type": "application/binary"}, content='{"foo": "bar"}') ) - response = self.client.post("/foo", cast_to=httpx.Response) + response = client.post("/foo", cast_to=httpx.Response) assert response.status_code == 200 assert isinstance(response, httpx.Response) assert response.json() == {"foo": "bar"} - def test_copy(self) -> None: - copied = self.client.copy() - assert id(copied) != id(self.client) + def test_copy(self, client: PostGrid) -> None: + copied = client.copy() + assert id(copied) != id(client) - copied = self.client.copy(address_verification_api_key="another My Address Verification API Key") + copied = client.copy(address_verification_api_key="another My Address Verification API Key") assert copied.address_verification_api_key == "another My Address Verification API Key" - assert self.client.address_verification_api_key == "My Address Verification API Key" + assert client.address_verification_api_key == "My Address Verification API Key" - copied = self.client.copy(print_mail_api_key="another My Print Mail API Key") + copied = client.copy(print_mail_api_key="another My Print Mail API Key") assert copied.print_mail_api_key == "another My Print Mail API Key" - assert self.client.print_mail_api_key == "My Print Mail API Key" + assert client.print_mail_api_key == "My Print Mail API Key" - def test_copy_default_options(self) -> None: + def test_copy_default_options(self, client: PostGrid) -> None: # options that have a default are overridden correctly - copied = self.client.copy(max_retries=7) + copied = client.copy(max_retries=7) assert copied.max_retries == 7 - assert self.client.max_retries == 2 + assert client.max_retries == 2 copied2 = copied.copy(max_retries=6) assert copied2.max_retries == 6 assert copied.max_retries == 7 # timeout - assert isinstance(self.client.timeout, httpx.Timeout) - copied = self.client.copy(timeout=None) + assert isinstance(client.timeout, httpx.Timeout) + copied = client.copy(timeout=None) assert copied.timeout is None - assert isinstance(self.client.timeout, httpx.Timeout) + assert isinstance(client.timeout, httpx.Timeout) def test_copy_default_headers(self) -> None: client = PostGrid( @@ -153,6 +145,7 @@ def test_copy_default_headers(self) -> None: match="`default_headers` and `set_default_headers` arguments are mutually exclusive", ): client.copy(set_default_headers={}, default_headers={"X-Foo": "Bar"}) + client.close() def test_copy_default_query(self) -> None: client = PostGrid( @@ -194,13 +187,15 @@ def test_copy_default_query(self) -> None: ): client.copy(set_default_query={}, default_query={"foo": "Bar"}) - def test_copy_signature(self) -> None: + client.close() + + def test_copy_signature(self, client: PostGrid) -> None: # ensure the same parameters that can be passed to the client are defined in the `.copy()` method init_signature = inspect.signature( # mypy doesn't like that we access the `__init__` property. - self.client.__init__, # type: ignore[misc] + client.__init__, # type: ignore[misc] ) - copy_signature = inspect.signature(self.client.copy) + copy_signature = inspect.signature(client.copy) exclude_params = {"transport", "proxies", "_strict_response_validation"} for name in init_signature.parameters.keys(): @@ -210,12 +205,13 @@ def test_copy_signature(self) -> None: copy_param = copy_signature.parameters.get(name) assert copy_param is not None, f"copy() signature is missing the {name} param" - def test_copy_build_request(self) -> None: + @pytest.mark.skipif(sys.version_info >= (3, 10), reason="fails because of a memory leak that started from 3.12") + def test_copy_build_request(self, client: PostGrid) -> None: options = FinalRequestOptions(method="get", url="/foo") def build_request(options: FinalRequestOptions) -> None: - client = self.client.copy() - client._build_request(options) + client_copy = client.copy() + client_copy._build_request(options) # ensure that the machinery is warmed up before tracing starts. build_request(options) @@ -272,14 +268,12 @@ def add_leak(leaks: list[tracemalloc.StatisticDiff], diff: tracemalloc.Statistic print(frame) raise AssertionError() - def test_request_timeout(self) -> None: - request = self.client._build_request(FinalRequestOptions(method="get", url="/foo")) + def test_request_timeout(self, client: PostGrid) -> None: + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == DEFAULT_TIMEOUT - request = self.client._build_request( - FinalRequestOptions(method="get", url="/foo", timeout=httpx.Timeout(100.0)) - ) + request = client._build_request(FinalRequestOptions(method="get", url="/foo", timeout=httpx.Timeout(100.0))) timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == httpx.Timeout(100.0) @@ -296,6 +290,8 @@ def test_client_timeout_option(self) -> None: timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == httpx.Timeout(0) + client.close() + def test_http_client_timeout_option(self) -> None: # custom timeout given to the httpx client should be used with httpx.Client(timeout=None) as http_client: @@ -311,6 +307,8 @@ def test_http_client_timeout_option(self) -> None: timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == httpx.Timeout(None) + client.close() + # no timeout given to the httpx client should not use the httpx default with httpx.Client() as http_client: client = PostGrid( @@ -325,6 +323,8 @@ def test_http_client_timeout_option(self) -> None: timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == DEFAULT_TIMEOUT + client.close() + # explicitly passing the default timeout currently results in it being ignored with httpx.Client(timeout=HTTPX_DEFAULT_TIMEOUT) as http_client: client = PostGrid( @@ -339,6 +339,8 @@ def test_http_client_timeout_option(self) -> None: timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == DEFAULT_TIMEOUT # our default + client.close() + async def test_invalid_http_client(self) -> None: with pytest.raises(TypeError, match="Invalid `http_client` arg"): async with httpx.AsyncClient() as http_client: @@ -351,18 +353,18 @@ async def test_invalid_http_client(self) -> None: ) def test_default_headers_option(self) -> None: - client = PostGrid( + test_client = PostGrid( base_url=base_url, address_verification_api_key=address_verification_api_key, print_mail_api_key=print_mail_api_key, _strict_response_validation=True, default_headers={"X-Foo": "bar"}, ) - request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + request = test_client._build_request(FinalRequestOptions(method="get", url="/foo")) assert request.headers.get("x-foo") == "bar" assert request.headers.get("x-stainless-lang") == "python" - client2 = PostGrid( + test_client2 = PostGrid( base_url=base_url, address_verification_api_key=address_verification_api_key, print_mail_api_key=print_mail_api_key, @@ -372,10 +374,13 @@ def test_default_headers_option(self) -> None: "X-Stainless-Lang": "my-overriding-header", }, ) - request = client2._build_request(FinalRequestOptions(method="get", url="/foo")) + request = test_client2._build_request(FinalRequestOptions(method="get", url="/foo")) assert request.headers.get("x-foo") == "stainless" assert request.headers.get("x-stainless-lang") == "my-overriding-header" + test_client.close() + test_client2.close() + def test_validate_headers(self) -> None: client = PostGrid( base_url=base_url, @@ -434,8 +439,10 @@ def test_default_query_option(self) -> None: url = httpx.URL(request.url) assert dict(url.params) == {"foo": "baz", "query_param": "overridden"} - def test_request_extra_json(self) -> None: - request = self.client._build_request( + client.close() + + def test_request_extra_json(self, client: PostGrid) -> None: + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -446,7 +453,7 @@ def test_request_extra_json(self) -> None: data = json.loads(request.content.decode("utf-8")) assert data == {"foo": "bar", "baz": False} - request = self.client._build_request( + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -457,7 +464,7 @@ def test_request_extra_json(self) -> None: assert data == {"baz": False} # `extra_json` takes priority over `json_data` when keys clash - request = self.client._build_request( + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -468,8 +475,8 @@ def test_request_extra_json(self) -> None: data = json.loads(request.content.decode("utf-8")) assert data == {"foo": "bar", "baz": None} - def test_request_extra_headers(self) -> None: - request = self.client._build_request( + def test_request_extra_headers(self, client: PostGrid) -> None: + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -479,7 +486,7 @@ def test_request_extra_headers(self) -> None: assert request.headers.get("X-Foo") == "Foo" # `extra_headers` takes priority over `default_headers` when keys clash - request = self.client.with_options(default_headers={"X-Bar": "true"})._build_request( + request = client.with_options(default_headers={"X-Bar": "true"})._build_request( FinalRequestOptions( method="post", url="/foo", @@ -490,8 +497,8 @@ def test_request_extra_headers(self) -> None: ) assert request.headers.get("X-Bar") == "false" - def test_request_extra_query(self) -> None: - request = self.client._build_request( + def test_request_extra_query(self, client: PostGrid) -> None: + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -504,7 +511,7 @@ def test_request_extra_query(self) -> None: assert params == {"my_query_param": "Foo"} # if both `query` and `extra_query` are given, they are merged - request = self.client._build_request( + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -518,7 +525,7 @@ def test_request_extra_query(self) -> None: assert params == {"bar": "1", "foo": "2"} # `extra_query` takes priority over `query` when keys clash - request = self.client._build_request( + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -534,7 +541,7 @@ def test_request_extra_query(self) -> None: def test_multipart_repeating_array(self, client: PostGrid) -> None: request = client._build_request( FinalRequestOptions.construct( - method="get", + method="post", url="/foo", headers={"Content-Type": "multipart/form-data; boundary=6b7ba517decee4a450543ea6ae821c82"}, json_data={"array": ["foo", "bar"]}, @@ -561,7 +568,7 @@ def test_multipart_repeating_array(self, client: PostGrid) -> None: ] @pytest.mark.respx(base_url=base_url) - def test_basic_union_response(self, respx_mock: MockRouter) -> None: + def test_basic_union_response(self, respx_mock: MockRouter, client: PostGrid) -> None: class Model1(BaseModel): name: str @@ -570,12 +577,12 @@ class Model2(BaseModel): respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) - response = self.client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) + response = client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) assert isinstance(response, Model2) assert response.foo == "bar" @pytest.mark.respx(base_url=base_url) - def test_union_response_different_types(self, respx_mock: MockRouter) -> None: + def test_union_response_different_types(self, respx_mock: MockRouter, client: PostGrid) -> None: """Union of objects with the same field name using a different type""" class Model1(BaseModel): @@ -586,18 +593,18 @@ class Model2(BaseModel): respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) - response = self.client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) + response = client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) assert isinstance(response, Model2) assert response.foo == "bar" respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": 1})) - response = self.client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) + response = client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) assert isinstance(response, Model1) assert response.foo == 1 @pytest.mark.respx(base_url=base_url) - def test_non_application_json_content_type_for_json_data(self, respx_mock: MockRouter) -> None: + def test_non_application_json_content_type_for_json_data(self, respx_mock: MockRouter, client: PostGrid) -> None: """ Response that sets Content-Type to something other than application/json but returns json data """ @@ -613,7 +620,7 @@ class Model(BaseModel): ) ) - response = self.client.get("/foo", cast_to=Model) + response = client.get("/foo", cast_to=Model) assert isinstance(response, Model) assert response.foo == 2 @@ -630,6 +637,8 @@ def test_base_url_setter(self) -> None: assert client.base_url == "https://example.com/from_setter/" + client.close() + def test_base_url_env(self) -> None: with update_env(POSTGRID_BASE_URL="http://localhost:5000/from/env"): client = PostGrid( @@ -667,6 +676,7 @@ def test_base_url_trailing_slash(self, client: PostGrid) -> None: ), ) assert request.url == "http://localhost:5000/custom/path/foo" + client.close() @pytest.mark.parametrize( "client", @@ -696,6 +706,7 @@ def test_base_url_no_trailing_slash(self, client: PostGrid) -> None: ), ) assert request.url == "http://localhost:5000/custom/path/foo" + client.close() @pytest.mark.parametrize( "client", @@ -725,45 +736,46 @@ def test_absolute_request_url(self, client: PostGrid) -> None: ), ) assert request.url == "https://myapi.com/foo" + client.close() def test_copied_client_does_not_close_http(self) -> None: - client = PostGrid( + test_client = PostGrid( base_url=base_url, address_verification_api_key=address_verification_api_key, print_mail_api_key=print_mail_api_key, _strict_response_validation=True, ) - assert not client.is_closed() + assert not test_client.is_closed() - copied = client.copy() - assert copied is not client + copied = test_client.copy() + assert copied is not test_client del copied - assert not client.is_closed() + assert not test_client.is_closed() def test_client_context_manager(self) -> None: - client = PostGrid( + test_client = PostGrid( base_url=base_url, address_verification_api_key=address_verification_api_key, print_mail_api_key=print_mail_api_key, _strict_response_validation=True, ) - with client as c2: - assert c2 is client + with test_client as c2: + assert c2 is test_client assert not c2.is_closed() - assert not client.is_closed() - assert client.is_closed() + assert not test_client.is_closed() + assert test_client.is_closed() @pytest.mark.respx(base_url=base_url) - def test_client_response_validation_error(self, respx_mock: MockRouter) -> None: + def test_client_response_validation_error(self, respx_mock: MockRouter, client: PostGrid) -> None: class Model(BaseModel): foo: str respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": {"invalid": True}})) with pytest.raises(APIResponseValidationError) as exc: - self.client.get("/foo", cast_to=Model) + client.get("/foo", cast_to=Model) assert isinstance(exc.value.__cause__, ValidationError) @@ -794,16 +806,19 @@ class Model(BaseModel): with pytest.raises(APIResponseValidationError): strict_client.get("/foo", cast_to=Model) - client = PostGrid( + non_strict_client = PostGrid( base_url=base_url, address_verification_api_key=address_verification_api_key, print_mail_api_key=print_mail_api_key, _strict_response_validation=False, ) - response = client.get("/foo", cast_to=Model) + response = non_strict_client.get("/foo", cast_to=Model) assert isinstance(response, str) # type: ignore[unreachable] + strict_client.close() + non_strict_client.close() + @pytest.mark.parametrize( "remaining_retries,retry_after,timeout", [ @@ -826,14 +841,9 @@ class Model(BaseModel): ], ) @mock.patch("time.time", mock.MagicMock(return_value=1696004797)) - def test_parse_retry_after_header(self, remaining_retries: int, retry_after: str, timeout: float) -> None: - client = PostGrid( - base_url=base_url, - address_verification_api_key=address_verification_api_key, - print_mail_api_key=print_mail_api_key, - _strict_response_validation=True, - ) - + def test_parse_retry_after_header( + self, remaining_retries: int, retry_after: str, timeout: float, client: PostGrid + ) -> None: headers = httpx.Headers({"retry-after": retry_after}) options = FinalRequestOptions(method="get", url="/foo", max_retries=3) calculated = client._calculate_retry_timeout(remaining_retries, options, headers) @@ -841,33 +851,22 @@ def test_parse_retry_after_header(self, remaining_retries: int, retry_after: str @mock.patch("postgrid._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) - def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter) -> None: + def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter, client: PostGrid) -> None: respx_mock.post("/v1/addver/verifications").mock(side_effect=httpx.TimeoutException("Test timeout error")) with pytest.raises(APITimeoutError): - self.client.post( - "/v1/addver/verifications", - body=cast(object, maybe_transform(dict(address="address"), StandardFreeformAddressInput)), - cast_to=httpx.Response, - options={"headers": {RAW_RESPONSE_HEADER: "stream"}}, - ) + client.address_verification.with_streaming_response.verify(address="address").__enter__() - assert _get_open_connections(self.client) == 0 + assert _get_open_connections(client) == 0 @mock.patch("postgrid._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) - def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter) -> None: + def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter, client: PostGrid) -> None: respx_mock.post("/v1/addver/verifications").mock(return_value=httpx.Response(500)) with pytest.raises(APIStatusError): - self.client.post( - "/v1/addver/verifications", - body=cast(object, maybe_transform(dict(address="address"), StandardFreeformAddressInput)), - cast_to=httpx.Response, - options={"headers": {RAW_RESPONSE_HEADER: "stream"}}, - ) - - assert _get_open_connections(self.client) == 0 + client.address_verification.with_streaming_response.verify(address="address").__enter__() + assert _get_open_connections(client) == 0 @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @mock.patch("postgrid._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @@ -950,66 +949,104 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: assert response.http_request.headers.get("x-stainless-retry-count") == "42" + def test_proxy_environment_variables(self, monkeypatch: pytest.MonkeyPatch) -> None: + # Test that the proxy environment variables are set correctly + monkeypatch.setenv("HTTPS_PROXY", "https://example.org") -class TestAsyncPostGrid: - client = AsyncPostGrid( - base_url=base_url, - address_verification_api_key=address_verification_api_key, - print_mail_api_key=print_mail_api_key, - _strict_response_validation=True, - ) + client = DefaultHttpxClient() + + mounts = tuple(client._mounts.items()) + assert len(mounts) == 1 + assert mounts[0][0].pattern == "https://" + + @pytest.mark.filterwarnings("ignore:.*deprecated.*:DeprecationWarning") + def test_default_client_creation(self) -> None: + # Ensure that the client can be initialized without any exceptions + DefaultHttpxClient( + verify=True, + cert=None, + trust_env=True, + http1=True, + http2=False, + limits=httpx.Limits(max_connections=100, max_keepalive_connections=20), + ) + + @pytest.mark.respx(base_url=base_url) + def test_follow_redirects(self, respx_mock: MockRouter, client: PostGrid) -> None: + # Test that the default follow_redirects=True allows following redirects + respx_mock.post("/redirect").mock( + return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"}) + ) + respx_mock.get("/redirected").mock(return_value=httpx.Response(200, json={"status": "ok"})) + + response = client.post("/redirect", body={"key": "value"}, cast_to=httpx.Response) + assert response.status_code == 200 + assert response.json() == {"status": "ok"} @pytest.mark.respx(base_url=base_url) - @pytest.mark.asyncio - async def test_raw_response(self, respx_mock: MockRouter) -> None: + def test_follow_redirects_disabled(self, respx_mock: MockRouter, client: PostGrid) -> None: + # Test that follow_redirects=False prevents following redirects + respx_mock.post("/redirect").mock( + return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"}) + ) + + with pytest.raises(APIStatusError) as exc_info: + client.post("/redirect", body={"key": "value"}, options={"follow_redirects": False}, cast_to=httpx.Response) + + assert exc_info.value.response.status_code == 302 + assert exc_info.value.response.headers["Location"] == f"{base_url}/redirected" + + +class TestAsyncPostGrid: + @pytest.mark.respx(base_url=base_url) + async def test_raw_response(self, respx_mock: MockRouter, async_client: AsyncPostGrid) -> None: respx_mock.post("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) - response = await self.client.post("/foo", cast_to=httpx.Response) + response = await async_client.post("/foo", cast_to=httpx.Response) assert response.status_code == 200 assert isinstance(response, httpx.Response) assert response.json() == {"foo": "bar"} @pytest.mark.respx(base_url=base_url) - @pytest.mark.asyncio - async def test_raw_response_for_binary(self, respx_mock: MockRouter) -> None: + async def test_raw_response_for_binary(self, respx_mock: MockRouter, async_client: AsyncPostGrid) -> None: respx_mock.post("/foo").mock( return_value=httpx.Response(200, headers={"Content-Type": "application/binary"}, content='{"foo": "bar"}') ) - response = await self.client.post("/foo", cast_to=httpx.Response) + response = await async_client.post("/foo", cast_to=httpx.Response) assert response.status_code == 200 assert isinstance(response, httpx.Response) assert response.json() == {"foo": "bar"} - def test_copy(self) -> None: - copied = self.client.copy() - assert id(copied) != id(self.client) + def test_copy(self, async_client: AsyncPostGrid) -> None: + copied = async_client.copy() + assert id(copied) != id(async_client) - copied = self.client.copy(address_verification_api_key="another My Address Verification API Key") + copied = async_client.copy(address_verification_api_key="another My Address Verification API Key") assert copied.address_verification_api_key == "another My Address Verification API Key" - assert self.client.address_verification_api_key == "My Address Verification API Key" + assert async_client.address_verification_api_key == "My Address Verification API Key" - copied = self.client.copy(print_mail_api_key="another My Print Mail API Key") + copied = async_client.copy(print_mail_api_key="another My Print Mail API Key") assert copied.print_mail_api_key == "another My Print Mail API Key" - assert self.client.print_mail_api_key == "My Print Mail API Key" + assert async_client.print_mail_api_key == "My Print Mail API Key" - def test_copy_default_options(self) -> None: + def test_copy_default_options(self, async_client: AsyncPostGrid) -> None: # options that have a default are overridden correctly - copied = self.client.copy(max_retries=7) + copied = async_client.copy(max_retries=7) assert copied.max_retries == 7 - assert self.client.max_retries == 2 + assert async_client.max_retries == 2 copied2 = copied.copy(max_retries=6) assert copied2.max_retries == 6 assert copied.max_retries == 7 # timeout - assert isinstance(self.client.timeout, httpx.Timeout) - copied = self.client.copy(timeout=None) + assert isinstance(async_client.timeout, httpx.Timeout) + copied = async_client.copy(timeout=None) assert copied.timeout is None - assert isinstance(self.client.timeout, httpx.Timeout) + assert isinstance(async_client.timeout, httpx.Timeout) - def test_copy_default_headers(self) -> None: + async def test_copy_default_headers(self) -> None: client = AsyncPostGrid( base_url=base_url, address_verification_api_key=address_verification_api_key, @@ -1046,8 +1083,9 @@ def test_copy_default_headers(self) -> None: match="`default_headers` and `set_default_headers` arguments are mutually exclusive", ): client.copy(set_default_headers={}, default_headers={"X-Foo": "Bar"}) + await client.close() - def test_copy_default_query(self) -> None: + async def test_copy_default_query(self) -> None: client = AsyncPostGrid( base_url=base_url, address_verification_api_key=address_verification_api_key, @@ -1087,13 +1125,15 @@ def test_copy_default_query(self) -> None: ): client.copy(set_default_query={}, default_query={"foo": "Bar"}) - def test_copy_signature(self) -> None: + await client.close() + + def test_copy_signature(self, async_client: AsyncPostGrid) -> None: # ensure the same parameters that can be passed to the client are defined in the `.copy()` method init_signature = inspect.signature( # mypy doesn't like that we access the `__init__` property. - self.client.__init__, # type: ignore[misc] + async_client.__init__, # type: ignore[misc] ) - copy_signature = inspect.signature(self.client.copy) + copy_signature = inspect.signature(async_client.copy) exclude_params = {"transport", "proxies", "_strict_response_validation"} for name in init_signature.parameters.keys(): @@ -1103,12 +1143,13 @@ def test_copy_signature(self) -> None: copy_param = copy_signature.parameters.get(name) assert copy_param is not None, f"copy() signature is missing the {name} param" - def test_copy_build_request(self) -> None: + @pytest.mark.skipif(sys.version_info >= (3, 10), reason="fails because of a memory leak that started from 3.12") + def test_copy_build_request(self, async_client: AsyncPostGrid) -> None: options = FinalRequestOptions(method="get", url="/foo") def build_request(options: FinalRequestOptions) -> None: - client = self.client.copy() - client._build_request(options) + client_copy = async_client.copy() + client_copy._build_request(options) # ensure that the machinery is warmed up before tracing starts. build_request(options) @@ -1165,12 +1206,12 @@ def add_leak(leaks: list[tracemalloc.StatisticDiff], diff: tracemalloc.Statistic print(frame) raise AssertionError() - async def test_request_timeout(self) -> None: - request = self.client._build_request(FinalRequestOptions(method="get", url="/foo")) + async def test_request_timeout(self, async_client: AsyncPostGrid) -> None: + request = async_client._build_request(FinalRequestOptions(method="get", url="/foo")) timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == DEFAULT_TIMEOUT - request = self.client._build_request( + request = async_client._build_request( FinalRequestOptions(method="get", url="/foo", timeout=httpx.Timeout(100.0)) ) timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore @@ -1189,6 +1230,8 @@ async def test_client_timeout_option(self) -> None: timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == httpx.Timeout(0) + await client.close() + async def test_http_client_timeout_option(self) -> None: # custom timeout given to the httpx client should be used async with httpx.AsyncClient(timeout=None) as http_client: @@ -1204,6 +1247,8 @@ async def test_http_client_timeout_option(self) -> None: timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == httpx.Timeout(None) + await client.close() + # no timeout given to the httpx client should not use the httpx default async with httpx.AsyncClient() as http_client: client = AsyncPostGrid( @@ -1218,6 +1263,8 @@ async def test_http_client_timeout_option(self) -> None: timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == DEFAULT_TIMEOUT + await client.close() + # explicitly passing the default timeout currently results in it being ignored async with httpx.AsyncClient(timeout=HTTPX_DEFAULT_TIMEOUT) as http_client: client = AsyncPostGrid( @@ -1232,6 +1279,8 @@ async def test_http_client_timeout_option(self) -> None: timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == DEFAULT_TIMEOUT # our default + await client.close() + def test_invalid_http_client(self) -> None: with pytest.raises(TypeError, match="Invalid `http_client` arg"): with httpx.Client() as http_client: @@ -1243,19 +1292,19 @@ def test_invalid_http_client(self) -> None: http_client=cast(Any, http_client), ) - def test_default_headers_option(self) -> None: - client = AsyncPostGrid( + async def test_default_headers_option(self) -> None: + test_client = AsyncPostGrid( base_url=base_url, address_verification_api_key=address_verification_api_key, print_mail_api_key=print_mail_api_key, _strict_response_validation=True, default_headers={"X-Foo": "bar"}, ) - request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + request = test_client._build_request(FinalRequestOptions(method="get", url="/foo")) assert request.headers.get("x-foo") == "bar" assert request.headers.get("x-stainless-lang") == "python" - client2 = AsyncPostGrid( + test_client2 = AsyncPostGrid( base_url=base_url, address_verification_api_key=address_verification_api_key, print_mail_api_key=print_mail_api_key, @@ -1265,10 +1314,13 @@ def test_default_headers_option(self) -> None: "X-Stainless-Lang": "my-overriding-header", }, ) - request = client2._build_request(FinalRequestOptions(method="get", url="/foo")) + request = test_client2._build_request(FinalRequestOptions(method="get", url="/foo")) assert request.headers.get("x-foo") == "stainless" assert request.headers.get("x-stainless-lang") == "my-overriding-header" + await test_client.close() + await test_client2.close() + def test_validate_headers(self) -> None: client = AsyncPostGrid( base_url=base_url, @@ -1305,7 +1357,7 @@ def test_validate_headers(self) -> None: request2 = client2._build_request(FinalRequestOptions(method="get", url="/foo", headers={"X-API-Key": Omit()})) assert request2.headers.get("X-API-Key") is None - def test_default_query_option(self) -> None: + async def test_default_query_option(self) -> None: client = AsyncPostGrid( base_url=base_url, address_verification_api_key=address_verification_api_key, @@ -1327,8 +1379,10 @@ def test_default_query_option(self) -> None: url = httpx.URL(request.url) assert dict(url.params) == {"foo": "baz", "query_param": "overridden"} - def test_request_extra_json(self) -> None: - request = self.client._build_request( + await client.close() + + def test_request_extra_json(self, client: PostGrid) -> None: + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -1339,7 +1393,7 @@ def test_request_extra_json(self) -> None: data = json.loads(request.content.decode("utf-8")) assert data == {"foo": "bar", "baz": False} - request = self.client._build_request( + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -1350,7 +1404,7 @@ def test_request_extra_json(self) -> None: assert data == {"baz": False} # `extra_json` takes priority over `json_data` when keys clash - request = self.client._build_request( + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -1361,8 +1415,8 @@ def test_request_extra_json(self) -> None: data = json.loads(request.content.decode("utf-8")) assert data == {"foo": "bar", "baz": None} - def test_request_extra_headers(self) -> None: - request = self.client._build_request( + def test_request_extra_headers(self, client: PostGrid) -> None: + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -1372,7 +1426,7 @@ def test_request_extra_headers(self) -> None: assert request.headers.get("X-Foo") == "Foo" # `extra_headers` takes priority over `default_headers` when keys clash - request = self.client.with_options(default_headers={"X-Bar": "true"})._build_request( + request = client.with_options(default_headers={"X-Bar": "true"})._build_request( FinalRequestOptions( method="post", url="/foo", @@ -1383,8 +1437,8 @@ def test_request_extra_headers(self) -> None: ) assert request.headers.get("X-Bar") == "false" - def test_request_extra_query(self) -> None: - request = self.client._build_request( + def test_request_extra_query(self, client: PostGrid) -> None: + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -1397,7 +1451,7 @@ def test_request_extra_query(self) -> None: assert params == {"my_query_param": "Foo"} # if both `query` and `extra_query` are given, they are merged - request = self.client._build_request( + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -1411,7 +1465,7 @@ def test_request_extra_query(self) -> None: assert params == {"bar": "1", "foo": "2"} # `extra_query` takes priority over `query` when keys clash - request = self.client._build_request( + request = client._build_request( FinalRequestOptions( method="post", url="/foo", @@ -1427,7 +1481,7 @@ def test_request_extra_query(self) -> None: def test_multipart_repeating_array(self, async_client: AsyncPostGrid) -> None: request = async_client._build_request( FinalRequestOptions.construct( - method="get", + method="post", url="/foo", headers={"Content-Type": "multipart/form-data; boundary=6b7ba517decee4a450543ea6ae821c82"}, json_data={"array": ["foo", "bar"]}, @@ -1454,7 +1508,7 @@ def test_multipart_repeating_array(self, async_client: AsyncPostGrid) -> None: ] @pytest.mark.respx(base_url=base_url) - async def test_basic_union_response(self, respx_mock: MockRouter) -> None: + async def test_basic_union_response(self, respx_mock: MockRouter, async_client: AsyncPostGrid) -> None: class Model1(BaseModel): name: str @@ -1463,12 +1517,12 @@ class Model2(BaseModel): respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) - response = await self.client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) + response = await async_client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) assert isinstance(response, Model2) assert response.foo == "bar" @pytest.mark.respx(base_url=base_url) - async def test_union_response_different_types(self, respx_mock: MockRouter) -> None: + async def test_union_response_different_types(self, respx_mock: MockRouter, async_client: AsyncPostGrid) -> None: """Union of objects with the same field name using a different type""" class Model1(BaseModel): @@ -1479,18 +1533,20 @@ class Model2(BaseModel): respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) - response = await self.client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) + response = await async_client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) assert isinstance(response, Model2) assert response.foo == "bar" respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": 1})) - response = await self.client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) + response = await async_client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) assert isinstance(response, Model1) assert response.foo == 1 @pytest.mark.respx(base_url=base_url) - async def test_non_application_json_content_type_for_json_data(self, respx_mock: MockRouter) -> None: + async def test_non_application_json_content_type_for_json_data( + self, respx_mock: MockRouter, async_client: AsyncPostGrid + ) -> None: """ Response that sets Content-Type to something other than application/json but returns json data """ @@ -1506,11 +1562,11 @@ class Model(BaseModel): ) ) - response = await self.client.get("/foo", cast_to=Model) + response = await async_client.get("/foo", cast_to=Model) assert isinstance(response, Model) assert response.foo == 2 - def test_base_url_setter(self) -> None: + async def test_base_url_setter(self) -> None: client = AsyncPostGrid( base_url="https://example.com/from_init", address_verification_api_key=address_verification_api_key, @@ -1523,7 +1579,9 @@ def test_base_url_setter(self) -> None: assert client.base_url == "https://example.com/from_setter/" - def test_base_url_env(self) -> None: + await client.close() + + async def test_base_url_env(self) -> None: with update_env(POSTGRID_BASE_URL="http://localhost:5000/from/env"): client = AsyncPostGrid( address_verification_api_key=address_verification_api_key, @@ -1551,7 +1609,7 @@ def test_base_url_env(self) -> None: ], ids=["standard", "custom http client"], ) - def test_base_url_trailing_slash(self, client: AsyncPostGrid) -> None: + async def test_base_url_trailing_slash(self, client: AsyncPostGrid) -> None: request = client._build_request( FinalRequestOptions( method="post", @@ -1560,6 +1618,7 @@ def test_base_url_trailing_slash(self, client: AsyncPostGrid) -> None: ), ) assert request.url == "http://localhost:5000/custom/path/foo" + await client.close() @pytest.mark.parametrize( "client", @@ -1580,7 +1639,7 @@ def test_base_url_trailing_slash(self, client: AsyncPostGrid) -> None: ], ids=["standard", "custom http client"], ) - def test_base_url_no_trailing_slash(self, client: AsyncPostGrid) -> None: + async def test_base_url_no_trailing_slash(self, client: AsyncPostGrid) -> None: request = client._build_request( FinalRequestOptions( method="post", @@ -1589,6 +1648,7 @@ def test_base_url_no_trailing_slash(self, client: AsyncPostGrid) -> None: ), ) assert request.url == "http://localhost:5000/custom/path/foo" + await client.close() @pytest.mark.parametrize( "client", @@ -1609,7 +1669,7 @@ def test_base_url_no_trailing_slash(self, client: AsyncPostGrid) -> None: ], ids=["standard", "custom http client"], ) - def test_absolute_request_url(self, client: AsyncPostGrid) -> None: + async def test_absolute_request_url(self, client: AsyncPostGrid) -> None: request = client._build_request( FinalRequestOptions( method="post", @@ -1618,47 +1678,47 @@ def test_absolute_request_url(self, client: AsyncPostGrid) -> None: ), ) assert request.url == "https://myapi.com/foo" + await client.close() async def test_copied_client_does_not_close_http(self) -> None: - client = AsyncPostGrid( + test_client = AsyncPostGrid( base_url=base_url, address_verification_api_key=address_verification_api_key, print_mail_api_key=print_mail_api_key, _strict_response_validation=True, ) - assert not client.is_closed() + assert not test_client.is_closed() - copied = client.copy() - assert copied is not client + copied = test_client.copy() + assert copied is not test_client del copied await asyncio.sleep(0.2) - assert not client.is_closed() + assert not test_client.is_closed() async def test_client_context_manager(self) -> None: - client = AsyncPostGrid( + test_client = AsyncPostGrid( base_url=base_url, address_verification_api_key=address_verification_api_key, print_mail_api_key=print_mail_api_key, _strict_response_validation=True, ) - async with client as c2: - assert c2 is client + async with test_client as c2: + assert c2 is test_client assert not c2.is_closed() - assert not client.is_closed() - assert client.is_closed() + assert not test_client.is_closed() + assert test_client.is_closed() @pytest.mark.respx(base_url=base_url) - @pytest.mark.asyncio - async def test_client_response_validation_error(self, respx_mock: MockRouter) -> None: + async def test_client_response_validation_error(self, respx_mock: MockRouter, async_client: AsyncPostGrid) -> None: class Model(BaseModel): foo: str respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": {"invalid": True}})) with pytest.raises(APIResponseValidationError) as exc: - await self.client.get("/foo", cast_to=Model) + await async_client.get("/foo", cast_to=Model) assert isinstance(exc.value.__cause__, ValidationError) @@ -1673,7 +1733,6 @@ async def test_client_max_retries_validation(self) -> None: ) @pytest.mark.respx(base_url=base_url) - @pytest.mark.asyncio async def test_received_text_for_expected_json(self, respx_mock: MockRouter) -> None: class Model(BaseModel): name: str @@ -1690,16 +1749,19 @@ class Model(BaseModel): with pytest.raises(APIResponseValidationError): await strict_client.get("/foo", cast_to=Model) - client = AsyncPostGrid( + non_strict_client = AsyncPostGrid( base_url=base_url, address_verification_api_key=address_verification_api_key, print_mail_api_key=print_mail_api_key, _strict_response_validation=False, ) - response = await client.get("/foo", cast_to=Model) + response = await non_strict_client.get("/foo", cast_to=Model) assert isinstance(response, str) # type: ignore[unreachable] + await strict_client.close() + await non_strict_client.close() + @pytest.mark.parametrize( "remaining_retries,retry_after,timeout", [ @@ -1722,54 +1784,40 @@ class Model(BaseModel): ], ) @mock.patch("time.time", mock.MagicMock(return_value=1696004797)) - @pytest.mark.asyncio - async def test_parse_retry_after_header(self, remaining_retries: int, retry_after: str, timeout: float) -> None: - client = AsyncPostGrid( - base_url=base_url, - address_verification_api_key=address_verification_api_key, - print_mail_api_key=print_mail_api_key, - _strict_response_validation=True, - ) - + async def test_parse_retry_after_header( + self, remaining_retries: int, retry_after: str, timeout: float, async_client: AsyncPostGrid + ) -> None: headers = httpx.Headers({"retry-after": retry_after}) options = FinalRequestOptions(method="get", url="/foo", max_retries=3) - calculated = client._calculate_retry_timeout(remaining_retries, options, headers) + calculated = async_client._calculate_retry_timeout(remaining_retries, options, headers) assert calculated == pytest.approx(timeout, 0.5 * 0.875) # pyright: ignore[reportUnknownMemberType] @mock.patch("postgrid._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) - async def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter) -> None: + async def test_retrying_timeout_errors_doesnt_leak( + self, respx_mock: MockRouter, async_client: AsyncPostGrid + ) -> None: respx_mock.post("/v1/addver/verifications").mock(side_effect=httpx.TimeoutException("Test timeout error")) with pytest.raises(APITimeoutError): - await self.client.post( - "/v1/addver/verifications", - body=cast(object, maybe_transform(dict(address="address"), StandardFreeformAddressInput)), - cast_to=httpx.Response, - options={"headers": {RAW_RESPONSE_HEADER: "stream"}}, - ) + await async_client.address_verification.with_streaming_response.verify(address="address").__aenter__() - assert _get_open_connections(self.client) == 0 + assert _get_open_connections(async_client) == 0 @mock.patch("postgrid._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) - async def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter) -> None: + async def test_retrying_status_errors_doesnt_leak( + self, respx_mock: MockRouter, async_client: AsyncPostGrid + ) -> None: respx_mock.post("/v1/addver/verifications").mock(return_value=httpx.Response(500)) with pytest.raises(APIStatusError): - await self.client.post( - "/v1/addver/verifications", - body=cast(object, maybe_transform(dict(address="address"), StandardFreeformAddressInput)), - cast_to=httpx.Response, - options={"headers": {RAW_RESPONSE_HEADER: "stream"}}, - ) - - assert _get_open_connections(self.client) == 0 + await async_client.address_verification.with_streaming_response.verify(address="address").__aenter__() + assert _get_open_connections(async_client) == 0 @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @mock.patch("postgrid._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) - @pytest.mark.asyncio @pytest.mark.parametrize("failure_mode", ["status", "exception"]) async def test_retries_taken( self, @@ -1801,7 +1849,6 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @mock.patch("postgrid._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) - @pytest.mark.asyncio async def test_omit_retry_count_header( self, async_client: AsyncPostGrid, failures_before_success: int, respx_mock: MockRouter ) -> None: @@ -1827,7 +1874,6 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @mock.patch("postgrid._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) - @pytest.mark.asyncio async def test_overwrite_retry_count_header( self, async_client: AsyncPostGrid, failures_before_success: int, respx_mock: MockRouter ) -> None: @@ -1850,47 +1896,55 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: assert response.http_request.headers.get("x-stainless-retry-count") == "42" - def test_get_platform(self) -> None: - # A previous implementation of asyncify could leave threads unterminated when - # used with nest_asyncio. - # - # Since nest_asyncio.apply() is global and cannot be un-applied, this - # test is run in a separate process to avoid affecting other tests. - test_code = dedent(""" - import asyncio - import nest_asyncio - import threading - - from postgrid._utils import asyncify - from postgrid._base_client import get_platform - - async def test_main() -> None: - result = await asyncify(get_platform)() - print(result) - for thread in threading.enumerate(): - print(thread.name) - - nest_asyncio.apply() - asyncio.run(test_main()) - """) - with subprocess.Popen( - [sys.executable, "-c", test_code], - text=True, - ) as process: - timeout = 10 # seconds - - start_time = time.monotonic() - while True: - return_code = process.poll() - if return_code is not None: - if return_code != 0: - raise AssertionError("calling get_platform using asyncify resulted in a non-zero exit code") - - # success - break - - if time.monotonic() - start_time > timeout: - process.kill() - raise AssertionError("calling get_platform using asyncify resulted in a hung process") - - time.sleep(0.1) + async def test_get_platform(self) -> None: + platform = await asyncify(get_platform)() + assert isinstance(platform, (str, OtherPlatform)) + + async def test_proxy_environment_variables(self, monkeypatch: pytest.MonkeyPatch) -> None: + # Test that the proxy environment variables are set correctly + monkeypatch.setenv("HTTPS_PROXY", "https://example.org") + + client = DefaultAsyncHttpxClient() + + mounts = tuple(client._mounts.items()) + assert len(mounts) == 1 + assert mounts[0][0].pattern == "https://" + + @pytest.mark.filterwarnings("ignore:.*deprecated.*:DeprecationWarning") + async def test_default_client_creation(self) -> None: + # Ensure that the client can be initialized without any exceptions + DefaultAsyncHttpxClient( + verify=True, + cert=None, + trust_env=True, + http1=True, + http2=False, + limits=httpx.Limits(max_connections=100, max_keepalive_connections=20), + ) + + @pytest.mark.respx(base_url=base_url) + async def test_follow_redirects(self, respx_mock: MockRouter, async_client: AsyncPostGrid) -> None: + # Test that the default follow_redirects=True allows following redirects + respx_mock.post("/redirect").mock( + return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"}) + ) + respx_mock.get("/redirected").mock(return_value=httpx.Response(200, json={"status": "ok"})) + + response = await async_client.post("/redirect", body={"key": "value"}, cast_to=httpx.Response) + assert response.status_code == 200 + assert response.json() == {"status": "ok"} + + @pytest.mark.respx(base_url=base_url) + async def test_follow_redirects_disabled(self, respx_mock: MockRouter, async_client: AsyncPostGrid) -> None: + # Test that follow_redirects=False prevents following redirects + respx_mock.post("/redirect").mock( + return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"}) + ) + + with pytest.raises(APIStatusError) as exc_info: + await async_client.post( + "/redirect", body={"key": "value"}, options={"follow_redirects": False}, cast_to=httpx.Response + ) + + assert exc_info.value.response.status_code == 302 + assert exc_info.value.response.headers["Location"] == f"{base_url}/redirected" diff --git a/tests/test_models.py b/tests/test_models.py index e570758..a8c25dd 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -1,5 +1,5 @@ import json -from typing import Any, Dict, List, Union, Optional, cast +from typing import TYPE_CHECKING, Any, Dict, List, Union, Optional, cast from datetime import datetime, timezone from typing_extensions import Literal, Annotated, TypeAliasType @@ -8,7 +8,7 @@ from pydantic import Field from postgrid._utils import PropertyInfo -from postgrid._compat import PYDANTIC_V2, parse_obj, model_dump, model_json +from postgrid._compat import PYDANTIC_V1, parse_obj, model_dump, model_json from postgrid._models import BaseModel, construct_type @@ -294,12 +294,12 @@ class Model(BaseModel): assert cast(bool, m.foo) is True m = Model.construct(foo={"name": 3}) - if PYDANTIC_V2: - assert isinstance(m.foo, Submodel1) - assert m.foo.name == 3 # type: ignore - else: + if PYDANTIC_V1: assert isinstance(m.foo, Submodel2) assert m.foo.name == "3" + else: + assert isinstance(m.foo, Submodel1) + assert m.foo.name == 3 # type: ignore def test_list_of_unions() -> None: @@ -426,10 +426,10 @@ class Model(BaseModel): expected = datetime(2019, 12, 27, 18, 11, 19, 117000, tzinfo=timezone.utc) - if PYDANTIC_V2: - expected_json = '{"created_at":"2019-12-27T18:11:19.117000Z"}' - else: + if PYDANTIC_V1: expected_json = '{"created_at": "2019-12-27T18:11:19.117000+00:00"}' + else: + expected_json = '{"created_at":"2019-12-27T18:11:19.117000Z"}' model = Model.construct(created_at="2019-12-27T18:11:19.117Z") assert model.created_at == expected @@ -492,12 +492,15 @@ class Model(BaseModel): resource_id: Optional[str] = None m = Model.construct() + assert m.resource_id is None assert "resource_id" not in m.model_fields_set m = Model.construct(resource_id=None) + assert m.resource_id is None assert "resource_id" in m.model_fields_set m = Model.construct(resource_id="foo") + assert m.resource_id == "foo" assert "resource_id" in m.model_fields_set @@ -528,7 +531,7 @@ class Model2(BaseModel): assert m4.to_dict(mode="python") == {"created_at": datetime.fromisoformat(time_str)} assert m4.to_dict(mode="json") == {"created_at": time_str} - if not PYDANTIC_V2: + if PYDANTIC_V1: with pytest.raises(ValueError, match="warnings is only supported in Pydantic v2"): m.to_dict(warnings=False) @@ -553,7 +556,7 @@ class Model(BaseModel): assert m3.model_dump() == {"foo": None} assert m3.model_dump(exclude_none=True) == {} - if not PYDANTIC_V2: + if PYDANTIC_V1: with pytest.raises(ValueError, match="round_trip is only supported in Pydantic v2"): m.model_dump(round_trip=True) @@ -577,10 +580,10 @@ class Model(BaseModel): assert json.loads(m.to_json()) == {"FOO": "hello"} assert json.loads(m.to_json(use_api_names=False)) == {"foo": "hello"} - if PYDANTIC_V2: - assert m.to_json(indent=None) == '{"FOO":"hello"}' - else: + if PYDANTIC_V1: assert m.to_json(indent=None) == '{"FOO": "hello"}' + else: + assert m.to_json(indent=None) == '{"FOO":"hello"}' m2 = Model() assert json.loads(m2.to_json()) == {} @@ -592,7 +595,7 @@ class Model(BaseModel): assert json.loads(m3.to_json()) == {"FOO": None} assert json.loads(m3.to_json(exclude_none=True)) == {} - if not PYDANTIC_V2: + if PYDANTIC_V1: with pytest.raises(ValueError, match="warnings is only supported in Pydantic v2"): m.to_json(warnings=False) @@ -619,7 +622,7 @@ class Model(BaseModel): assert json.loads(m3.model_dump_json()) == {"foo": None} assert json.loads(m3.model_dump_json(exclude_none=True)) == {} - if not PYDANTIC_V2: + if PYDANTIC_V1: with pytest.raises(ValueError, match="round_trip is only supported in Pydantic v2"): m.model_dump_json(round_trip=True) @@ -676,12 +679,12 @@ class B(BaseModel): ) assert isinstance(m, A) assert m.type == "a" - if PYDANTIC_V2: - assert m.data == 100 # type: ignore[comparison-overlap] - else: + if PYDANTIC_V1: # pydantic v1 automatically converts inputs to strings # if the expected type is a str assert m.data == "100" + else: + assert m.data == 100 # type: ignore[comparison-overlap] def test_discriminated_unions_unknown_variant() -> None: @@ -765,12 +768,12 @@ class B(BaseModel): ) assert isinstance(m, A) assert m.foo_type == "a" - if PYDANTIC_V2: - assert m.data == 100 # type: ignore[comparison-overlap] - else: + if PYDANTIC_V1: # pydantic v1 automatically converts inputs to strings # if the expected type is a str assert m.data == "100" + else: + assert m.data == 100 # type: ignore[comparison-overlap] def test_discriminated_unions_overlapping_discriminators_invalid_data() -> None: @@ -830,9 +833,9 @@ class B(BaseModel): assert UnionType.__discriminator__ is discriminator -@pytest.mark.skipif(not PYDANTIC_V2, reason="TypeAliasType is not supported in Pydantic v1") +@pytest.mark.skipif(PYDANTIC_V1, reason="TypeAliasType is not supported in Pydantic v1") def test_type_alias_type() -> None: - Alias = TypeAliasType("Alias", str) + Alias = TypeAliasType("Alias", str) # pyright: ignore class Model(BaseModel): alias: Alias @@ -846,7 +849,7 @@ class Model(BaseModel): assert m.union == "bar" -@pytest.mark.skipif(not PYDANTIC_V2, reason="TypeAliasType is not supported in Pydantic v1") +@pytest.mark.skipif(PYDANTIC_V1, reason="TypeAliasType is not supported in Pydantic v1") def test_field_named_cls() -> None: class Model(BaseModel): cls: str @@ -854,3 +857,107 @@ class Model(BaseModel): m = construct_type(value={"cls": "foo"}, type_=Model) assert isinstance(m, Model) assert isinstance(m.cls, str) + + +def test_discriminated_union_case() -> None: + class A(BaseModel): + type: Literal["a"] + + data: bool + + class B(BaseModel): + type: Literal["b"] + + data: List[Union[A, object]] + + class ModelA(BaseModel): + type: Literal["modelA"] + + data: int + + class ModelB(BaseModel): + type: Literal["modelB"] + + required: str + + data: Union[A, B] + + # when constructing ModelA | ModelB, value data doesn't match ModelB exactly - missing `required` + m = construct_type( + value={"type": "modelB", "data": {"type": "a", "data": True}}, + type_=cast(Any, Annotated[Union[ModelA, ModelB], PropertyInfo(discriminator="type")]), + ) + + assert isinstance(m, ModelB) + + +def test_nested_discriminated_union() -> None: + class InnerType1(BaseModel): + type: Literal["type_1"] + + class InnerModel(BaseModel): + inner_value: str + + class InnerType2(BaseModel): + type: Literal["type_2"] + some_inner_model: InnerModel + + class Type1(BaseModel): + base_type: Literal["base_type_1"] + value: Annotated[ + Union[ + InnerType1, + InnerType2, + ], + PropertyInfo(discriminator="type"), + ] + + class Type2(BaseModel): + base_type: Literal["base_type_2"] + + T = Annotated[ + Union[ + Type1, + Type2, + ], + PropertyInfo(discriminator="base_type"), + ] + + model = construct_type( + type_=T, + value={ + "base_type": "base_type_1", + "value": { + "type": "type_2", + }, + }, + ) + assert isinstance(model, Type1) + assert isinstance(model.value, InnerType2) + + +@pytest.mark.skipif(PYDANTIC_V1, reason="this is only supported in pydantic v2 for now") +def test_extra_properties() -> None: + class Item(BaseModel): + prop: int + + class Model(BaseModel): + __pydantic_extra__: Dict[str, Item] = Field(init=False) # pyright: ignore[reportIncompatibleVariableOverride] + + other: str + + if TYPE_CHECKING: + + def __getattr__(self, attr: str) -> Item: ... + + model = construct_type( + type_=Model, + value={ + "a": {"prop": 1}, + "other": "foo", + }, + ) + assert isinstance(model, Model) + assert model.a.prop == 1 + assert isinstance(model.a, Item) + assert model.other == "foo" diff --git a/tests/test_transform.py b/tests/test_transform.py index 7dde06d..c16f4ed 100644 --- a/tests/test_transform.py +++ b/tests/test_transform.py @@ -8,14 +8,14 @@ import pytest -from postgrid._types import Base64FileInput +from postgrid._types import Base64FileInput, omit, not_given from postgrid._utils import ( PropertyInfo, transform as _transform, parse_datetime, async_transform as _async_transform, ) -from postgrid._compat import PYDANTIC_V2 +from postgrid._compat import PYDANTIC_V1 from postgrid._models import BaseModel _T = TypeVar("_T") @@ -189,7 +189,7 @@ class DateModel(BaseModel): @pytest.mark.asyncio async def test_iso8601_format(use_async: bool) -> None: dt = datetime.fromisoformat("2023-02-23T14:16:36.337692+00:00") - tz = "Z" if PYDANTIC_V2 else "+00:00" + tz = "+00:00" if PYDANTIC_V1 else "Z" assert await transform({"foo": dt}, DatetimeDict, use_async) == {"foo": "2023-02-23T14:16:36.337692+00:00"} # type: ignore[comparison-overlap] assert await transform(DatetimeModel(foo=dt), Any, use_async) == {"foo": "2023-02-23T14:16:36.337692" + tz} # type: ignore[comparison-overlap] @@ -297,11 +297,11 @@ async def test_pydantic_unknown_field(use_async: bool) -> None: @pytest.mark.asyncio async def test_pydantic_mismatched_types(use_async: bool) -> None: model = MyModel.construct(foo=True) - if PYDANTIC_V2: + if PYDANTIC_V1: + params = await transform(model, Any, use_async) + else: with pytest.warns(UserWarning): params = await transform(model, Any, use_async) - else: - params = await transform(model, Any, use_async) assert cast(Any, params) == {"foo": True} @@ -309,11 +309,11 @@ async def test_pydantic_mismatched_types(use_async: bool) -> None: @pytest.mark.asyncio async def test_pydantic_mismatched_object_type(use_async: bool) -> None: model = MyModel.construct(foo=MyModel.construct(hello="world")) - if PYDANTIC_V2: + if PYDANTIC_V1: + params = await transform(model, Any, use_async) + else: with pytest.warns(UserWarning): params = await transform(model, Any, use_async) - else: - params = await transform(model, Any, use_async) assert cast(Any, params) == {"foo": {"hello": "world"}} @@ -432,3 +432,29 @@ async def test_base64_file_input(use_async: bool) -> None: assert await transform({"foo": io.BytesIO(b"Hello, world!")}, TypedDictBase64Input, use_async) == { "foo": "SGVsbG8sIHdvcmxkIQ==" } # type: ignore[comparison-overlap] + + +@parametrize +@pytest.mark.asyncio +async def test_transform_skipping(use_async: bool) -> None: + # lists of ints are left as-is + data = [1, 2, 3] + assert await transform(data, List[int], use_async) is data + + # iterables of ints are converted to a list + data = iter([1, 2, 3]) + assert await transform(data, Iterable[int], use_async) == [1, 2, 3] + + +@parametrize +@pytest.mark.asyncio +async def test_strips_notgiven(use_async: bool) -> None: + assert await transform({"foo_bar": "bar"}, Foo1, use_async) == {"fooBar": "bar"} + assert await transform({"foo_bar": not_given}, Foo1, use_async) == {} + + +@parametrize +@pytest.mark.asyncio +async def test_strips_omit(use_async: bool) -> None: + assert await transform({"foo_bar": "bar"}, Foo1, use_async) == {"fooBar": "bar"} + assert await transform({"foo_bar": omit}, Foo1, use_async) == {} diff --git a/tests/test_utils/test_datetime_parse.py b/tests/test_utils/test_datetime_parse.py new file mode 100644 index 0000000..19ccd09 --- /dev/null +++ b/tests/test_utils/test_datetime_parse.py @@ -0,0 +1,110 @@ +""" +Copied from https://github.com/pydantic/pydantic/blob/v1.10.22/tests/test_datetime_parse.py +with modifications so it works without pydantic v1 imports. +""" + +from typing import Type, Union +from datetime import date, datetime, timezone, timedelta + +import pytest + +from postgrid._utils import parse_date, parse_datetime + + +def create_tz(minutes: int) -> timezone: + return timezone(timedelta(minutes=minutes)) + + +@pytest.mark.parametrize( + "value,result", + [ + # Valid inputs + ("1494012444.883309", date(2017, 5, 5)), + (b"1494012444.883309", date(2017, 5, 5)), + (1_494_012_444.883_309, date(2017, 5, 5)), + ("1494012444", date(2017, 5, 5)), + (1_494_012_444, date(2017, 5, 5)), + (0, date(1970, 1, 1)), + ("2012-04-23", date(2012, 4, 23)), + (b"2012-04-23", date(2012, 4, 23)), + ("2012-4-9", date(2012, 4, 9)), + (date(2012, 4, 9), date(2012, 4, 9)), + (datetime(2012, 4, 9, 12, 15), date(2012, 4, 9)), + # Invalid inputs + ("x20120423", ValueError), + ("2012-04-56", ValueError), + (19_999_999_999, date(2603, 10, 11)), # just before watershed + (20_000_000_001, date(1970, 8, 20)), # just after watershed + (1_549_316_052, date(2019, 2, 4)), # nowish in s + (1_549_316_052_104, date(2019, 2, 4)), # nowish in ms + (1_549_316_052_104_324, date(2019, 2, 4)), # nowish in μs + (1_549_316_052_104_324_096, date(2019, 2, 4)), # nowish in ns + ("infinity", date(9999, 12, 31)), + ("inf", date(9999, 12, 31)), + (float("inf"), date(9999, 12, 31)), + ("infinity ", date(9999, 12, 31)), + (int("1" + "0" * 100), date(9999, 12, 31)), + (1e1000, date(9999, 12, 31)), + ("-infinity", date(1, 1, 1)), + ("-inf", date(1, 1, 1)), + ("nan", ValueError), + ], +) +def test_date_parsing(value: Union[str, bytes, int, float], result: Union[date, Type[Exception]]) -> None: + if type(result) == type and issubclass(result, Exception): # pyright: ignore[reportUnnecessaryIsInstance] + with pytest.raises(result): + parse_date(value) + else: + assert parse_date(value) == result + + +@pytest.mark.parametrize( + "value,result", + [ + # Valid inputs + # values in seconds + ("1494012444.883309", datetime(2017, 5, 5, 19, 27, 24, 883_309, tzinfo=timezone.utc)), + (1_494_012_444.883_309, datetime(2017, 5, 5, 19, 27, 24, 883_309, tzinfo=timezone.utc)), + ("1494012444", datetime(2017, 5, 5, 19, 27, 24, tzinfo=timezone.utc)), + (b"1494012444", datetime(2017, 5, 5, 19, 27, 24, tzinfo=timezone.utc)), + (1_494_012_444, datetime(2017, 5, 5, 19, 27, 24, tzinfo=timezone.utc)), + # values in ms + ("1494012444000.883309", datetime(2017, 5, 5, 19, 27, 24, 883, tzinfo=timezone.utc)), + ("-1494012444000.883309", datetime(1922, 8, 29, 4, 32, 35, 999117, tzinfo=timezone.utc)), + (1_494_012_444_000, datetime(2017, 5, 5, 19, 27, 24, tzinfo=timezone.utc)), + ("2012-04-23T09:15:00", datetime(2012, 4, 23, 9, 15)), + ("2012-4-9 4:8:16", datetime(2012, 4, 9, 4, 8, 16)), + ("2012-04-23T09:15:00Z", datetime(2012, 4, 23, 9, 15, 0, 0, timezone.utc)), + ("2012-4-9 4:8:16-0320", datetime(2012, 4, 9, 4, 8, 16, 0, create_tz(-200))), + ("2012-04-23T10:20:30.400+02:30", datetime(2012, 4, 23, 10, 20, 30, 400_000, create_tz(150))), + ("2012-04-23T10:20:30.400+02", datetime(2012, 4, 23, 10, 20, 30, 400_000, create_tz(120))), + ("2012-04-23T10:20:30.400-02", datetime(2012, 4, 23, 10, 20, 30, 400_000, create_tz(-120))), + (b"2012-04-23T10:20:30.400-02", datetime(2012, 4, 23, 10, 20, 30, 400_000, create_tz(-120))), + (datetime(2017, 5, 5), datetime(2017, 5, 5)), + (0, datetime(1970, 1, 1, 0, 0, 0, tzinfo=timezone.utc)), + # Invalid inputs + ("x20120423091500", ValueError), + ("2012-04-56T09:15:90", ValueError), + ("2012-04-23T11:05:00-25:00", ValueError), + (19_999_999_999, datetime(2603, 10, 11, 11, 33, 19, tzinfo=timezone.utc)), # just before watershed + (20_000_000_001, datetime(1970, 8, 20, 11, 33, 20, 1000, tzinfo=timezone.utc)), # just after watershed + (1_549_316_052, datetime(2019, 2, 4, 21, 34, 12, 0, tzinfo=timezone.utc)), # nowish in s + (1_549_316_052_104, datetime(2019, 2, 4, 21, 34, 12, 104_000, tzinfo=timezone.utc)), # nowish in ms + (1_549_316_052_104_324, datetime(2019, 2, 4, 21, 34, 12, 104_324, tzinfo=timezone.utc)), # nowish in μs + (1_549_316_052_104_324_096, datetime(2019, 2, 4, 21, 34, 12, 104_324, tzinfo=timezone.utc)), # nowish in ns + ("infinity", datetime(9999, 12, 31, 23, 59, 59, 999999)), + ("inf", datetime(9999, 12, 31, 23, 59, 59, 999999)), + ("inf ", datetime(9999, 12, 31, 23, 59, 59, 999999)), + (1e50, datetime(9999, 12, 31, 23, 59, 59, 999999)), + (float("inf"), datetime(9999, 12, 31, 23, 59, 59, 999999)), + ("-infinity", datetime(1, 1, 1, 0, 0)), + ("-inf", datetime(1, 1, 1, 0, 0)), + ("nan", ValueError), + ], +) +def test_datetime_parsing(value: Union[str, bytes, int, float], result: Union[datetime, Type[Exception]]) -> None: + if type(result) == type and issubclass(result, Exception): # pyright: ignore[reportUnnecessaryIsInstance] + with pytest.raises(result): + parse_datetime(value) + else: + assert parse_datetime(value) == result diff --git a/tests/test_utils/test_proxy.py b/tests/test_utils/test_proxy.py index 39187d5..f287eb8 100644 --- a/tests/test_utils/test_proxy.py +++ b/tests/test_utils/test_proxy.py @@ -21,3 +21,14 @@ def test_recursive_proxy() -> None: assert dir(proxy) == [] assert type(proxy).__name__ == "RecursiveLazyProxy" assert type(operator.attrgetter("name.foo.bar.baz")(proxy)).__name__ == "RecursiveLazyProxy" + + +def test_isinstance_does_not_error() -> None: + class AlwaysErrorProxy(LazyProxy[Any]): + @override + def __load__(self) -> Any: + raise RuntimeError("Mocking missing dependency") + + proxy = AlwaysErrorProxy() + assert not isinstance(proxy, dict) + assert isinstance(proxy, LazyProxy) diff --git a/tests/utils.py b/tests/utils.py index 51557a9..db3287f 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -4,7 +4,7 @@ import inspect import traceback import contextlib -from typing import Any, TypeVar, Iterator, cast +from typing import Any, TypeVar, Iterator, Sequence, cast from datetime import date, datetime from typing_extensions import Literal, get_args, get_origin, assert_type @@ -15,10 +15,11 @@ is_list_type, is_union_type, extract_type_arg, + is_sequence_type, is_annotated_type, is_type_alias_type, ) -from postgrid._compat import PYDANTIC_V2, field_outer_type, get_model_fields +from postgrid._compat import PYDANTIC_V1, field_outer_type, get_model_fields from postgrid._models import BaseModel BaseModelT = TypeVar("BaseModelT", bound=BaseModel) @@ -27,12 +28,12 @@ def assert_matches_model(model: type[BaseModelT], value: BaseModelT, *, path: list[str]) -> bool: for name, field in get_model_fields(model).items(): field_value = getattr(value, name) - if PYDANTIC_V2: - allow_none = False - else: + if PYDANTIC_V1: # in v1 nullability was structured differently # https://docs.pydantic.dev/2.0/migration/#required-optional-and-nullable-fields allow_none = getattr(field, "allow_none", False) + else: + allow_none = False assert_matches_type( field_outer_type(field), @@ -71,6 +72,13 @@ def assert_matches_type( if is_list_type(type_): return _assert_list_type(type_, value) + if is_sequence_type(type_): + assert isinstance(value, Sequence) + inner_type = get_args(type_)[0] + for entry in value: # type: ignore + assert_type(inner_type, entry) # type: ignore + return + if origin == str: assert isinstance(value, str) elif origin == int: From e2e6808b739591baf7258c1ea49fca1d83b09dfc Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 12 Nov 2025 04:32:00 +0000 Subject: [PATCH 06/13] chore(package): drop Python 3.8 support --- README.md | 4 ++-- pyproject.toml | 5 ++--- src/postgrid/_utils/_sync.py | 34 +++------------------------------- 3 files changed, 7 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index b514139..469d9bf 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![PyPI version](https://img.shields.io/pypi/v/postgrid-python.svg?label=pypi%20(stable))](https://pypi.org/project/postgrid-python/) -The PostGrid Python library provides convenient access to the PostGrid REST API from any Python 3.8+ +The PostGrid Python library provides convenient access to the PostGrid REST API from any Python 3.9+ application. The library includes type definitions for all request params and response fields, and offers both synchronous and asynchronous clients powered by [httpx](https://github.com/encode/httpx). @@ -470,7 +470,7 @@ print(postgrid.__version__) ## Requirements -Python 3.8 or higher. +Python 3.9 or higher. ## Contributing diff --git a/pyproject.toml b/pyproject.toml index a82d942..c59b0a7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,11 +15,10 @@ dependencies = [ "distro>=1.7.0, <2", "sniffio", ] -requires-python = ">= 3.8" +requires-python = ">= 3.9" classifiers = [ "Typing :: Typed", "Intended Audience :: Developers", - "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", @@ -141,7 +140,7 @@ filterwarnings = [ # there are a couple of flags that are still disabled by # default in strict mode as they are experimental and niche. typeCheckingMode = "strict" -pythonVersion = "3.8" +pythonVersion = "3.9" exclude = [ "_dev", diff --git a/src/postgrid/_utils/_sync.py b/src/postgrid/_utils/_sync.py index ad7ec71..f6027c1 100644 --- a/src/postgrid/_utils/_sync.py +++ b/src/postgrid/_utils/_sync.py @@ -1,10 +1,8 @@ from __future__ import annotations -import sys import asyncio import functools -import contextvars -from typing import Any, TypeVar, Callable, Awaitable +from typing import TypeVar, Callable, Awaitable from typing_extensions import ParamSpec import anyio @@ -15,34 +13,11 @@ T_ParamSpec = ParamSpec("T_ParamSpec") -if sys.version_info >= (3, 9): - _asyncio_to_thread = asyncio.to_thread -else: - # backport of https://docs.python.org/3/library/asyncio-task.html#asyncio.to_thread - # for Python 3.8 support - async def _asyncio_to_thread( - func: Callable[T_ParamSpec, T_Retval], /, *args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs - ) -> Any: - """Asynchronously run function *func* in a separate thread. - - Any *args and **kwargs supplied for this function are directly passed - to *func*. Also, the current :class:`contextvars.Context` is propagated, - allowing context variables from the main thread to be accessed in the - separate thread. - - Returns a coroutine that can be awaited to get the eventual result of *func*. - """ - loop = asyncio.events.get_running_loop() - ctx = contextvars.copy_context() - func_call = functools.partial(ctx.run, func, *args, **kwargs) - return await loop.run_in_executor(None, func_call) - - async def to_thread( func: Callable[T_ParamSpec, T_Retval], /, *args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs ) -> T_Retval: if sniffio.current_async_library() == "asyncio": - return await _asyncio_to_thread(func, *args, **kwargs) + return await asyncio.to_thread(func, *args, **kwargs) return await anyio.to_thread.run_sync( functools.partial(func, *args, **kwargs), @@ -53,10 +28,7 @@ async def to_thread( def asyncify(function: Callable[T_ParamSpec, T_Retval]) -> Callable[T_ParamSpec, Awaitable[T_Retval]]: """ Take a blocking function and create an async one that receives the same - positional and keyword arguments. For python version 3.9 and above, it uses - asyncio.to_thread to run the function in a separate thread. For python version - 3.8, it uses locally defined copy of the asyncio.to_thread function which was - introduced in python 3.9. + positional and keyword arguments. Usage: From 53457abee1fea7154fd0918aa194f106ff4a9a51 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 12 Nov 2025 04:32:29 +0000 Subject: [PATCH 07/13] fix: compat with Python 3.14 --- src/postgrid/_models.py | 11 ++++++++--- tests/test_models.py | 8 ++++---- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/postgrid/_models.py b/src/postgrid/_models.py index 6a3cd1d..fcec2cf 100644 --- a/src/postgrid/_models.py +++ b/src/postgrid/_models.py @@ -2,6 +2,7 @@ import os import inspect +import weakref from typing import TYPE_CHECKING, Any, Type, Union, Generic, TypeVar, Callable, Optional, cast from datetime import date, datetime from typing_extensions import ( @@ -573,6 +574,9 @@ class CachedDiscriminatorType(Protocol): __discriminator__: DiscriminatorDetails +DISCRIMINATOR_CACHE: weakref.WeakKeyDictionary[type, DiscriminatorDetails] = weakref.WeakKeyDictionary() + + class DiscriminatorDetails: field_name: str """The name of the discriminator field in the variant class, e.g. @@ -615,8 +619,9 @@ def __init__( def _build_discriminated_union_meta(*, union: type, meta_annotations: tuple[Any, ...]) -> DiscriminatorDetails | None: - if isinstance(union, CachedDiscriminatorType): - return union.__discriminator__ + cached = DISCRIMINATOR_CACHE.get(union) + if cached is not None: + return cached discriminator_field_name: str | None = None @@ -669,7 +674,7 @@ def _build_discriminated_union_meta(*, union: type, meta_annotations: tuple[Any, discriminator_field=discriminator_field_name, discriminator_alias=discriminator_alias, ) - cast(CachedDiscriminatorType, union).__discriminator__ = details + DISCRIMINATOR_CACHE.setdefault(union, details) return details diff --git a/tests/test_models.py b/tests/test_models.py index a8c25dd..a185732 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -9,7 +9,7 @@ from postgrid._utils import PropertyInfo from postgrid._compat import PYDANTIC_V1, parse_obj, model_dump, model_json -from postgrid._models import BaseModel, construct_type +from postgrid._models import DISCRIMINATOR_CACHE, BaseModel, construct_type class BasicModel(BaseModel): @@ -809,7 +809,7 @@ class B(BaseModel): UnionType = cast(Any, Union[A, B]) - assert not hasattr(UnionType, "__discriminator__") + assert not DISCRIMINATOR_CACHE.get(UnionType) m = construct_type( value={"type": "b", "data": "foo"}, type_=cast(Any, Annotated[UnionType, PropertyInfo(discriminator="type")]) @@ -818,7 +818,7 @@ class B(BaseModel): assert m.type == "b" assert m.data == "foo" # type: ignore[comparison-overlap] - discriminator = UnionType.__discriminator__ + discriminator = DISCRIMINATOR_CACHE.get(UnionType) assert discriminator is not None m = construct_type( @@ -830,7 +830,7 @@ class B(BaseModel): # if the discriminator details object stays the same between invocations then # we hit the cache - assert UnionType.__discriminator__ is discriminator + assert DISCRIMINATOR_CACHE.get(UnionType) is discriminator @pytest.mark.skipif(PYDANTIC_V1, reason="TypeAliasType is not supported in Pydantic v1") From 899cab3df49e481eee336a3e585df7bb58334b0b Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 12 Nov 2025 04:39:35 +0000 Subject: [PATCH 08/13] fix(compat): update signatures of `model_dump` and `model_dump_json` for Pydantic v1 --- src/postgrid/_models.py | 41 +++++++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/src/postgrid/_models.py b/src/postgrid/_models.py index fcec2cf..ca9500b 100644 --- a/src/postgrid/_models.py +++ b/src/postgrid/_models.py @@ -257,15 +257,16 @@ def model_dump( mode: Literal["json", "python"] | str = "python", include: IncEx | None = None, exclude: IncEx | None = None, + context: Any | None = None, by_alias: bool | None = None, exclude_unset: bool = False, exclude_defaults: bool = False, exclude_none: bool = False, + exclude_computed_fields: bool = False, round_trip: bool = False, warnings: bool | Literal["none", "warn", "error"] = True, - context: dict[str, Any] | None = None, - serialize_as_any: bool = False, fallback: Callable[[Any], Any] | None = None, + serialize_as_any: bool = False, ) -> dict[str, Any]: """Usage docs: https://docs.pydantic.dev/2.4/concepts/serialization/#modelmodel_dump @@ -273,16 +274,24 @@ def model_dump( Args: mode: The mode in which `to_python` should run. - If mode is 'json', the dictionary will only contain JSON serializable types. - If mode is 'python', the dictionary may contain any Python objects. - include: A list of fields to include in the output. - exclude: A list of fields to exclude from the output. + If mode is 'json', the output will only contain JSON serializable types. + If mode is 'python', the output may contain non-JSON-serializable Python objects. + include: A set of fields to include in the output. + exclude: A set of fields to exclude from the output. + context: Additional context to pass to the serializer. by_alias: Whether to use the field's alias in the dictionary key if defined. - exclude_unset: Whether to exclude fields that are unset or None from the output. - exclude_defaults: Whether to exclude fields that are set to their default value from the output. - exclude_none: Whether to exclude fields that have a value of `None` from the output. - round_trip: Whether to enable serialization and deserialization round-trip support. - warnings: Whether to log warnings when invalid fields are encountered. + exclude_unset: Whether to exclude fields that have not been explicitly set. + exclude_defaults: Whether to exclude fields that are set to their default value. + exclude_none: Whether to exclude fields that have a value of `None`. + exclude_computed_fields: Whether to exclude computed fields. + While this can be useful for round-tripping, it is usually recommended to use the dedicated + `round_trip` parameter instead. + round_trip: If True, dumped values should be valid as input for non-idempotent types such as Json[T]. + warnings: How to handle serialization errors. False/"none" ignores them, True/"warn" logs errors, + "error" raises a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError]. + fallback: A function to call when an unknown value is encountered. If not provided, + a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError] error is raised. + serialize_as_any: Whether to serialize fields with duck-typing serialization behavior. Returns: A dictionary representation of the model. @@ -299,6 +308,8 @@ def model_dump( raise ValueError("serialize_as_any is only supported in Pydantic v2") if fallback is not None: raise ValueError("fallback is only supported in Pydantic v2") + if exclude_computed_fields != False: + raise ValueError("exclude_computed_fields is only supported in Pydantic v2") dumped = super().dict( # pyright: ignore[reportDeprecated] include=include, exclude=exclude, @@ -315,15 +326,17 @@ def model_dump_json( self, *, indent: int | None = None, + ensure_ascii: bool = False, include: IncEx | None = None, exclude: IncEx | None = None, + context: Any | None = None, by_alias: bool | None = None, exclude_unset: bool = False, exclude_defaults: bool = False, exclude_none: bool = False, + exclude_computed_fields: bool = False, round_trip: bool = False, warnings: bool | Literal["none", "warn", "error"] = True, - context: dict[str, Any] | None = None, fallback: Callable[[Any], Any] | None = None, serialize_as_any: bool = False, ) -> str: @@ -355,6 +368,10 @@ def model_dump_json( raise ValueError("serialize_as_any is only supported in Pydantic v2") if fallback is not None: raise ValueError("fallback is only supported in Pydantic v2") + if ensure_ascii != False: + raise ValueError("ensure_ascii is only supported in Pydantic v2") + if exclude_computed_fields != False: + raise ValueError("exclude_computed_fields is only supported in Pydantic v2") return super().json( # type: ignore[reportDeprecated] indent=indent, include=include, From 900a34bacc1f3e587ae48d889865a4c4a29cee66 Mon Sep 17 00:00:00 2001 From: Apaar Madan Date: Wed, 12 Nov 2025 22:58:23 -0500 Subject: [PATCH 09/13] Prepare request API key based on URL (#2) --- src/postgrid/_client.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/postgrid/_client.py b/src/postgrid/_client.py index c3e506b..248d793 100644 --- a/src/postgrid/_client.py +++ b/src/postgrid/_client.py @@ -115,6 +115,19 @@ def __init__( self.with_raw_response = PostGridWithRawResponse(self) self.with_streaming_response = PostGridWithStreamedResponse(self) + @override + def _prepare_request( + self, + request: httpx.Request, # noqa: ARG002 + ) -> None: + # Update API key header based on URL of request + if 'print-mail' in request.url: + request.headers['x-api-key'] = self.print_mail_api_key + else: + request.headers['x-api-key'] = self.address_verification_api_key + + return None + @property @override def qs(self) -> Querystring: @@ -322,6 +335,19 @@ def __init__( self.with_raw_response = AsyncPostGridWithRawResponse(self) self.with_streaming_response = AsyncPostGridWithStreamedResponse(self) + @override + async def _prepare_request( + self, + request: httpx.Request, # noqa: ARG002 + ) -> None: + # Update API key header based on URL of request + if 'print-mail' in request.url: + request.headers['x-api-key'] = self.print_mail_api_key + else: + request.headers['x-api-key'] = self.address_verification_api_key + + return None + @property @override def qs(self) -> Querystring: From 9f737cc73d70892712759f52624ade9f9bd8ba7e Mon Sep 17 00:00:00 2001 From: Apaar Madan Date: Wed, 12 Nov 2025 23:02:15 -0500 Subject: [PATCH 10/13] fix(api): Prepare request fix --- src/postgrid/_client.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/postgrid/_client.py b/src/postgrid/_client.py index 248d793..b4b36f6 100644 --- a/src/postgrid/_client.py +++ b/src/postgrid/_client.py @@ -121,7 +121,7 @@ def _prepare_request( request: httpx.Request, # noqa: ARG002 ) -> None: # Update API key header based on URL of request - if 'print-mail' in request.url: + if 'print-mail' in request.url.path: request.headers['x-api-key'] = self.print_mail_api_key else: request.headers['x-api-key'] = self.address_verification_api_key @@ -341,7 +341,7 @@ async def _prepare_request( request: httpx.Request, # noqa: ARG002 ) -> None: # Update API key header based on URL of request - if 'print-mail' in request.url: + if 'print-mail' in request.url.path: request.headers['x-api-key'] = self.print_mail_api_key else: request.headers['x-api-key'] = self.address_verification_api_key From 0dea36d9fcab59fec8412c753bc66b4dbc5b99e1 Mon Sep 17 00:00:00 2001 From: Apaar Madan Date: Wed, 12 Nov 2025 23:12:34 -0500 Subject: [PATCH 11/13] fix(api): Pacify pyright --- src/postgrid/_client.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/postgrid/_client.py b/src/postgrid/_client.py index b4b36f6..58281bc 100644 --- a/src/postgrid/_client.py +++ b/src/postgrid/_client.py @@ -122,8 +122,9 @@ def _prepare_request( ) -> None: # Update API key header based on URL of request if 'print-mail' in request.url.path: - request.headers['x-api-key'] = self.print_mail_api_key - else: + if self.print_mail_api_key: + request.headers['x-api-key'] = self.print_mail_api_key + elif self.address_verification_api_key: request.headers['x-api-key'] = self.address_verification_api_key return None @@ -342,8 +343,9 @@ async def _prepare_request( ) -> None: # Update API key header based on URL of request if 'print-mail' in request.url.path: - request.headers['x-api-key'] = self.print_mail_api_key - else: + if self.print_mail_api_key: + request.headers['x-api-key'] = self.print_mail_api_key + elif self.address_verification_api_key: request.headers['x-api-key'] = self.address_verification_api_key return None From fdbeb1a3cf24dfbdaff4bf08966b6b04853e2840 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 13 Nov 2025 04:14:30 +0000 Subject: [PATCH 12/13] fix(api): remove unsupported collaterals --- .stats.yml | 4 +- api.md | 22 - src/postgrid/resources/print_mail/__init__.py | 14 - src/postgrid/resources/print_mail/boxes.py | 562 ------------------ src/postgrid/resources/print_mail/cheques.py | 63 +- src/postgrid/resources/print_mail/letters.py | 182 +++++- .../print_mail/order_profiles/cheques.py | 124 +++- .../print_mail/order_profiles/letters.py | 123 +++- .../print_mail/order_profiles/postcards.py | 123 +++- .../print_mail/order_profiles/self_mailers.py | 123 +++- .../resources/print_mail/postcards.py | 245 +++++++- .../resources/print_mail/print_mail.py | 32 - .../resources/print_mail/self_mailers.py | 245 +++++++- src/postgrid/types/print_mail/__init__.py | 9 - src/postgrid/types/print_mail/box.py | 134 ----- .../types/print_mail/box_cheque_base.py | 35 -- .../types/print_mail/box_cheque_base_param.py | 36 -- .../types/print_mail/box_create_params.py | 81 --- .../types/print_mail/box_list_params.py | 22 - src/postgrid/types/print_mail/cancellation.py | 21 - src/postgrid/types/print_mail/cheque.py | 52 +- .../types/print_mail/cheque_create_params.py | 33 +- src/postgrid/types/print_mail/letter.py | 52 +- .../types/print_mail/letter_create_params.py | 65 +- .../types/print_mail/order_imb_status.py | 7 - .../types/print_mail/order_mailing_class.py | 34 -- .../order_profiles/cheque_create_params.py | 35 +- .../order_profiles/cheque_list_response.py | 32 +- .../order_profiles/cheque_profile.py | 32 +- .../order_profiles/cheque_update_params.py | 35 +- .../order_profiles/letter_create_params.py | 33 +- .../order_profiles/letter_profile.py | 32 +- .../order_profiles/letter_update_params.py | 33 +- .../order_profiles/postcard_create_params.py | 35 +- .../order_profiles/postcard_profile.py | 32 +- .../order_profiles/postcard_update_params.py | 35 +- .../self_mailer_create_params.py | 35 +- .../order_profiles/self_mailer_profile.py | 32 +- .../self_mailer_update_params.py | 35 +- src/postgrid/types/print_mail/order_status.py | 7 - src/postgrid/types/print_mail/postcard.py | 52 +- .../print_mail/postcard_create_params.py | 99 ++- src/postgrid/types/print_mail/self_mailer.py | 52 +- .../print_mail/self_mailer_create_params.py | 99 ++- tests/api_resources/print_mail/test_boxes.py | 452 -------------- 45 files changed, 2033 insertions(+), 1607 deletions(-) delete mode 100644 src/postgrid/resources/print_mail/boxes.py delete mode 100644 src/postgrid/types/print_mail/box.py delete mode 100644 src/postgrid/types/print_mail/box_cheque_base.py delete mode 100644 src/postgrid/types/print_mail/box_cheque_base_param.py delete mode 100644 src/postgrid/types/print_mail/box_create_params.py delete mode 100644 src/postgrid/types/print_mail/box_list_params.py delete mode 100644 src/postgrid/types/print_mail/cancellation.py delete mode 100644 src/postgrid/types/print_mail/order_imb_status.py delete mode 100644 src/postgrid/types/print_mail/order_mailing_class.py delete mode 100644 src/postgrid/types/print_mail/order_status.py delete mode 100644 tests/api_resources/print_mail/test_boxes.py diff --git a/.stats.yml b/.stats.yml index 6a4ca1c..a7354cb 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 91 +configured_endpoints: 87 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/postgrid%2Fpostgrid-be6a47768faf3612d1dc9c8a108edb10a6c5a4e52b78cc7f4768e1d497e11e08.yml openapi_spec_hash: a3ed2b74031c834a724b67db9ab6b23d -config_hash: 371afa1c13a4b49cacbe76aaa665a8aa +config_hash: 3e6912e5fb40d06f5dc19a10e7b4e703 diff --git a/api.md b/api.md index 64a2eb5..b315b61 100644 --- a/api.md +++ b/api.md @@ -45,28 +45,6 @@ Methods: - client.print_mail.bank_accounts.list(\*\*params) -> SyncSkipLimit[BankAccount] - client.print_mail.bank_accounts.delete(id) -> BankAccountDeleteResponse -## Boxes - -Types: - -```python -from postgrid.types.print_mail import ( - Box, - BoxChequeBase, - Cancellation, - OrderImbStatus, - OrderMailingClass, - OrderStatus, -) -``` - -Methods: - -- client.print_mail.boxes.create(\*\*params) -> Box -- client.print_mail.boxes.retrieve(id) -> Box -- client.print_mail.boxes.list(\*\*params) -> SyncSkipLimit[Box] -- client.print_mail.boxes.delete(id) -> Box - ## Campaigns Types: diff --git a/src/postgrid/resources/print_mail/__init__.py b/src/postgrid/resources/print_mail/__init__.py index c3138c6..2f87a66 100644 --- a/src/postgrid/resources/print_mail/__init__.py +++ b/src/postgrid/resources/print_mail/__init__.py @@ -1,13 +1,5 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from .boxes import ( - BoxesResource, - AsyncBoxesResource, - BoxesResourceWithRawResponse, - AsyncBoxesResourceWithRawResponse, - BoxesResourceWithStreamingResponse, - AsyncBoxesResourceWithStreamingResponse, -) from .cheques import ( ChequesResource, AsyncChequesResource, @@ -128,12 +120,6 @@ "AsyncBankAccountsResourceWithRawResponse", "BankAccountsResourceWithStreamingResponse", "AsyncBankAccountsResourceWithStreamingResponse", - "BoxesResource", - "AsyncBoxesResource", - "BoxesResourceWithRawResponse", - "AsyncBoxesResourceWithRawResponse", - "BoxesResourceWithStreamingResponse", - "AsyncBoxesResourceWithStreamingResponse", "CampaignsResource", "AsyncCampaignsResource", "CampaignsResourceWithRawResponse", diff --git a/src/postgrid/resources/print_mail/boxes.py b/src/postgrid/resources/print_mail/boxes.py deleted file mode 100644 index 2f9e190..0000000 --- a/src/postgrid/resources/print_mail/boxes.py +++ /dev/null @@ -1,562 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict, Union, Iterable -from datetime import datetime - -import httpx - -from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from ...pagination import SyncSkipLimit, AsyncSkipLimit -from ..._base_client import AsyncPaginator, make_request_options -from ...types.print_mail import OrderMailingClass, box_list_params, box_create_params -from ...types.print_mail.box import Box -from ...types.print_mail.order_mailing_class import OrderMailingClass - -__all__ = ["BoxesResource", "AsyncBoxesResource"] - - -class BoxesResource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> BoxesResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/postgrid/postgrid-python#accessing-raw-response-data-eg-headers - """ - return BoxesResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> BoxesResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/postgrid/postgrid-python#with_streaming_response - """ - return BoxesResourceWithStreamingResponse(self) - - def create( - self, - *, - cheques: Iterable[box_create_params.Cheque], - from_: box_create_params.From, - to: box_create_params.To, - description: str | Omit = omit, - mailing_class: OrderMailingClass | Omit = omit, - merge_variables: Dict[str, object] | Omit = omit, - metadata: Dict[str, object] | Omit = omit, - send_date: Union[str, datetime] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> Box: - """This endpoint allows you to create a box containing up to 100 cheques. - - A Box is - mailed to a single destination. - - To create a box. You must specify: - - - `to`: The recipient (contact or contact ID) - - `from`: The sender (contact or contact ID) - - `cheques`: An array of cheques to go in the box - - For each cheque You must specify: - - - `to`: The recipient (contact or contact ID) - - `from`: The sender (contact or contact ID) - - `bankAccount`: The bank account ID - - `amount`: The amount to be sent - - `number`: The cheque number - - Args: - cheques: The cheques to be mailed in the box. Only 100 cheques can be included in a box - at a time. - - from_: The 'from' (sender) of the entire box. Accepts inline ContactCreate or a - contactID. - - to: The recipient of this order. You can either supply the contact information - inline here or provide a contact ID. PostGrid will automatically deduplicate - contacts regardless of whether you provide the information inline here or call - the contact creation endpoint. - - description: An optional string describing this resource. Will be visible in the API and the - dashboard. - - mailing_class: The mailing class of this order. If not provided, automatically set to - `first_class`. - - merge_variables: These will be merged with the variables in the template or HTML you create this - order with. The keys in this object should match the variable names in the - template _exactly_ as they are case-sensitive. Note that these _do not_ apply to - PDFs uploaded with the order. - - metadata: See the section on Metadata. - - send_date: This order will transition from `ready` to `printing` on the day after this - date. You can use this parameter to schedule orders for a future date. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._post( - "/print-mail/v1/boxes", - body=maybe_transform( - { - "cheques": cheques, - "from_": from_, - "to": to, - "description": description, - "mailing_class": mailing_class, - "merge_variables": merge_variables, - "metadata": metadata, - "send_date": send_date, - }, - box_create_params.BoxCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=Box, - ) - - def retrieve( - self, - id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> Box: - """ - Retrieve a box by ID. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return self._get( - f"/print-mail/v1/boxes/{id}", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=Box, - ) - - def list( - self, - *, - limit: int | Omit = omit, - search: str | Omit = omit, - skip: int | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> SyncSkipLimit[Box]: - """ - List all boxes. - - Args: - search: You can supply any string to help narrow down the list of resources. For - example, if you pass `"New York"` (quoted), it will return resources that have - that string present somewhere in their response. Alternatively, you can supply a - structured search query. See the documentation on `StructuredSearchQuery` for - more details. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._get_api_list( - "/print-mail/v1/boxes", - page=SyncSkipLimit[Box], - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "limit": limit, - "search": search, - "skip": skip, - }, - box_list_params.BoxListParams, - ), - ), - model=Box, - ) - - def delete( - self, - id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> Box: - """ - Cancel a box by ID (cannot be undone). - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return self._delete( - f"/print-mail/v1/boxes/{id}", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=Box, - ) - - -class AsyncBoxesResource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncBoxesResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/postgrid/postgrid-python#accessing-raw-response-data-eg-headers - """ - return AsyncBoxesResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncBoxesResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/postgrid/postgrid-python#with_streaming_response - """ - return AsyncBoxesResourceWithStreamingResponse(self) - - async def create( - self, - *, - cheques: Iterable[box_create_params.Cheque], - from_: box_create_params.From, - to: box_create_params.To, - description: str | Omit = omit, - mailing_class: OrderMailingClass | Omit = omit, - merge_variables: Dict[str, object] | Omit = omit, - metadata: Dict[str, object] | Omit = omit, - send_date: Union[str, datetime] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> Box: - """This endpoint allows you to create a box containing up to 100 cheques. - - A Box is - mailed to a single destination. - - To create a box. You must specify: - - - `to`: The recipient (contact or contact ID) - - `from`: The sender (contact or contact ID) - - `cheques`: An array of cheques to go in the box - - For each cheque You must specify: - - - `to`: The recipient (contact or contact ID) - - `from`: The sender (contact or contact ID) - - `bankAccount`: The bank account ID - - `amount`: The amount to be sent - - `number`: The cheque number - - Args: - cheques: The cheques to be mailed in the box. Only 100 cheques can be included in a box - at a time. - - from_: The 'from' (sender) of the entire box. Accepts inline ContactCreate or a - contactID. - - to: The recipient of this order. You can either supply the contact information - inline here or provide a contact ID. PostGrid will automatically deduplicate - contacts regardless of whether you provide the information inline here or call - the contact creation endpoint. - - description: An optional string describing this resource. Will be visible in the API and the - dashboard. - - mailing_class: The mailing class of this order. If not provided, automatically set to - `first_class`. - - merge_variables: These will be merged with the variables in the template or HTML you create this - order with. The keys in this object should match the variable names in the - template _exactly_ as they are case-sensitive. Note that these _do not_ apply to - PDFs uploaded with the order. - - metadata: See the section on Metadata. - - send_date: This order will transition from `ready` to `printing` on the day after this - date. You can use this parameter to schedule orders for a future date. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._post( - "/print-mail/v1/boxes", - body=await async_maybe_transform( - { - "cheques": cheques, - "from_": from_, - "to": to, - "description": description, - "mailing_class": mailing_class, - "merge_variables": merge_variables, - "metadata": metadata, - "send_date": send_date, - }, - box_create_params.BoxCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=Box, - ) - - async def retrieve( - self, - id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> Box: - """ - Retrieve a box by ID. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return await self._get( - f"/print-mail/v1/boxes/{id}", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=Box, - ) - - def list( - self, - *, - limit: int | Omit = omit, - search: str | Omit = omit, - skip: int | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> AsyncPaginator[Box, AsyncSkipLimit[Box]]: - """ - List all boxes. - - Args: - search: You can supply any string to help narrow down the list of resources. For - example, if you pass `"New York"` (quoted), it will return resources that have - that string present somewhere in their response. Alternatively, you can supply a - structured search query. See the documentation on `StructuredSearchQuery` for - more details. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._get_api_list( - "/print-mail/v1/boxes", - page=AsyncSkipLimit[Box], - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "limit": limit, - "search": search, - "skip": skip, - }, - box_list_params.BoxListParams, - ), - ), - model=Box, - ) - - async def delete( - self, - id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> Box: - """ - Cancel a box by ID (cannot be undone). - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return await self._delete( - f"/print-mail/v1/boxes/{id}", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=Box, - ) - - -class BoxesResourceWithRawResponse: - def __init__(self, boxes: BoxesResource) -> None: - self._boxes = boxes - - self.create = to_raw_response_wrapper( - boxes.create, - ) - self.retrieve = to_raw_response_wrapper( - boxes.retrieve, - ) - self.list = to_raw_response_wrapper( - boxes.list, - ) - self.delete = to_raw_response_wrapper( - boxes.delete, - ) - - -class AsyncBoxesResourceWithRawResponse: - def __init__(self, boxes: AsyncBoxesResource) -> None: - self._boxes = boxes - - self.create = async_to_raw_response_wrapper( - boxes.create, - ) - self.retrieve = async_to_raw_response_wrapper( - boxes.retrieve, - ) - self.list = async_to_raw_response_wrapper( - boxes.list, - ) - self.delete = async_to_raw_response_wrapper( - boxes.delete, - ) - - -class BoxesResourceWithStreamingResponse: - def __init__(self, boxes: BoxesResource) -> None: - self._boxes = boxes - - self.create = to_streamed_response_wrapper( - boxes.create, - ) - self.retrieve = to_streamed_response_wrapper( - boxes.retrieve, - ) - self.list = to_streamed_response_wrapper( - boxes.list, - ) - self.delete = to_streamed_response_wrapper( - boxes.delete, - ) - - -class AsyncBoxesResourceWithStreamingResponse: - def __init__(self, boxes: AsyncBoxesResource) -> None: - self._boxes = boxes - - self.create = async_to_streamed_response_wrapper( - boxes.create, - ) - self.retrieve = async_to_streamed_response_wrapper( - boxes.retrieve, - ) - self.list = async_to_streamed_response_wrapper( - boxes.list, - ) - self.delete = async_to_streamed_response_wrapper( - boxes.delete, - ) diff --git a/src/postgrid/resources/print_mail/cheques.py b/src/postgrid/resources/print_mail/cheques.py index 44d389e..8239622 100644 --- a/src/postgrid/resources/print_mail/cheques.py +++ b/src/postgrid/resources/print_mail/cheques.py @@ -20,11 +20,10 @@ ) from ...pagination import SyncSkipLimit, AsyncSkipLimit from ..._base_client import AsyncPaginator, make_request_options -from ...types.print_mail import ChequeSize, OrderMailingClass, cheque_list_params, cheque_create_params +from ...types.print_mail import ChequeSize, cheque_list_params, cheque_create_params from ...types.print_mail.cheque import Cheque from ...types.print_mail.cheque_size import ChequeSize from ...types.print_mail.digital_only_param import DigitalOnlyParam -from ...types.print_mail.order_mailing_class import OrderMailingClass from ...types.print_mail.cheque_retrieve_url_response import ChequeRetrieveURLResponse __all__ = ["ChequesResource", "AsyncChequesResource"] @@ -62,7 +61,35 @@ def create( digital_only: DigitalOnlyParam | Omit = omit, envelope: Union[Literal["standard"], str] | Omit = omit, logo_url: str | Omit = omit, - mailing_class: OrderMailingClass | Omit = omit, + mailing_class: Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ] + | Omit = omit, memo: str | Omit = omit, merge_variables: Dict[str, object] | Omit = omit, message: str | Omit = omit, @@ -428,7 +455,35 @@ async def create( digital_only: DigitalOnlyParam | Omit = omit, envelope: Union[Literal["standard"], str] | Omit = omit, logo_url: str | Omit = omit, - mailing_class: OrderMailingClass | Omit = omit, + mailing_class: Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ] + | Omit = omit, memo: str | Omit = omit, merge_variables: Dict[str, object] | Omit = omit, message: str | Omit = omit, diff --git a/src/postgrid/resources/print_mail/letters.py b/src/postgrid/resources/print_mail/letters.py index b134ee6..0c8b7eb 100644 --- a/src/postgrid/resources/print_mail/letters.py +++ b/src/postgrid/resources/print_mail/letters.py @@ -23,7 +23,6 @@ from ...types.print_mail import ( LetterSize, AddressPlacement, - OrderMailingClass, letter_list_params, letter_create_params, ) @@ -32,7 +31,6 @@ from ...types.print_mail.address_placement import AddressPlacement from ...types.print_mail.attached_pdf_param import AttachedPdfParam from ...types.print_mail.plastic_card_param import PlasticCardParam -from ...types.print_mail.order_mailing_class import OrderMailingClass from ...types.print_mail.letter_retrieve_url_response import LetterRetrieveURLResponse __all__ = ["LettersResource", "AsyncLettersResource"] @@ -71,7 +69,35 @@ def create( description: str | Omit = omit, double_sided: bool | Omit = omit, envelope: str | Omit = omit, - mailing_class: OrderMailingClass | Omit = omit, + mailing_class: Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ] + | Omit = omit, merge_variables: Dict[str, object] | Omit = omit, metadata: Dict[str, object] | Omit = omit, perforated_page: Literal[1] | Omit = omit, @@ -201,7 +227,35 @@ def create( description: str | Omit = omit, double_sided: bool | Omit = omit, envelope: str | Omit = omit, - mailing_class: OrderMailingClass | Omit = omit, + mailing_class: Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ] + | Omit = omit, merge_variables: Dict[str, object] | Omit = omit, metadata: Dict[str, object] | Omit = omit, perforated_page: Literal[1] | Omit = omit, @@ -297,7 +351,35 @@ def create( description: str | Omit = omit, double_sided: bool | Omit = omit, envelope: str | Omit = omit, - mailing_class: OrderMailingClass | Omit = omit, + mailing_class: Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ] + | Omit = omit, merge_variables: Dict[str, object] | Omit = omit, metadata: Dict[str, object] | Omit = omit, perforated_page: Literal[1] | Omit = omit, @@ -536,7 +618,35 @@ async def create( description: str | Omit = omit, double_sided: bool | Omit = omit, envelope: str | Omit = omit, - mailing_class: OrderMailingClass | Omit = omit, + mailing_class: Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ] + | Omit = omit, merge_variables: Dict[str, object] | Omit = omit, metadata: Dict[str, object] | Omit = omit, perforated_page: Literal[1] | Omit = omit, @@ -666,7 +776,35 @@ async def create( description: str | Omit = omit, double_sided: bool | Omit = omit, envelope: str | Omit = omit, - mailing_class: OrderMailingClass | Omit = omit, + mailing_class: Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ] + | Omit = omit, merge_variables: Dict[str, object] | Omit = omit, metadata: Dict[str, object] | Omit = omit, perforated_page: Literal[1] | Omit = omit, @@ -762,7 +900,35 @@ async def create( description: str | Omit = omit, double_sided: bool | Omit = omit, envelope: str | Omit = omit, - mailing_class: OrderMailingClass | Omit = omit, + mailing_class: Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ] + | Omit = omit, merge_variables: Dict[str, object] | Omit = omit, metadata: Dict[str, object] | Omit = omit, perforated_page: Literal[1] | Omit = omit, diff --git a/src/postgrid/resources/print_mail/order_profiles/cheques.py b/src/postgrid/resources/print_mail/order_profiles/cheques.py index 3cb9194..4c1dccf 100644 --- a/src/postgrid/resources/print_mail/order_profiles/cheques.py +++ b/src/postgrid/resources/print_mail/order_profiles/cheques.py @@ -3,6 +3,7 @@ from __future__ import annotations from typing import Dict, Union, Optional +from typing_extensions import Literal import httpx @@ -28,7 +29,7 @@ ) from ....pagination import SyncSkipLimit, AsyncSkipLimit from ...._base_client import AsyncPaginator, make_request_options -from ....types.print_mail import ChequeSize, OrderMailingClass +from ....types.print_mail import ChequeSize from ....types.print_mail.cheque_size import ChequeSize from ....types.print_mail.order_profiles import ( CurrencyCode, @@ -37,7 +38,6 @@ cheque_update_params, cheque_retrieve_params, ) -from ....types.print_mail.order_mailing_class import OrderMailingClass from ....types.print_mail.order_profiles.currency_code import CurrencyCode from ....types.print_mail.order_profiles.cheque_profile import ChequeProfile from ....types.print_mail.order_profiles.cheque_list_response import ChequeListResponse @@ -77,7 +77,35 @@ def create( letter_pdf: Union[str, Base64FileInput] | Omit = omit, letter_template: str | Omit = omit, logo: Optional[str] | Omit = omit, - mailing_class: OrderMailingClass | Omit = omit, + mailing_class: Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ] + | Omit = omit, memo: Optional[str] | Omit = omit, merge_variables: Optional[Dict[str, object]] | Omit = omit, message: Optional[str] | Omit = omit, @@ -216,7 +244,35 @@ def update( letter_pdf: Union[str, Base64FileInput] | Omit = omit, letter_template: str | Omit = omit, logo: Optional[str] | Omit = omit, - mailing_class: OrderMailingClass | Omit = omit, + mailing_class: Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ] + | Omit = omit, memo: Optional[str] | Omit = omit, merge_variables: Optional[Dict[str, object]] | Omit = omit, message: Optional[str] | Omit = omit, @@ -419,7 +475,35 @@ async def create( letter_pdf: Union[str, Base64FileInput] | Omit = omit, letter_template: str | Omit = omit, logo: Optional[str] | Omit = omit, - mailing_class: OrderMailingClass | Omit = omit, + mailing_class: Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ] + | Omit = omit, memo: Optional[str] | Omit = omit, merge_variables: Optional[Dict[str, object]] | Omit = omit, message: Optional[str] | Omit = omit, @@ -558,7 +642,35 @@ async def update( letter_pdf: Union[str, Base64FileInput] | Omit = omit, letter_template: str | Omit = omit, logo: Optional[str] | Omit = omit, - mailing_class: OrderMailingClass | Omit = omit, + mailing_class: Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ] + | Omit = omit, memo: Optional[str] | Omit = omit, merge_variables: Optional[Dict[str, object]] | Omit = omit, message: Optional[str] | Omit = omit, diff --git a/src/postgrid/resources/print_mail/order_profiles/letters.py b/src/postgrid/resources/print_mail/order_profiles/letters.py index 692ecbf..e03dc46 100644 --- a/src/postgrid/resources/print_mail/order_profiles/letters.py +++ b/src/postgrid/resources/print_mail/order_profiles/letters.py @@ -19,7 +19,7 @@ ) from ....pagination import SyncSkipLimit, AsyncSkipLimit from ...._base_client import AsyncPaginator, make_request_options -from ....types.print_mail import LetterSize, AddressPlacement, OrderMailingClass +from ....types.print_mail import LetterSize, AddressPlacement from ....types.print_mail.letter_size import LetterSize from ....types.print_mail.order_profiles import ( letter_list_params, @@ -29,7 +29,6 @@ ) from ....types.print_mail.address_placement import AddressPlacement from ....types.print_mail.attached_pdf_param import AttachedPdfParam -from ....types.print_mail.order_mailing_class import OrderMailingClass from ....types.print_mail.order_profiles.letter_profile import LetterProfile from ....types.print_mail.order_profiles.letter_delete_response import LetterDeleteResponse @@ -67,7 +66,35 @@ def create( description: Optional[str] | Omit = omit, double_sided: bool | Omit = omit, envelope: str | Omit = omit, - mailing_class: OrderMailingClass | Omit = omit, + mailing_class: Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ] + | Omit = omit, merge_variables: Optional[Dict[str, object]] | Omit = omit, metadata: Optional[Dict[str, str]] | Omit = omit, pdf: str | Omit = omit, @@ -208,7 +235,35 @@ def update( description: Optional[str] | Omit = omit, double_sided: bool | Omit = omit, envelope: str | Omit = omit, - mailing_class: OrderMailingClass | Omit = omit, + mailing_class: Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ] + | Omit = omit, merge_variables: Optional[Dict[str, object]] | Omit = omit, metadata: Optional[Dict[str, str]] | Omit = omit, pdf: str | Omit = omit, @@ -417,7 +472,35 @@ async def create( description: Optional[str] | Omit = omit, double_sided: bool | Omit = omit, envelope: str | Omit = omit, - mailing_class: OrderMailingClass | Omit = omit, + mailing_class: Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ] + | Omit = omit, merge_variables: Optional[Dict[str, object]] | Omit = omit, metadata: Optional[Dict[str, str]] | Omit = omit, pdf: str | Omit = omit, @@ -558,7 +641,35 @@ async def update( description: Optional[str] | Omit = omit, double_sided: bool | Omit = omit, envelope: str | Omit = omit, - mailing_class: OrderMailingClass | Omit = omit, + mailing_class: Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ] + | Omit = omit, merge_variables: Optional[Dict[str, object]] | Omit = omit, metadata: Optional[Dict[str, str]] | Omit = omit, pdf: str | Omit = omit, diff --git a/src/postgrid/resources/print_mail/order_profiles/postcards.py b/src/postgrid/resources/print_mail/order_profiles/postcards.py index f46367a..5677359 100644 --- a/src/postgrid/resources/print_mail/order_profiles/postcards.py +++ b/src/postgrid/resources/print_mail/order_profiles/postcards.py @@ -3,6 +3,7 @@ from __future__ import annotations from typing import Dict, Optional +from typing_extensions import Literal import httpx @@ -18,7 +19,6 @@ ) from ....pagination import SyncSkipLimit, AsyncSkipLimit from ...._base_client import AsyncPaginator, make_request_options -from ....types.print_mail import OrderMailingClass from ....types.print_mail.order_profiles import ( PostcardSize, postcard_list_params, @@ -26,7 +26,6 @@ postcard_update_params, postcard_retrieve_params, ) -from ....types.print_mail.order_mailing_class import OrderMailingClass from ....types.print_mail.order_profiles.postcard_size import PostcardSize from ....types.print_mail.order_profiles.postcard_profile import PostcardProfile from ....types.print_mail.order_profiles.postcard_delete_response import PostcardDeleteResponse @@ -62,7 +61,35 @@ def create( back_template: str | Omit = omit, description: Optional[str] | Omit = omit, front_template: str | Omit = omit, - mailing_class: OrderMailingClass | Omit = omit, + mailing_class: Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ] + | Omit = omit, merge_variables: Optional[Dict[str, object]] | Omit = omit, metadata: Optional[Dict[str, str]] | Omit = omit, pdf: str | Omit = omit, @@ -181,7 +208,35 @@ def update( back_template: str | Omit = omit, description: Optional[str] | Omit = omit, front_template: str | Omit = omit, - mailing_class: OrderMailingClass | Omit = omit, + mailing_class: Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ] + | Omit = omit, merge_variables: Optional[Dict[str, object]] | Omit = omit, metadata: Optional[Dict[str, str]] | Omit = omit, pdf: str | Omit = omit, @@ -363,7 +418,35 @@ async def create( back_template: str | Omit = omit, description: Optional[str] | Omit = omit, front_template: str | Omit = omit, - mailing_class: OrderMailingClass | Omit = omit, + mailing_class: Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ] + | Omit = omit, merge_variables: Optional[Dict[str, object]] | Omit = omit, metadata: Optional[Dict[str, str]] | Omit = omit, pdf: str | Omit = omit, @@ -482,7 +565,35 @@ async def update( back_template: str | Omit = omit, description: Optional[str] | Omit = omit, front_template: str | Omit = omit, - mailing_class: OrderMailingClass | Omit = omit, + mailing_class: Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ] + | Omit = omit, merge_variables: Optional[Dict[str, object]] | Omit = omit, metadata: Optional[Dict[str, str]] | Omit = omit, pdf: str | Omit = omit, diff --git a/src/postgrid/resources/print_mail/order_profiles/self_mailers.py b/src/postgrid/resources/print_mail/order_profiles/self_mailers.py index 7da1682..d786556 100644 --- a/src/postgrid/resources/print_mail/order_profiles/self_mailers.py +++ b/src/postgrid/resources/print_mail/order_profiles/self_mailers.py @@ -3,6 +3,7 @@ from __future__ import annotations from typing import Dict, Optional +from typing_extensions import Literal import httpx @@ -18,7 +19,6 @@ ) from ....pagination import SyncSkipLimit, AsyncSkipLimit from ...._base_client import AsyncPaginator, make_request_options -from ....types.print_mail import OrderMailingClass from ....types.print_mail.order_profiles import ( SelfMailerSize, self_mailer_list_params, @@ -26,7 +26,6 @@ self_mailer_update_params, self_mailer_retrieve_params, ) -from ....types.print_mail.order_mailing_class import OrderMailingClass from ....types.print_mail.order_profiles.self_mailer_size import SelfMailerSize from ....types.print_mail.order_profiles.self_mailer_profile import SelfMailerProfile from ....types.print_mail.order_profiles.self_mailer_delete_response import SelfMailerDeleteResponse @@ -61,7 +60,35 @@ def create( expand: SequenceNotStr[str] | Omit = omit, description: Optional[str] | Omit = omit, inside_template: str | Omit = omit, - mailing_class: OrderMailingClass | Omit = omit, + mailing_class: Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ] + | Omit = omit, merge_variables: Optional[Dict[str, object]] | Omit = omit, metadata: Optional[Dict[str, str]] | Omit = omit, outside_template: str | Omit = omit, @@ -180,7 +207,35 @@ def update( expand: SequenceNotStr[str] | Omit = omit, description: Optional[str] | Omit = omit, inside_template: str | Omit = omit, - mailing_class: OrderMailingClass | Omit = omit, + mailing_class: Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ] + | Omit = omit, merge_variables: Optional[Dict[str, object]] | Omit = omit, metadata: Optional[Dict[str, str]] | Omit = omit, outside_template: str | Omit = omit, @@ -364,7 +419,35 @@ async def create( expand: SequenceNotStr[str] | Omit = omit, description: Optional[str] | Omit = omit, inside_template: str | Omit = omit, - mailing_class: OrderMailingClass | Omit = omit, + mailing_class: Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ] + | Omit = omit, merge_variables: Optional[Dict[str, object]] | Omit = omit, metadata: Optional[Dict[str, str]] | Omit = omit, outside_template: str | Omit = omit, @@ -485,7 +568,35 @@ async def update( expand: SequenceNotStr[str] | Omit = omit, description: Optional[str] | Omit = omit, inside_template: str | Omit = omit, - mailing_class: OrderMailingClass | Omit = omit, + mailing_class: Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ] + | Omit = omit, merge_variables: Optional[Dict[str, object]] | Omit = omit, metadata: Optional[Dict[str, str]] | Omit = omit, outside_template: str | Omit = omit, diff --git a/src/postgrid/resources/print_mail/postcards.py b/src/postgrid/resources/print_mail/postcards.py index ba39f7c..8ab504e 100644 --- a/src/postgrid/resources/print_mail/postcards.py +++ b/src/postgrid/resources/print_mail/postcards.py @@ -4,7 +4,7 @@ from typing import Dict, Union from datetime import datetime -from typing_extensions import overload +from typing_extensions import Literal, overload import httpx @@ -20,10 +20,9 @@ ) from ...pagination import SyncSkipLimit, AsyncSkipLimit from ..._base_client import AsyncPaginator, make_request_options -from ...types.print_mail import OrderMailingClass, postcard_list_params, postcard_create_params +from ...types.print_mail import postcard_list_params, postcard_create_params from ...types.print_mail.postcard import Postcard from ...types.print_mail.order_profiles import PostcardSize -from ...types.print_mail.order_mailing_class import OrderMailingClass from ...types.print_mail.order_profiles.postcard_size import PostcardSize from ...types.print_mail.postcard_retrieve_url_response import PostcardRetrieveURLResponse @@ -60,7 +59,35 @@ def create( to: postcard_create_params.PostcardCreateWithHTMLTo, description: str | Omit = omit, from_: postcard_create_params.PostcardCreateWithHTMLFrom | Omit = omit, - mailing_class: OrderMailingClass | Omit = omit, + mailing_class: Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ] + | Omit = omit, merge_variables: Dict[str, object] | Omit = omit, metadata: Dict[str, object] | Omit = omit, send_date: Union[str, datetime] | Omit = omit, @@ -174,7 +201,35 @@ def create( to: postcard_create_params.PostcardCreateWithPdfurlTo, description: str | Omit = omit, from_: postcard_create_params.PostcardCreateWithPdfurlFrom | Omit = omit, - mailing_class: OrderMailingClass | Omit = omit, + mailing_class: Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ] + | Omit = omit, merge_variables: Dict[str, object] | Omit = omit, metadata: Dict[str, object] | Omit = omit, send_date: Union[str, datetime] | Omit = omit, @@ -245,7 +300,35 @@ def create( to: postcard_create_params.PostcardCreateWithPdfFileTo, description: str | Omit = omit, from_: postcard_create_params.PostcardCreateWithPdfFileFrom | Omit = omit, - mailing_class: OrderMailingClass | Omit = omit, + mailing_class: Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ] + | Omit = omit, merge_variables: Dict[str, object] | Omit = omit, metadata: Dict[str, object] | Omit = omit, send_date: Union[str, datetime] | Omit = omit, @@ -325,7 +408,35 @@ def create( | postcard_create_params.PostcardCreateWithPdfurlFrom | postcard_create_params.PostcardCreateWithPdfFileFrom | Omit = omit, - mailing_class: OrderMailingClass | Omit = omit, + mailing_class: Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ] + | Omit = omit, merge_variables: Dict[str, object] | Omit = omit, metadata: Dict[str, object] | Omit = omit, send_date: Union[str, datetime] | Omit = omit, @@ -552,7 +663,35 @@ async def create( to: postcard_create_params.PostcardCreateWithHTMLTo, description: str | Omit = omit, from_: postcard_create_params.PostcardCreateWithHTMLFrom | Omit = omit, - mailing_class: OrderMailingClass | Omit = omit, + mailing_class: Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ] + | Omit = omit, merge_variables: Dict[str, object] | Omit = omit, metadata: Dict[str, object] | Omit = omit, send_date: Union[str, datetime] | Omit = omit, @@ -666,7 +805,35 @@ async def create( to: postcard_create_params.PostcardCreateWithPdfurlTo, description: str | Omit = omit, from_: postcard_create_params.PostcardCreateWithPdfurlFrom | Omit = omit, - mailing_class: OrderMailingClass | Omit = omit, + mailing_class: Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ] + | Omit = omit, merge_variables: Dict[str, object] | Omit = omit, metadata: Dict[str, object] | Omit = omit, send_date: Union[str, datetime] | Omit = omit, @@ -737,7 +904,35 @@ async def create( to: postcard_create_params.PostcardCreateWithPdfFileTo, description: str | Omit = omit, from_: postcard_create_params.PostcardCreateWithPdfFileFrom | Omit = omit, - mailing_class: OrderMailingClass | Omit = omit, + mailing_class: Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ] + | Omit = omit, merge_variables: Dict[str, object] | Omit = omit, metadata: Dict[str, object] | Omit = omit, send_date: Union[str, datetime] | Omit = omit, @@ -817,7 +1012,35 @@ async def create( | postcard_create_params.PostcardCreateWithPdfurlFrom | postcard_create_params.PostcardCreateWithPdfFileFrom | Omit = omit, - mailing_class: OrderMailingClass | Omit = omit, + mailing_class: Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ] + | Omit = omit, merge_variables: Dict[str, object] | Omit = omit, metadata: Dict[str, object] | Omit = omit, send_date: Union[str, datetime] | Omit = omit, diff --git a/src/postgrid/resources/print_mail/print_mail.py b/src/postgrid/resources/print_mail/print_mail.py index b9559ba..7a80993 100644 --- a/src/postgrid/resources/print_mail/print_mail.py +++ b/src/postgrid/resources/print_mail/print_mail.py @@ -2,14 +2,6 @@ from __future__ import annotations -from .boxes import ( - BoxesResource, - AsyncBoxesResource, - BoxesResourceWithRawResponse, - AsyncBoxesResourceWithRawResponse, - BoxesResourceWithStreamingResponse, - AsyncBoxesResourceWithStreamingResponse, -) from .cheques import ( ChequesResource, AsyncChequesResource, @@ -125,10 +117,6 @@ class PrintMailResource(SyncAPIResource): def bank_accounts(self) -> BankAccountsResource: return BankAccountsResource(self._client) - @cached_property - def boxes(self) -> BoxesResource: - return BoxesResource(self._client) - @cached_property def campaigns(self) -> CampaignsResource: return CampaignsResource(self._client) @@ -202,10 +190,6 @@ class AsyncPrintMailResource(AsyncAPIResource): def bank_accounts(self) -> AsyncBankAccountsResource: return AsyncBankAccountsResource(self._client) - @cached_property - def boxes(self) -> AsyncBoxesResource: - return AsyncBoxesResource(self._client) - @cached_property def campaigns(self) -> AsyncCampaignsResource: return AsyncCampaignsResource(self._client) @@ -282,10 +266,6 @@ def __init__(self, print_mail: PrintMailResource) -> None: def bank_accounts(self) -> BankAccountsResourceWithRawResponse: return BankAccountsResourceWithRawResponse(self._print_mail.bank_accounts) - @cached_property - def boxes(self) -> BoxesResourceWithRawResponse: - return BoxesResourceWithRawResponse(self._print_mail.boxes) - @cached_property def campaigns(self) -> CampaignsResourceWithRawResponse: return CampaignsResourceWithRawResponse(self._print_mail.campaigns) @@ -343,10 +323,6 @@ def __init__(self, print_mail: AsyncPrintMailResource) -> None: def bank_accounts(self) -> AsyncBankAccountsResourceWithRawResponse: return AsyncBankAccountsResourceWithRawResponse(self._print_mail.bank_accounts) - @cached_property - def boxes(self) -> AsyncBoxesResourceWithRawResponse: - return AsyncBoxesResourceWithRawResponse(self._print_mail.boxes) - @cached_property def campaigns(self) -> AsyncCampaignsResourceWithRawResponse: return AsyncCampaignsResourceWithRawResponse(self._print_mail.campaigns) @@ -404,10 +380,6 @@ def __init__(self, print_mail: PrintMailResource) -> None: def bank_accounts(self) -> BankAccountsResourceWithStreamingResponse: return BankAccountsResourceWithStreamingResponse(self._print_mail.bank_accounts) - @cached_property - def boxes(self) -> BoxesResourceWithStreamingResponse: - return BoxesResourceWithStreamingResponse(self._print_mail.boxes) - @cached_property def campaigns(self) -> CampaignsResourceWithStreamingResponse: return CampaignsResourceWithStreamingResponse(self._print_mail.campaigns) @@ -465,10 +437,6 @@ def __init__(self, print_mail: AsyncPrintMailResource) -> None: def bank_accounts(self) -> AsyncBankAccountsResourceWithStreamingResponse: return AsyncBankAccountsResourceWithStreamingResponse(self._print_mail.bank_accounts) - @cached_property - def boxes(self) -> AsyncBoxesResourceWithStreamingResponse: - return AsyncBoxesResourceWithStreamingResponse(self._print_mail.boxes) - @cached_property def campaigns(self) -> AsyncCampaignsResourceWithStreamingResponse: return AsyncCampaignsResourceWithStreamingResponse(self._print_mail.campaigns) diff --git a/src/postgrid/resources/print_mail/self_mailers.py b/src/postgrid/resources/print_mail/self_mailers.py index b5924e9..4720fb4 100644 --- a/src/postgrid/resources/print_mail/self_mailers.py +++ b/src/postgrid/resources/print_mail/self_mailers.py @@ -4,7 +4,7 @@ from typing import Dict, Union from datetime import datetime -from typing_extensions import overload +from typing_extensions import Literal, overload import httpx @@ -20,10 +20,9 @@ ) from ...pagination import SyncSkipLimit, AsyncSkipLimit from ..._base_client import AsyncPaginator, make_request_options -from ...types.print_mail import OrderMailingClass, self_mailer_list_params, self_mailer_create_params +from ...types.print_mail import self_mailer_list_params, self_mailer_create_params from ...types.print_mail.self_mailer import SelfMailer from ...types.print_mail.order_profiles import SelfMailerSize -from ...types.print_mail.order_mailing_class import OrderMailingClass from ...types.print_mail.order_profiles.self_mailer_size import SelfMailerSize from ...types.print_mail.self_mailer_retrieve_url_response import SelfMailerRetrieveURLResponse @@ -60,7 +59,35 @@ def create( size: SelfMailerSize, to: self_mailer_create_params.SelfMailerCreateWithHTMLTo, description: str | Omit = omit, - mailing_class: OrderMailingClass | Omit = omit, + mailing_class: Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ] + | Omit = omit, merge_variables: Dict[str, object] | Omit = omit, metadata: Dict[str, object] | Omit = omit, send_date: Union[str, datetime] | Omit = omit, @@ -173,7 +200,35 @@ def create( size: SelfMailerSize, to: self_mailer_create_params.SelfMailerCreateWithPdfurlTo, description: str | Omit = omit, - mailing_class: OrderMailingClass | Omit = omit, + mailing_class: Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ] + | Omit = omit, merge_variables: Dict[str, object] | Omit = omit, metadata: Dict[str, object] | Omit = omit, send_date: Union[str, datetime] | Omit = omit, @@ -244,7 +299,35 @@ def create( size: SelfMailerSize, to: self_mailer_create_params.SelfMailerCreateWithPdfFileTo, description: str | Omit = omit, - mailing_class: OrderMailingClass | Omit = omit, + mailing_class: Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ] + | Omit = omit, merge_variables: Dict[str, object] | Omit = omit, metadata: Dict[str, object] | Omit = omit, send_date: Union[str, datetime] | Omit = omit, @@ -325,7 +408,35 @@ def create( | self_mailer_create_params.SelfMailerCreateWithPdfFileTo | Omit = omit, description: str | Omit = omit, - mailing_class: OrderMailingClass | Omit = omit, + mailing_class: Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ] + | Omit = omit, merge_variables: Dict[str, object] | Omit = omit, metadata: Dict[str, object] | Omit = omit, send_date: Union[str, datetime] | Omit = omit, @@ -552,7 +663,35 @@ async def create( size: SelfMailerSize, to: self_mailer_create_params.SelfMailerCreateWithHTMLTo, description: str | Omit = omit, - mailing_class: OrderMailingClass | Omit = omit, + mailing_class: Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ] + | Omit = omit, merge_variables: Dict[str, object] | Omit = omit, metadata: Dict[str, object] | Omit = omit, send_date: Union[str, datetime] | Omit = omit, @@ -665,7 +804,35 @@ async def create( size: SelfMailerSize, to: self_mailer_create_params.SelfMailerCreateWithPdfurlTo, description: str | Omit = omit, - mailing_class: OrderMailingClass | Omit = omit, + mailing_class: Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ] + | Omit = omit, merge_variables: Dict[str, object] | Omit = omit, metadata: Dict[str, object] | Omit = omit, send_date: Union[str, datetime] | Omit = omit, @@ -736,7 +903,35 @@ async def create( size: SelfMailerSize, to: self_mailer_create_params.SelfMailerCreateWithPdfFileTo, description: str | Omit = omit, - mailing_class: OrderMailingClass | Omit = omit, + mailing_class: Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ] + | Omit = omit, merge_variables: Dict[str, object] | Omit = omit, metadata: Dict[str, object] | Omit = omit, send_date: Union[str, datetime] | Omit = omit, @@ -817,7 +1012,35 @@ async def create( | self_mailer_create_params.SelfMailerCreateWithPdfFileTo | Omit = omit, description: str | Omit = omit, - mailing_class: OrderMailingClass | Omit = omit, + mailing_class: Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ] + | Omit = omit, merge_variables: Dict[str, object] | Omit = omit, metadata: Dict[str, object] | Omit = omit, send_date: Union[str, datetime] | Omit = omit, diff --git a/src/postgrid/types/print_mail/__init__.py b/src/postgrid/types/print_mail/__init__.py index 0527402..2e35113 100644 --- a/src/postgrid/types/print_mail/__init__.py +++ b/src/postgrid/types/print_mail/__init__.py @@ -2,7 +2,6 @@ from __future__ import annotations -from .box import Box as Box from .cheque import Cheque as Cheque from .letter import Letter as Letter from .report import Report as Report @@ -16,18 +15,12 @@ from .self_mailer import SelfMailer as SelfMailer from .attached_pdf import AttachedPdf as AttachedPdf from .bank_account import BankAccount as BankAccount -from .cancellation import Cancellation as Cancellation from .digital_only import DigitalOnly as DigitalOnly from .mailing_list import MailingList as MailingList -from .order_status import OrderStatus as OrderStatus from .plastic_card import PlasticCard as PlasticCard -from .box_cheque_base import BoxChequeBase as BoxChequeBase -from .box_list_params import BoxListParams as BoxListParams from .deleted_response import DeletedResponse as DeletedResponse -from .order_imb_status import OrderImbStatus as OrderImbStatus from .sub_organization import SubOrganization as SubOrganization from .address_placement import AddressPlacement as AddressPlacement -from .box_create_params import BoxCreateParams as BoxCreateParams from .email_preferences import EmailPreferences as EmailPreferences from .attached_pdf_param import AttachedPdfParam as AttachedPdfParam from .cheque_list_params import ChequeListParams as ChequeListParams @@ -37,7 +30,6 @@ from .report_list_params import ReportListParams as ReportListParams from .contact_list_params import ContactListParams as ContactListParams from .mailing_list_update import MailingListUpdate as MailingListUpdate -from .order_mailing_class import OrderMailingClass as OrderMailingClass from .campaign_list_params import CampaignListParams as CampaignListParams from .campaign_send_params import CampaignSendParams as CampaignSendParams from .cheque_create_params import ChequeCreateParams as ChequeCreateParams @@ -48,7 +40,6 @@ from .report_sample_params import ReportSampleParams as ReportSampleParams from .report_update_params import ReportUpdateParams as ReportUpdateParams from .template_list_params import TemplateListParams as TemplateListParams -from .box_cheque_base_param import BoxChequeBaseParam as BoxChequeBaseParam from .contact_create_params import ContactCreateParams as ContactCreateParams from .campaign_create_params import CampaignCreateParams as CampaignCreateParams from .campaign_update_params import CampaignUpdateParams as CampaignUpdateParams diff --git a/src/postgrid/types/print_mail/box.py b/src/postgrid/types/print_mail/box.py deleted file mode 100644 index 1086351..0000000 --- a/src/postgrid/types/print_mail/box.py +++ /dev/null @@ -1,134 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import builtins -from typing import Dict, List, Optional -from datetime import datetime -from typing_extensions import Literal - -from pydantic import Field as FieldInfo - -from .contact import Contact -from ..._models import BaseModel -from .cancellation import Cancellation -from .order_status import OrderStatus -from .box_cheque_base import BoxChequeBase -from .order_imb_status import OrderImbStatus -from .order_mailing_class import OrderMailingClass - -__all__ = ["Box", "Cheque"] - - -class Cheque(BoxChequeBase): - from_: Contact = FieldInfo(alias="from") - - to: Contact - - -class Box(BaseModel): - id: str - """A unique ID prefixed with box\\__""" - - cheques: List[Cheque] - """The cheques inside this box (in read mode).""" - - created_at: datetime = FieldInfo(alias="createdAt") - """The UTC time at which this resource was created.""" - - from_: Contact = FieldInfo(alias="from") - """ - The contact of the 'from' field in read mode should be a fully expanded Contact. - """ - - live: bool - """`true` if this is a live mode resource else `false`.""" - - mailing_class: OrderMailingClass = FieldInfo(alias="mailingClass") - """The mailing class of this order. - - This determines the speed and cost of delivery. See `OrderMailingClass` for more - details. - """ - - object: Literal["box"] - """Always "box".""" - - send_date: datetime = FieldInfo(alias="sendDate") - """This order will transition from `ready` to `printing` on the day after this - date. - - For example, if this is a date on Tuesday, the order will transition to - `printing` on Wednesday at midnight eastern time. - """ - - status: OrderStatus - """See `OrderStatus` for more details on the status of this order.""" - - to: Contact - """The recipient of this order. - - This will be provided even if you delete the underlying contact. - """ - - updated_at: datetime = FieldInfo(alias="updatedAt") - """The UTC time at which this resource was last updated.""" - - cancellation: Optional[Cancellation] = None - """The cancellation details of this order. - - Populated if the order has been cancelled. - """ - - description: Optional[str] = None - """An optional string describing this resource. - - Will be visible in the API and the dashboard. - """ - - imb_date: Optional[datetime] = FieldInfo(alias="imbDate", default=None) - """The last date that the IMB status was updated. - - See `imbStatus` for more details. - """ - - imb_status: Optional[OrderImbStatus] = FieldInfo(alias="imbStatus", default=None) - """The Intelligent Mail Barcode (IMB) status of this order. - - Only populated for US-printed and US-destined orders. This is the most detailed - way to track non-express/certified orders. - """ - - imb_zip_code: Optional[str] = FieldInfo(alias="imbZIPCode", default=None) - """ - The most recent ZIP code of the USPS facility that the order has been processed - through. Only populated when an `imbStatus` is present. - """ - - merge_variables: Optional[Dict[str, builtins.object]] = FieldInfo(alias="mergeVariables", default=None) - """ - These will be merged with the variables in the template or HTML you create this - order with. The keys in this object should match the variable names in the - template _exactly_ as they are case-sensitive. Note that these _do not_ apply to - PDFs uploaded with the order. - """ - - metadata: Optional[Dict[str, builtins.object]] = None - """See the section on Metadata.""" - - tracking_number: Optional[str] = FieldInfo(alias="trackingNumber", default=None) - """The tracking number of this order. - - Populated after an express/certified order has been processed for delivery. - """ - - url: Optional[str] = None - """PostGrid renders a PDF preview for all orders. - - This should be inspected to ensure that the order is correct before it is sent - out because it shows what will be printed and mailed to the recipient. Once the - PDF preview is generated, this field will be returned by all `GET` endpoints - which produce this order. - - This URL is a signed link to the PDF preview. It will expire after a short - period of time. If you need to access this URL after it has expired, you can - regenerate it by calling the `GET` endpoint again. - """ diff --git a/src/postgrid/types/print_mail/box_cheque_base.py b/src/postgrid/types/print_mail/box_cheque_base.py deleted file mode 100644 index c21aeb5..0000000 --- a/src/postgrid/types/print_mail/box_cheque_base.py +++ /dev/null @@ -1,35 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Dict, Optional - -from pydantic import Field as FieldInfo - -from ..._models import BaseModel - -__all__ = ["BoxChequeBase"] - - -class BoxChequeBase(BaseModel): - amount: int - """The amount on the cheque.""" - - bank_account: str = FieldInfo(alias="bankAccount") - """The bank account (ID or reference) from which the cheque amount is drawn.""" - - number: int - """The cheque number.""" - - logo_url: Optional[str] = FieldInfo(alias="logoURL", default=None) - """A URL to a logo for the cheque (optional).""" - - memo: Optional[str] = None - """The memo text on the cheque (optional).""" - - merge_variables: Optional[Dict[str, object]] = FieldInfo(alias="mergeVariables", default=None) - """ - A set of dynamic merge variables for customizing the cheque or accompanying - documents (optional). - """ - - message_template: Optional[str] = FieldInfo(alias="messageTemplate", default=None) - """An optional message template to be printed on or with the cheque.""" diff --git a/src/postgrid/types/print_mail/box_cheque_base_param.py b/src/postgrid/types/print_mail/box_cheque_base_param.py deleted file mode 100644 index 6ff0df6..0000000 --- a/src/postgrid/types/print_mail/box_cheque_base_param.py +++ /dev/null @@ -1,36 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict -from typing_extensions import Required, Annotated, TypedDict - -from ..._utils import PropertyInfo - -__all__ = ["BoxChequeBaseParam"] - - -class BoxChequeBaseParam(TypedDict, total=False): - amount: Required[int] - """The amount on the cheque.""" - - bank_account: Required[Annotated[str, PropertyInfo(alias="bankAccount")]] - """The bank account (ID or reference) from which the cheque amount is drawn.""" - - number: Required[int] - """The cheque number.""" - - logo_url: Annotated[str, PropertyInfo(alias="logoURL")] - """A URL to a logo for the cheque (optional).""" - - memo: str - """The memo text on the cheque (optional).""" - - merge_variables: Annotated[Dict[str, object], PropertyInfo(alias="mergeVariables")] - """ - A set of dynamic merge variables for customizing the cheque or accompanying - documents (optional). - """ - - message_template: Annotated[str, PropertyInfo(alias="messageTemplate")] - """An optional message template to be printed on or with the cheque.""" diff --git a/src/postgrid/types/print_mail/box_create_params.py b/src/postgrid/types/print_mail/box_create_params.py deleted file mode 100644 index d3d914a..0000000 --- a/src/postgrid/types/print_mail/box_create_params.py +++ /dev/null @@ -1,81 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict, Union, Iterable -from datetime import datetime -from typing_extensions import Required, Annotated, TypeAlias, TypedDict - -from ..._utils import PropertyInfo -from .order_mailing_class import OrderMailingClass -from .box_cheque_base_param import BoxChequeBaseParam -from ..contact_create_with_first_name_param import ContactCreateWithFirstNameParam -from ..contact_create_with_company_name_param import ContactCreateWithCompanyNameParam - -__all__ = ["BoxCreateParams", "Cheque", "ChequeFrom", "ChequeTo", "From", "To"] - - -class BoxCreateParams(TypedDict, total=False): - cheques: Required[Iterable[Cheque]] - """The cheques to be mailed in the box. - - Only 100 cheques can be included in a box at a time. - """ - - from_: Required[Annotated[From, PropertyInfo(alias="from")]] - """The 'from' (sender) of the entire box. - - Accepts inline ContactCreate or a contactID. - """ - - to: Required[To] - """The recipient of this order. - - You can either supply the contact information inline here or provide a contact - ID. PostGrid will automatically deduplicate contacts regardless of whether you - provide the information inline here or call the contact creation endpoint. - """ - - description: str - """An optional string describing this resource. - - Will be visible in the API and the dashboard. - """ - - mailing_class: Annotated[OrderMailingClass, PropertyInfo(alias="mailingClass")] - """The mailing class of this order. - - If not provided, automatically set to `first_class`. - """ - - merge_variables: Annotated[Dict[str, object], PropertyInfo(alias="mergeVariables")] - """ - These will be merged with the variables in the template or HTML you create this - order with. The keys in this object should match the variable names in the - template _exactly_ as they are case-sensitive. Note that these _do not_ apply to - PDFs uploaded with the order. - """ - - metadata: Dict[str, object] - """See the section on Metadata.""" - - send_date: Annotated[Union[str, datetime], PropertyInfo(alias="sendDate", format="iso8601")] - """This order will transition from `ready` to `printing` on the day after this - date. - - You can use this parameter to schedule orders for a future date. - """ - - -ChequeFrom: TypeAlias = Union[ContactCreateWithFirstNameParam, ContactCreateWithCompanyNameParam, str] - -ChequeTo: TypeAlias = Union[ContactCreateWithFirstNameParam, ContactCreateWithCompanyNameParam, str] - - -class Cheque(BoxChequeBaseParam, total=False): - to: Required[ChequeTo] - - -From: TypeAlias = Union[ContactCreateWithFirstNameParam, ContactCreateWithCompanyNameParam, str] - -To: TypeAlias = Union[ContactCreateWithFirstNameParam, ContactCreateWithCompanyNameParam, str] diff --git a/src/postgrid/types/print_mail/box_list_params.py b/src/postgrid/types/print_mail/box_list_params.py deleted file mode 100644 index 9832ccc..0000000 --- a/src/postgrid/types/print_mail/box_list_params.py +++ /dev/null @@ -1,22 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import TypedDict - -__all__ = ["BoxListParams"] - - -class BoxListParams(TypedDict, total=False): - limit: int - - search: str - """You can supply any string to help narrow down the list of resources. - - For example, if you pass `"New York"` (quoted), it will return resources that - have that string present somewhere in their response. Alternatively, you can - supply a structured search query. See the documentation on - `StructuredSearchQuery` for more details. - """ - - skip: int diff --git a/src/postgrid/types/print_mail/cancellation.py b/src/postgrid/types/print_mail/cancellation.py deleted file mode 100644 index 9e54979..0000000 --- a/src/postgrid/types/print_mail/cancellation.py +++ /dev/null @@ -1,21 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from typing_extensions import Literal - -from pydantic import Field as FieldInfo - -from ..._models import BaseModel - -__all__ = ["Cancellation"] - - -class Cancellation(BaseModel): - reason: Literal["user_initiated", "invalid_content", "invalid_order_mailing_class"] - """The reason for the cancellation.""" - - cancelled_by_user: Optional[str] = FieldInfo(alias="cancelledByUser", default=None) - """The user ID who cancelled the order.""" - - note: Optional[str] = None - """An optional note provided by the user who cancelled the order.""" diff --git a/src/postgrid/types/print_mail/cheque.py b/src/postgrid/types/print_mail/cheque.py index c2ea012..01f4bcd 100644 --- a/src/postgrid/types/print_mail/cheque.py +++ b/src/postgrid/types/print_mail/cheque.py @@ -10,13 +10,20 @@ from .contact import Contact from ..._models import BaseModel from .cheque_size import ChequeSize -from .cancellation import Cancellation from .digital_only import DigitalOnly -from .order_status import OrderStatus -from .order_imb_status import OrderImbStatus -from .order_mailing_class import OrderMailingClass -__all__ = ["Cheque"] +__all__ = ["Cheque", "Cancellation"] + + +class Cancellation(BaseModel): + reason: Literal["user_initiated", "invalid_content", "invalid_order_mailing_class"] + """The reason for the cancellation.""" + + cancelled_by_user: Optional[str] = FieldInfo(alias="cancelledByUser", default=None) + """The user ID who cancelled the order.""" + + note: Optional[str] = None + """An optional note provided by the user who cancelled the order.""" class Cheque(BaseModel): @@ -45,7 +52,34 @@ class Cheque(BaseModel): live: bool """`true` if this is a live mode resource else `false`.""" - mailing_class: OrderMailingClass = FieldInfo(alias="mailingClass") + mailing_class: Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ] = FieldInfo(alias="mailingClass") """The mailing class of this order. This determines the speed and cost of delivery. See `OrderMailingClass` for more @@ -66,7 +100,7 @@ class Cheque(BaseModel): size: ChequeSize """Enum representing the supported cheque sizes.""" - status: OrderStatus + status: Literal["ready", "printing", "processed_for_delivery", "completed", "cancelled"] """See `OrderStatus` for more details on the status of this order.""" to: Contact @@ -114,7 +148,9 @@ class Cheque(BaseModel): See `imbStatus` for more details. """ - imb_status: Optional[OrderImbStatus] = FieldInfo(alias="imbStatus", default=None) + imb_status: Optional[Literal["entered_mail_stream", "out_for_delivery", "returned_to_sender"]] = FieldInfo( + alias="imbStatus", default=None + ) """The Intelligent Mail Barcode (IMB) status of this order. Only populated for US-printed and US-destined orders. This is the most detailed diff --git a/src/postgrid/types/print_mail/cheque_create_params.py b/src/postgrid/types/print_mail/cheque_create_params.py index 68d6710..0bde982 100644 --- a/src/postgrid/types/print_mail/cheque_create_params.py +++ b/src/postgrid/types/print_mail/cheque_create_params.py @@ -9,7 +9,6 @@ from ..._utils import PropertyInfo from .cheque_size import ChequeSize from .digital_only_param import DigitalOnlyParam -from .order_mailing_class import OrderMailingClass from ..contact_create_with_first_name_param import ContactCreateWithFirstNameParam from ..contact_create_with_company_name_param import ContactCreateWithCompanyNameParam @@ -72,7 +71,37 @@ class ChequeCreateParams(TypedDict, total=False): file). """ - mailing_class: Annotated[OrderMailingClass, PropertyInfo(alias="mailingClass")] + mailing_class: Annotated[ + Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ], + PropertyInfo(alias="mailingClass"), + ] """The mailing class of this order. If not provided, automatically set to `first_class`. diff --git a/src/postgrid/types/print_mail/letter.py b/src/postgrid/types/print_mail/letter.py index 20d5799..3d8daf6 100644 --- a/src/postgrid/types/print_mail/letter.py +++ b/src/postgrid/types/print_mail/letter.py @@ -11,14 +11,21 @@ from ..._models import BaseModel from .letter_size import LetterSize from .attached_pdf import AttachedPdf -from .cancellation import Cancellation -from .order_status import OrderStatus from .plastic_card import PlasticCard -from .order_imb_status import OrderImbStatus from .address_placement import AddressPlacement -from .order_mailing_class import OrderMailingClass -__all__ = ["Letter"] +__all__ = ["Letter", "Cancellation"] + + +class Cancellation(BaseModel): + reason: Literal["user_initiated", "invalid_content", "invalid_order_mailing_class"] + """The reason for the cancellation.""" + + cancelled_by_user: Optional[str] = FieldInfo(alias="cancelledByUser", default=None) + """The user ID who cancelled the order.""" + + note: Optional[str] = None + """An optional note provided by the user who cancelled the order.""" class Letter(BaseModel): @@ -46,7 +53,34 @@ class Letter(BaseModel): live: bool """`true` if this is a live mode resource else `false`.""" - mailing_class: OrderMailingClass = FieldInfo(alias="mailingClass") + mailing_class: Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ] = FieldInfo(alias="mailingClass") """The mailing class of this order. This determines the speed and cost of delivery. See `OrderMailingClass` for more @@ -67,7 +101,7 @@ class Letter(BaseModel): size: LetterSize """Enum representing the supported letter sizes.""" - status: OrderStatus + status: Literal["ready", "printing", "processed_for_delivery", "completed", "cancelled"] """See `OrderStatus` for more details on the status of this order.""" to: Contact @@ -106,7 +140,9 @@ class Letter(BaseModel): See `imbStatus` for more details. """ - imb_status: Optional[OrderImbStatus] = FieldInfo(alias="imbStatus", default=None) + imb_status: Optional[Literal["entered_mail_stream", "out_for_delivery", "returned_to_sender"]] = FieldInfo( + alias="imbStatus", default=None + ) """The Intelligent Mail Barcode (IMB) status of this order. Only populated for US-printed and US-destined orders. This is the most detailed diff --git a/src/postgrid/types/print_mail/letter_create_params.py b/src/postgrid/types/print_mail/letter_create_params.py index a07d87e..cce3399 100644 --- a/src/postgrid/types/print_mail/letter_create_params.py +++ b/src/postgrid/types/print_mail/letter_create_params.py @@ -11,7 +11,6 @@ from .address_placement import AddressPlacement from .attached_pdf_param import AttachedPdfParam from .plastic_card_param import PlasticCardParam -from .order_mailing_class import OrderMailingClass from ..contact_create_with_first_name_param import ContactCreateWithFirstNameParam from ..contact_create_with_company_name_param import ContactCreateWithCompanyNameParam @@ -73,7 +72,37 @@ class LetterCreateWithHTML(TypedDict, total=False): envelope. """ - mailing_class: Annotated[OrderMailingClass, PropertyInfo(alias="mailingClass")] + mailing_class: Annotated[ + Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ], + PropertyInfo(alias="mailingClass"), + ] """The mailing class of this order. If not provided, automatically set to `first_class`. @@ -169,7 +198,37 @@ class LetterCreateWithPdf(TypedDict, total=False): envelope. """ - mailing_class: Annotated[OrderMailingClass, PropertyInfo(alias="mailingClass")] + mailing_class: Annotated[ + Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ], + PropertyInfo(alias="mailingClass"), + ] """The mailing class of this order. If not provided, automatically set to `first_class`. diff --git a/src/postgrid/types/print_mail/order_imb_status.py b/src/postgrid/types/print_mail/order_imb_status.py deleted file mode 100644 index 7bfc340..0000000 --- a/src/postgrid/types/print_mail/order_imb_status.py +++ /dev/null @@ -1,7 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal, TypeAlias - -__all__ = ["OrderImbStatus"] - -OrderImbStatus: TypeAlias = Literal["entered_mail_stream", "out_for_delivery", "returned_to_sender"] diff --git a/src/postgrid/types/print_mail/order_mailing_class.py b/src/postgrid/types/print_mail/order_mailing_class.py deleted file mode 100644 index 3ef97a4..0000000 --- a/src/postgrid/types/print_mail/order_mailing_class.py +++ /dev/null @@ -1,34 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal, TypeAlias - -__all__ = ["OrderMailingClass"] - -OrderMailingClass: TypeAlias = Literal[ - "first_class", - "standard_class", - "express", - "certified", - "certified_return_receipt", - "registered", - "usps_first_class", - "usps_standard_class", - "usps_eddm", - "usps_express_2_day", - "usps_express_3_day", - "usps_first_class_certified", - "usps_first_class_certified_return_receipt", - "usps_first_class_registered", - "usps_express_3_day_signature_confirmation", - "usps_express_3_day_certified", - "usps_express_3_day_certified_return_receipt", - "ca_post_lettermail", - "ca_post_personalized", - "ca_post_neighbourhood_mail", - "ups_express_overnight", - "ups_express_2_day", - "ups_express_3_day", - "royal_mail_first_class", - "royal_mail_second_class", - "au_post_second_class", -] diff --git a/src/postgrid/types/print_mail/order_profiles/cheque_create_params.py b/src/postgrid/types/print_mail/order_profiles/cheque_create_params.py index 0d0ac3f..294abf7 100644 --- a/src/postgrid/types/print_mail/order_profiles/cheque_create_params.py +++ b/src/postgrid/types/print_mail/order_profiles/cheque_create_params.py @@ -3,13 +3,12 @@ from __future__ import annotations from typing import Dict, Union, Optional -from typing_extensions import Required, Annotated, TypedDict +from typing_extensions import Literal, Required, Annotated, TypedDict from ...._types import SequenceNotStr, Base64FileInput from ...._utils import PropertyInfo from ..cheque_size import ChequeSize from .currency_code import CurrencyCode -from ..order_mailing_class import OrderMailingClass __all__ = ["ChequeCreateParams"] @@ -48,7 +47,37 @@ class ChequeCreateParams(TypedDict, total=False): Set to `null` to remove during update. """ - mailing_class: Annotated[OrderMailingClass, PropertyInfo(alias="mailingClass")] + mailing_class: Annotated[ + Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ], + PropertyInfo(alias="mailingClass"), + ] """Mailing class. Generally must be first class (or equivalent for destination country) for diff --git a/src/postgrid/types/print_mail/order_profiles/cheque_list_response.py b/src/postgrid/types/print_mail/order_profiles/cheque_list_response.py index 538a449..9da8ec1 100644 --- a/src/postgrid/types/print_mail/order_profiles/cheque_list_response.py +++ b/src/postgrid/types/print_mail/order_profiles/cheque_list_response.py @@ -9,7 +9,6 @@ from ...._models import BaseModel from ..cheque_size import ChequeSize -from ..order_mailing_class import OrderMailingClass __all__ = ["ChequeListResponse"] @@ -54,7 +53,36 @@ class ChequeListResponse(BaseModel): Set to `null` to remove during update. """ - mailing_class: Optional[OrderMailingClass] = FieldInfo(alias="mailingClass", default=None) + mailing_class: Optional[ + Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ] + ] = FieldInfo(alias="mailingClass", default=None) """Mailing class. Generally must be first class (or equivalent for destination country) for diff --git a/src/postgrid/types/print_mail/order_profiles/cheque_profile.py b/src/postgrid/types/print_mail/order_profiles/cheque_profile.py index 7b5f889..b354056 100644 --- a/src/postgrid/types/print_mail/order_profiles/cheque_profile.py +++ b/src/postgrid/types/print_mail/order_profiles/cheque_profile.py @@ -10,7 +10,6 @@ from ...._models import BaseModel from ..cheque_size import ChequeSize from .currency_code import CurrencyCode -from ..order_mailing_class import OrderMailingClass __all__ = ["ChequeProfile"] @@ -58,7 +57,36 @@ class ChequeProfile(BaseModel): Set to `null` to remove during update. """ - mailing_class: Optional[OrderMailingClass] = FieldInfo(alias="mailingClass", default=None) + mailing_class: Optional[ + Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ] + ] = FieldInfo(alias="mailingClass", default=None) """Mailing class. Generally must be first class (or equivalent for destination country) for diff --git a/src/postgrid/types/print_mail/order_profiles/cheque_update_params.py b/src/postgrid/types/print_mail/order_profiles/cheque_update_params.py index 1793013..8c3aeaf 100644 --- a/src/postgrid/types/print_mail/order_profiles/cheque_update_params.py +++ b/src/postgrid/types/print_mail/order_profiles/cheque_update_params.py @@ -3,13 +3,12 @@ from __future__ import annotations from typing import Dict, Union, Optional -from typing_extensions import Required, Annotated, TypedDict +from typing_extensions import Literal, Required, Annotated, TypedDict from ...._types import SequenceNotStr, Base64FileInput from ...._utils import PropertyInfo from ..cheque_size import ChequeSize from .currency_code import CurrencyCode -from ..order_mailing_class import OrderMailingClass __all__ = ["ChequeUpdateParams"] @@ -48,7 +47,37 @@ class ChequeUpdateParams(TypedDict, total=False): Set to `null` to remove during update. """ - mailing_class: Annotated[OrderMailingClass, PropertyInfo(alias="mailingClass")] + mailing_class: Annotated[ + Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ], + PropertyInfo(alias="mailingClass"), + ] """Mailing class. Generally must be first class (or equivalent for destination country) for diff --git a/src/postgrid/types/print_mail/order_profiles/letter_create_params.py b/src/postgrid/types/print_mail/order_profiles/letter_create_params.py index 56ddeb8..0ccffab 100644 --- a/src/postgrid/types/print_mail/order_profiles/letter_create_params.py +++ b/src/postgrid/types/print_mail/order_profiles/letter_create_params.py @@ -10,7 +10,6 @@ from ..letter_size import LetterSize from ..address_placement import AddressPlacement from ..attached_pdf_param import AttachedPdfParam -from ..order_mailing_class import OrderMailingClass __all__ = ["LetterCreateParams"] @@ -40,7 +39,37 @@ class LetterCreateParams(TypedDict, total=False): envelope: str """ID of a custom envelope to use.""" - mailing_class: Annotated[OrderMailingClass, PropertyInfo(alias="mailingClass")] + mailing_class: Annotated[ + Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ], + PropertyInfo(alias="mailingClass"), + ] """Mailing class.""" merge_variables: Annotated[Optional[Dict[str, object]], PropertyInfo(alias="mergeVariables")] diff --git a/src/postgrid/types/print_mail/order_profiles/letter_profile.py b/src/postgrid/types/print_mail/order_profiles/letter_profile.py index dc7555e..fd9d80e 100644 --- a/src/postgrid/types/print_mail/order_profiles/letter_profile.py +++ b/src/postgrid/types/print_mail/order_profiles/letter_profile.py @@ -11,7 +11,6 @@ from ..letter_size import LetterSize from ..attached_pdf import AttachedPdf from ..address_placement import AddressPlacement -from ..order_mailing_class import OrderMailingClass __all__ = ["LetterProfile"] @@ -53,7 +52,36 @@ class LetterProfile(BaseModel): envelope: Optional[str] = None """ID of a custom envelope to use.""" - mailing_class: Optional[OrderMailingClass] = FieldInfo(alias="mailingClass", default=None) + mailing_class: Optional[ + Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ] + ] = FieldInfo(alias="mailingClass", default=None) """Mailing class.""" merge_variables: Optional[Dict[str, builtins.object]] = FieldInfo(alias="mergeVariables", default=None) diff --git a/src/postgrid/types/print_mail/order_profiles/letter_update_params.py b/src/postgrid/types/print_mail/order_profiles/letter_update_params.py index 46cc510..84eb61c 100644 --- a/src/postgrid/types/print_mail/order_profiles/letter_update_params.py +++ b/src/postgrid/types/print_mail/order_profiles/letter_update_params.py @@ -9,7 +9,6 @@ from ...._utils import PropertyInfo from ..address_placement import AddressPlacement from ..attached_pdf_param import AttachedPdfParam -from ..order_mailing_class import OrderMailingClass __all__ = ["LetterUpdateParams"] @@ -36,7 +35,37 @@ class LetterUpdateParams(TypedDict, total=False): envelope: str """ID of a custom envelope to use.""" - mailing_class: Annotated[OrderMailingClass, PropertyInfo(alias="mailingClass")] + mailing_class: Annotated[ + Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ], + PropertyInfo(alias="mailingClass"), + ] """Mailing class.""" merge_variables: Annotated[Optional[Dict[str, object]], PropertyInfo(alias="mergeVariables")] diff --git a/src/postgrid/types/print_mail/order_profiles/postcard_create_params.py b/src/postgrid/types/print_mail/order_profiles/postcard_create_params.py index 91e1c06..37359d0 100644 --- a/src/postgrid/types/print_mail/order_profiles/postcard_create_params.py +++ b/src/postgrid/types/print_mail/order_profiles/postcard_create_params.py @@ -3,12 +3,11 @@ from __future__ import annotations from typing import Dict, Optional -from typing_extensions import Required, Annotated, TypedDict +from typing_extensions import Literal, Required, Annotated, TypedDict from ...._types import SequenceNotStr from ...._utils import PropertyInfo from .postcard_size import PostcardSize -from ..order_mailing_class import OrderMailingClass __all__ = ["PostcardCreateParams"] @@ -29,7 +28,37 @@ class PostcardCreateParams(TypedDict, total=False): front_template: Annotated[str, PropertyInfo(alias="frontTemplate")] """ID of the template for the front side. Required unless `pdf` is provided.""" - mailing_class: Annotated[OrderMailingClass, PropertyInfo(alias="mailingClass")] + mailing_class: Annotated[ + Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ], + PropertyInfo(alias="mailingClass"), + ] """ Mailing class (cannot include extra services like `certified` or `registered` for postcards, though). diff --git a/src/postgrid/types/print_mail/order_profiles/postcard_profile.py b/src/postgrid/types/print_mail/order_profiles/postcard_profile.py index b1fbef4..9c25e57 100644 --- a/src/postgrid/types/print_mail/order_profiles/postcard_profile.py +++ b/src/postgrid/types/print_mail/order_profiles/postcard_profile.py @@ -9,7 +9,6 @@ from ...._models import BaseModel from .postcard_size import PostcardSize -from ..order_mailing_class import OrderMailingClass __all__ = ["PostcardProfile"] @@ -42,7 +41,36 @@ class PostcardProfile(BaseModel): front_template: Optional[str] = FieldInfo(alias="frontTemplate", default=None) """ID of the template for the front side. Required unless `pdf` is provided.""" - mailing_class: Optional[OrderMailingClass] = FieldInfo(alias="mailingClass", default=None) + mailing_class: Optional[ + Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ] + ] = FieldInfo(alias="mailingClass", default=None) """ Mailing class (cannot include extra services like `certified` or `registered` for postcards, though). diff --git a/src/postgrid/types/print_mail/order_profiles/postcard_update_params.py b/src/postgrid/types/print_mail/order_profiles/postcard_update_params.py index 32fc824..cf58687 100644 --- a/src/postgrid/types/print_mail/order_profiles/postcard_update_params.py +++ b/src/postgrid/types/print_mail/order_profiles/postcard_update_params.py @@ -3,11 +3,10 @@ from __future__ import annotations from typing import Dict, Optional -from typing_extensions import Annotated, TypedDict +from typing_extensions import Literal, Annotated, TypedDict from ...._types import SequenceNotStr from ...._utils import PropertyInfo -from ..order_mailing_class import OrderMailingClass __all__ = ["PostcardUpdateParams"] @@ -25,7 +24,37 @@ class PostcardUpdateParams(TypedDict, total=False): front_template: Annotated[str, PropertyInfo(alias="frontTemplate")] """ID of the template for the front side. Required unless `pdf` is provided.""" - mailing_class: Annotated[OrderMailingClass, PropertyInfo(alias="mailingClass")] + mailing_class: Annotated[ + Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ], + PropertyInfo(alias="mailingClass"), + ] """ Mailing class (cannot include extra services like `certified` or `registered` for postcards, though). diff --git a/src/postgrid/types/print_mail/order_profiles/self_mailer_create_params.py b/src/postgrid/types/print_mail/order_profiles/self_mailer_create_params.py index f979fb8..37984be 100644 --- a/src/postgrid/types/print_mail/order_profiles/self_mailer_create_params.py +++ b/src/postgrid/types/print_mail/order_profiles/self_mailer_create_params.py @@ -3,12 +3,11 @@ from __future__ import annotations from typing import Dict, Optional -from typing_extensions import Required, Annotated, TypedDict +from typing_extensions import Literal, Required, Annotated, TypedDict from ...._types import SequenceNotStr from ...._utils import PropertyInfo from .self_mailer_size import SelfMailerSize -from ..order_mailing_class import OrderMailingClass __all__ = ["SelfMailerCreateParams"] @@ -26,7 +25,37 @@ class SelfMailerCreateParams(TypedDict, total=False): inside_template: Annotated[str, PropertyInfo(alias="insideTemplate")] """ID of the template for the inside. Required unless `pdf` is provided.""" - mailing_class: Annotated[OrderMailingClass, PropertyInfo(alias="mailingClass")] + mailing_class: Annotated[ + Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ], + PropertyInfo(alias="mailingClass"), + ] """Mailing class (cannot include extra services for self-mailers).""" merge_variables: Annotated[Optional[Dict[str, object]], PropertyInfo(alias="mergeVariables")] diff --git a/src/postgrid/types/print_mail/order_profiles/self_mailer_profile.py b/src/postgrid/types/print_mail/order_profiles/self_mailer_profile.py index 08ee044..4656ce8 100644 --- a/src/postgrid/types/print_mail/order_profiles/self_mailer_profile.py +++ b/src/postgrid/types/print_mail/order_profiles/self_mailer_profile.py @@ -9,7 +9,6 @@ from ...._models import BaseModel from .self_mailer_size import SelfMailerSize -from ..order_mailing_class import OrderMailingClass __all__ = ["SelfMailerProfile"] @@ -39,7 +38,36 @@ class SelfMailerProfile(BaseModel): inside_template: Optional[str] = FieldInfo(alias="insideTemplate", default=None) """ID of the template for the inside. Required unless `pdf` is provided.""" - mailing_class: Optional[OrderMailingClass] = FieldInfo(alias="mailingClass", default=None) + mailing_class: Optional[ + Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ] + ] = FieldInfo(alias="mailingClass", default=None) """Mailing class (cannot include extra services for self-mailers).""" merge_variables: Optional[Dict[str, builtins.object]] = FieldInfo(alias="mergeVariables", default=None) diff --git a/src/postgrid/types/print_mail/order_profiles/self_mailer_update_params.py b/src/postgrid/types/print_mail/order_profiles/self_mailer_update_params.py index f617a22..b51045e 100644 --- a/src/postgrid/types/print_mail/order_profiles/self_mailer_update_params.py +++ b/src/postgrid/types/print_mail/order_profiles/self_mailer_update_params.py @@ -3,12 +3,11 @@ from __future__ import annotations from typing import Dict, Optional -from typing_extensions import Required, Annotated, TypedDict +from typing_extensions import Literal, Required, Annotated, TypedDict from ...._types import SequenceNotStr from ...._utils import PropertyInfo from .self_mailer_size import SelfMailerSize -from ..order_mailing_class import OrderMailingClass __all__ = ["SelfMailerUpdateParams"] @@ -26,7 +25,37 @@ class SelfMailerUpdateParams(TypedDict, total=False): inside_template: Annotated[str, PropertyInfo(alias="insideTemplate")] """ID of the template for the inside. Required unless `pdf` is provided.""" - mailing_class: Annotated[OrderMailingClass, PropertyInfo(alias="mailingClass")] + mailing_class: Annotated[ + Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ], + PropertyInfo(alias="mailingClass"), + ] """Mailing class (cannot include extra services for self-mailers).""" merge_variables: Annotated[Optional[Dict[str, object]], PropertyInfo(alias="mergeVariables")] diff --git a/src/postgrid/types/print_mail/order_status.py b/src/postgrid/types/print_mail/order_status.py deleted file mode 100644 index 9fc29d4..0000000 --- a/src/postgrid/types/print_mail/order_status.py +++ /dev/null @@ -1,7 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal, TypeAlias - -__all__ = ["OrderStatus"] - -OrderStatus: TypeAlias = Literal["ready", "printing", "processed_for_delivery", "completed", "cancelled"] diff --git a/src/postgrid/types/print_mail/postcard.py b/src/postgrid/types/print_mail/postcard.py index 5609a2a..c153dc6 100644 --- a/src/postgrid/types/print_mail/postcard.py +++ b/src/postgrid/types/print_mail/postcard.py @@ -9,13 +9,20 @@ from .contact import Contact from ..._models import BaseModel -from .cancellation import Cancellation -from .order_status import OrderStatus -from .order_imb_status import OrderImbStatus -from .order_mailing_class import OrderMailingClass from .order_profiles.postcard_size import PostcardSize -__all__ = ["Postcard"] +__all__ = ["Postcard", "Cancellation"] + + +class Cancellation(BaseModel): + reason: Literal["user_initiated", "invalid_content", "invalid_order_mailing_class"] + """The reason for the cancellation.""" + + cancelled_by_user: Optional[str] = FieldInfo(alias="cancelledByUser", default=None) + """The user ID who cancelled the order.""" + + note: Optional[str] = None + """An optional note provided by the user who cancelled the order.""" class Postcard(BaseModel): @@ -28,7 +35,34 @@ class Postcard(BaseModel): live: bool """`true` if this is a live mode resource else `false`.""" - mailing_class: OrderMailingClass = FieldInfo(alias="mailingClass") + mailing_class: Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ] = FieldInfo(alias="mailingClass") """The mailing class of this order. This determines the speed and cost of delivery. See `OrderMailingClass` for more @@ -49,7 +83,7 @@ class Postcard(BaseModel): size: PostcardSize """Enum representing the supported postcard sizes.""" - status: OrderStatus + status: Literal["ready", "printing", "processed_for_delivery", "completed", "cancelled"] """See `OrderStatus` for more details on the status of this order.""" to: Contact @@ -82,7 +116,9 @@ class Postcard(BaseModel): See `imbStatus` for more details. """ - imb_status: Optional[OrderImbStatus] = FieldInfo(alias="imbStatus", default=None) + imb_status: Optional[Literal["entered_mail_stream", "out_for_delivery", "returned_to_sender"]] = FieldInfo( + alias="imbStatus", default=None + ) """The Intelligent Mail Barcode (IMB) status of this order. Only populated for US-printed and US-destined orders. This is the most detailed diff --git a/src/postgrid/types/print_mail/postcard_create_params.py b/src/postgrid/types/print_mail/postcard_create_params.py index ef43122..7c8f1b9 100644 --- a/src/postgrid/types/print_mail/postcard_create_params.py +++ b/src/postgrid/types/print_mail/postcard_create_params.py @@ -4,11 +4,10 @@ from typing import Dict, Union from datetime import datetime -from typing_extensions import Required, Annotated, TypeAlias, TypedDict +from typing_extensions import Literal, Required, Annotated, TypeAlias, TypedDict from ..._types import Base64FileInput from ..._utils import PropertyInfo -from .order_mailing_class import OrderMailingClass from .order_profiles.postcard_size import PostcardSize from ..contact_create_with_first_name_param import ContactCreateWithFirstNameParam from ..contact_create_with_company_name_param import ContactCreateWithCompanyNameParam @@ -65,7 +64,37 @@ class PostcardCreateWithHTML(TypedDict, total=False): Unlike other order types, the sender address is optional for postcards. """ - mailing_class: Annotated[OrderMailingClass, PropertyInfo(alias="mailingClass")] + mailing_class: Annotated[ + Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ], + PropertyInfo(alias="mailingClass"), + ] """The mailing class of this order. If not provided, automatically set to `first_class`. @@ -141,7 +170,37 @@ class PostcardCreateWithPdfurl(TypedDict, total=False): Unlike other order types, the sender address is optional for postcards. """ - mailing_class: Annotated[OrderMailingClass, PropertyInfo(alias="mailingClass")] + mailing_class: Annotated[ + Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ], + PropertyInfo(alias="mailingClass"), + ] """The mailing class of this order. If not provided, automatically set to `first_class`. @@ -203,7 +262,37 @@ class PostcardCreateWithPdfFile(TypedDict, total=False): Unlike other order types, the sender address is optional for postcards. """ - mailing_class: Annotated[OrderMailingClass, PropertyInfo(alias="mailingClass")] + mailing_class: Annotated[ + Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ], + PropertyInfo(alias="mailingClass"), + ] """The mailing class of this order. If not provided, automatically set to `first_class`. diff --git a/src/postgrid/types/print_mail/self_mailer.py b/src/postgrid/types/print_mail/self_mailer.py index ebf0d08..d8f88f9 100644 --- a/src/postgrid/types/print_mail/self_mailer.py +++ b/src/postgrid/types/print_mail/self_mailer.py @@ -9,13 +9,20 @@ from .contact import Contact from ..._models import BaseModel -from .cancellation import Cancellation -from .order_status import OrderStatus -from .order_imb_status import OrderImbStatus -from .order_mailing_class import OrderMailingClass from .order_profiles.self_mailer_size import SelfMailerSize -__all__ = ["SelfMailer"] +__all__ = ["SelfMailer", "Cancellation"] + + +class Cancellation(BaseModel): + reason: Literal["user_initiated", "invalid_content", "invalid_order_mailing_class"] + """The reason for the cancellation.""" + + cancelled_by_user: Optional[str] = FieldInfo(alias="cancelledByUser", default=None) + """The user ID who cancelled the order.""" + + note: Optional[str] = None + """An optional note provided by the user who cancelled the order.""" class SelfMailer(BaseModel): @@ -31,7 +38,34 @@ class SelfMailer(BaseModel): live: bool """`true` if this is a live mode resource else `false`.""" - mailing_class: OrderMailingClass = FieldInfo(alias="mailingClass") + mailing_class: Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ] = FieldInfo(alias="mailingClass") """The mailing class of this order. This determines the speed and cost of delivery. See `OrderMailingClass` for more @@ -52,7 +86,7 @@ class SelfMailer(BaseModel): size: SelfMailerSize """Enum representing the supported self-mailer sizes.""" - status: OrderStatus + status: Literal["ready", "printing", "processed_for_delivery", "completed", "cancelled"] """See `OrderStatus` for more details on the status of this order.""" to: Contact @@ -82,7 +116,9 @@ class SelfMailer(BaseModel): See `imbStatus` for more details. """ - imb_status: Optional[OrderImbStatus] = FieldInfo(alias="imbStatus", default=None) + imb_status: Optional[Literal["entered_mail_stream", "out_for_delivery", "returned_to_sender"]] = FieldInfo( + alias="imbStatus", default=None + ) """The Intelligent Mail Barcode (IMB) status of this order. Only populated for US-printed and US-destined orders. This is the most detailed diff --git a/src/postgrid/types/print_mail/self_mailer_create_params.py b/src/postgrid/types/print_mail/self_mailer_create_params.py index 23d2ae2..f9f6ca8 100644 --- a/src/postgrid/types/print_mail/self_mailer_create_params.py +++ b/src/postgrid/types/print_mail/self_mailer_create_params.py @@ -4,11 +4,10 @@ from typing import Dict, Union from datetime import datetime -from typing_extensions import Required, Annotated, TypeAlias, TypedDict +from typing_extensions import Literal, Required, Annotated, TypeAlias, TypedDict from ..._types import Base64FileInput from ..._utils import PropertyInfo -from .order_mailing_class import OrderMailingClass from .order_profiles.self_mailer_size import SelfMailerSize from ..contact_create_with_first_name_param import ContactCreateWithFirstNameParam from ..contact_create_with_company_name_param import ContactCreateWithCompanyNameParam @@ -64,7 +63,37 @@ class SelfMailerCreateWithHTML(TypedDict, total=False): Will be visible in the API and the dashboard. """ - mailing_class: Annotated[OrderMailingClass, PropertyInfo(alias="mailingClass")] + mailing_class: Annotated[ + Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ], + PropertyInfo(alias="mailingClass"), + ] """The mailing class of this order. If not provided, automatically set to `first_class`. @@ -139,7 +168,37 @@ class SelfMailerCreateWithPdfurl(TypedDict, total=False): Will be visible in the API and the dashboard. """ - mailing_class: Annotated[OrderMailingClass, PropertyInfo(alias="mailingClass")] + mailing_class: Annotated[ + Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ], + PropertyInfo(alias="mailingClass"), + ] """The mailing class of this order. If not provided, automatically set to `first_class`. @@ -202,7 +261,37 @@ class SelfMailerCreateWithPdfFile(TypedDict, total=False): Will be visible in the API and the dashboard. """ - mailing_class: Annotated[OrderMailingClass, PropertyInfo(alias="mailingClass")] + mailing_class: Annotated[ + Literal[ + "first_class", + "standard_class", + "express", + "certified", + "certified_return_receipt", + "registered", + "usps_first_class", + "usps_standard_class", + "usps_eddm", + "usps_express_2_day", + "usps_express_3_day", + "usps_first_class_certified", + "usps_first_class_certified_return_receipt", + "usps_first_class_registered", + "usps_express_3_day_signature_confirmation", + "usps_express_3_day_certified", + "usps_express_3_day_certified_return_receipt", + "ca_post_lettermail", + "ca_post_personalized", + "ca_post_neighbourhood_mail", + "ups_express_overnight", + "ups_express_2_day", + "ups_express_3_day", + "royal_mail_first_class", + "royal_mail_second_class", + "au_post_second_class", + ], + PropertyInfo(alias="mailingClass"), + ] """The mailing class of this order. If not provided, automatically set to `first_class`. diff --git a/tests/api_resources/print_mail/test_boxes.py b/tests/api_resources/print_mail/test_boxes.py deleted file mode 100644 index 20e0f67..0000000 --- a/tests/api_resources/print_mail/test_boxes.py +++ /dev/null @@ -1,452 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -import os -from typing import Any, cast - -import pytest - -from postgrid import PostGrid, AsyncPostGrid -from tests.utils import assert_matches_type -from postgrid._utils import parse_datetime -from postgrid.pagination import SyncSkipLimit, AsyncSkipLimit -from postgrid.types.print_mail import Box - -base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") - - -class TestBoxes: - parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_method_create(self, client: PostGrid) -> None: - box = client.print_mail.boxes.create( - cheques=[ - { - "amount": 5000, - "bank_account": "bank_abc", - "number": 1042, - "from": "contact_456", - "to": "contact_123", - } - ], - from_="contact_456", - to="contact_123", - ) - assert_matches_type(Box, box, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_method_create_with_all_params(self, client: PostGrid) -> None: - box = client.print_mail.boxes.create( - cheques=[ - { - "amount": 5000, - "bank_account": "bank_abc", - "number": 1042, - "logo_url": "https://example.com", - "memo": "memo", - "merge_variables": {"foo": "bar"}, - "message_template": "messageTemplate", - "from": "contact_456", - "to": "contact_123", - } - ], - from_="contact_456", - to="contact_123", - description="description", - mailing_class="first_class", - merge_variables={"foo": "bar"}, - metadata={"foo": "bar"}, - send_date=parse_datetime("2019-12-27T18:11:19.117Z"), - ) - assert_matches_type(Box, box, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_raw_response_create(self, client: PostGrid) -> None: - response = client.print_mail.boxes.with_raw_response.create( - cheques=[ - { - "amount": 5000, - "bank_account": "bank_abc", - "number": 1042, - "from": "contact_456", - "to": "contact_123", - } - ], - from_="contact_456", - to="contact_123", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - box = response.parse() - assert_matches_type(Box, box, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_streaming_response_create(self, client: PostGrid) -> None: - with client.print_mail.boxes.with_streaming_response.create( - cheques=[ - { - "amount": 5000, - "bank_account": "bank_abc", - "number": 1042, - "from": "contact_456", - "to": "contact_123", - } - ], - from_="contact_456", - to="contact_123", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - box = response.parse() - assert_matches_type(Box, box, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_method_retrieve(self, client: PostGrid) -> None: - box = client.print_mail.boxes.retrieve( - "id", - ) - assert_matches_type(Box, box, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_raw_response_retrieve(self, client: PostGrid) -> None: - response = client.print_mail.boxes.with_raw_response.retrieve( - "id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - box = response.parse() - assert_matches_type(Box, box, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_streaming_response_retrieve(self, client: PostGrid) -> None: - with client.print_mail.boxes.with_streaming_response.retrieve( - "id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - box = response.parse() - assert_matches_type(Box, box, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_path_params_retrieve(self, client: PostGrid) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - client.print_mail.boxes.with_raw_response.retrieve( - "", - ) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_method_list(self, client: PostGrid) -> None: - box = client.print_mail.boxes.list() - assert_matches_type(SyncSkipLimit[Box], box, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_method_list_with_all_params(self, client: PostGrid) -> None: - box = client.print_mail.boxes.list( - limit=0, - search="search", - skip=0, - ) - assert_matches_type(SyncSkipLimit[Box], box, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_raw_response_list(self, client: PostGrid) -> None: - response = client.print_mail.boxes.with_raw_response.list() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - box = response.parse() - assert_matches_type(SyncSkipLimit[Box], box, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_streaming_response_list(self, client: PostGrid) -> None: - with client.print_mail.boxes.with_streaming_response.list() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - box = response.parse() - assert_matches_type(SyncSkipLimit[Box], box, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_method_delete(self, client: PostGrid) -> None: - box = client.print_mail.boxes.delete( - "id", - ) - assert_matches_type(Box, box, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_raw_response_delete(self, client: PostGrid) -> None: - response = client.print_mail.boxes.with_raw_response.delete( - "id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - box = response.parse() - assert_matches_type(Box, box, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_streaming_response_delete(self, client: PostGrid) -> None: - with client.print_mail.boxes.with_streaming_response.delete( - "id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - box = response.parse() - assert_matches_type(Box, box, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - def test_path_params_delete(self, client: PostGrid) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - client.print_mail.boxes.with_raw_response.delete( - "", - ) - - -class TestAsyncBoxes: - parametrize = pytest.mark.parametrize( - "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] - ) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_method_create(self, async_client: AsyncPostGrid) -> None: - box = await async_client.print_mail.boxes.create( - cheques=[ - { - "amount": 5000, - "bank_account": "bank_abc", - "number": 1042, - "from": "contact_456", - "to": "contact_123", - } - ], - from_="contact_456", - to="contact_123", - ) - assert_matches_type(Box, box, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_method_create_with_all_params(self, async_client: AsyncPostGrid) -> None: - box = await async_client.print_mail.boxes.create( - cheques=[ - { - "amount": 5000, - "bank_account": "bank_abc", - "number": 1042, - "logo_url": "https://example.com", - "memo": "memo", - "merge_variables": {"foo": "bar"}, - "message_template": "messageTemplate", - "from": "contact_456", - "to": "contact_123", - } - ], - from_="contact_456", - to="contact_123", - description="description", - mailing_class="first_class", - merge_variables={"foo": "bar"}, - metadata={"foo": "bar"}, - send_date=parse_datetime("2019-12-27T18:11:19.117Z"), - ) - assert_matches_type(Box, box, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_raw_response_create(self, async_client: AsyncPostGrid) -> None: - response = await async_client.print_mail.boxes.with_raw_response.create( - cheques=[ - { - "amount": 5000, - "bank_account": "bank_abc", - "number": 1042, - "from": "contact_456", - "to": "contact_123", - } - ], - from_="contact_456", - to="contact_123", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - box = await response.parse() - assert_matches_type(Box, box, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_streaming_response_create(self, async_client: AsyncPostGrid) -> None: - async with async_client.print_mail.boxes.with_streaming_response.create( - cheques=[ - { - "amount": 5000, - "bank_account": "bank_abc", - "number": 1042, - "from": "contact_456", - "to": "contact_123", - } - ], - from_="contact_456", - to="contact_123", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - box = await response.parse() - assert_matches_type(Box, box, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_method_retrieve(self, async_client: AsyncPostGrid) -> None: - box = await async_client.print_mail.boxes.retrieve( - "id", - ) - assert_matches_type(Box, box, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_raw_response_retrieve(self, async_client: AsyncPostGrid) -> None: - response = await async_client.print_mail.boxes.with_raw_response.retrieve( - "id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - box = await response.parse() - assert_matches_type(Box, box, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_streaming_response_retrieve(self, async_client: AsyncPostGrid) -> None: - async with async_client.print_mail.boxes.with_streaming_response.retrieve( - "id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - box = await response.parse() - assert_matches_type(Box, box, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_path_params_retrieve(self, async_client: AsyncPostGrid) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - await async_client.print_mail.boxes.with_raw_response.retrieve( - "", - ) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_method_list(self, async_client: AsyncPostGrid) -> None: - box = await async_client.print_mail.boxes.list() - assert_matches_type(AsyncSkipLimit[Box], box, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_method_list_with_all_params(self, async_client: AsyncPostGrid) -> None: - box = await async_client.print_mail.boxes.list( - limit=0, - search="search", - skip=0, - ) - assert_matches_type(AsyncSkipLimit[Box], box, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_raw_response_list(self, async_client: AsyncPostGrid) -> None: - response = await async_client.print_mail.boxes.with_raw_response.list() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - box = await response.parse() - assert_matches_type(AsyncSkipLimit[Box], box, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_streaming_response_list(self, async_client: AsyncPostGrid) -> None: - async with async_client.print_mail.boxes.with_streaming_response.list() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - box = await response.parse() - assert_matches_type(AsyncSkipLimit[Box], box, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_method_delete(self, async_client: AsyncPostGrid) -> None: - box = await async_client.print_mail.boxes.delete( - "id", - ) - assert_matches_type(Box, box, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_raw_response_delete(self, async_client: AsyncPostGrid) -> None: - response = await async_client.print_mail.boxes.with_raw_response.delete( - "id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - box = await response.parse() - assert_matches_type(Box, box, path=["response"]) - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_streaming_response_delete(self, async_client: AsyncPostGrid) -> None: - async with async_client.print_mail.boxes.with_streaming_response.delete( - "id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - box = await response.parse() - assert_matches_type(Box, box, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @pytest.mark.skip(reason="Prism tests are disabled") - @parametrize - async def test_path_params_delete(self, async_client: AsyncPostGrid) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - await async_client.print_mail.boxes.with_raw_response.delete( - "", - ) From 0e71a295aea25a9e853e15ac6488c983744dd763 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 13 Nov 2025 04:14:50 +0000 Subject: [PATCH 13/13] release: 2.0.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 23 +++++++++++++++++++++++ README.md | 4 ++-- pyproject.toml | 2 +- src/postgrid/_version.py | 2 +- 5 files changed, 28 insertions(+), 5 deletions(-) create mode 100644 CHANGELOG.md diff --git a/.release-please-manifest.json b/.release-please-manifest.json index c476280..65f558e 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.0.1-alpha.0" + ".": "2.0.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..1fe90a9 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,23 @@ +# Changelog + +## 2.0.0 (2025-11-13) + +Full Changelog: [v0.0.1-alpha.0...v2.0.0](https://github.com/postgrid/postgrid-python/compare/v0.0.1-alpha.0...v2.0.0) + +### Bug Fixes + +* **api:** Pacify pyright ([0dea36d](https://github.com/postgrid/postgrid-python/commit/0dea36d9fcab59fec8412c753bc66b4dbc5b99e1)) +* **api:** Prepare request fix ([9f737cc](https://github.com/postgrid/postgrid-python/commit/9f737cc73d70892712759f52624ade9f9bd8ba7e)) +* **api:** remove unsupported collaterals ([fdbeb1a](https://github.com/postgrid/postgrid-python/commit/fdbeb1a3cf24dfbdaff4bf08966b6b04853e2840)) +* compat with Python 3.14 ([53457ab](https://github.com/postgrid/postgrid-python/commit/53457abee1fea7154fd0918aa194f106ff4a9a51)) +* **compat:** update signatures of `model_dump` and `model_dump_json` for Pydantic v1 ([899cab3](https://github.com/postgrid/postgrid-python/commit/899cab3df49e481eee336a3e585df7bb58334b0b)) +* **readme:** rename PostGrid ([c23572a](https://github.com/postgrid/postgrid-python/commit/c23572aeb87f04560983d0bdc664fecd393bab2a)) + + +### Chores + +* **internal:** codegen related update ([3b415ec](https://github.com/postgrid/postgrid-python/commit/3b415ece8193658f82764f82f6e6c55575b3d005)) +* **package:** drop Python 3.8 support ([e2e6808](https://github.com/postgrid/postgrid-python/commit/e2e6808b739591baf7258c1ea49fca1d83b09dfc)) +* sync repo ([3f005c5](https://github.com/postgrid/postgrid-python/commit/3f005c5c29d1583544b1e55269fe71b07f165d13)) +* update SDK settings ([8831e90](https://github.com/postgrid/postgrid-python/commit/8831e903672a9e505744acb42af75da40c816f61)) +* update SDK settings ([277f25a](https://github.com/postgrid/postgrid-python/commit/277f25a1fa957073e355fba73bfa783b85adb2ad)) diff --git a/README.md b/README.md index 469d9bf..cd4c8c7 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ The REST API documentation can be found on [docs.postgrid.com](https://docs.post ```sh # install from PyPI -pip install --pre postgrid-python +pip install postgrid-python ``` ## Usage @@ -83,7 +83,7 @@ You can enable this by installing `aiohttp`: ```sh # install from PyPI -pip install --pre postgrid-python[aiohttp] +pip install postgrid-python[aiohttp] ``` Then you can enable it by instantiating the client with `http_client=DefaultAioHttpClient()`: diff --git a/pyproject.toml b/pyproject.toml index c59b0a7..1777195 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "postgrid-python" -version = "0.0.1-alpha.0" +version = "2.0.0" description = "The official Python library for the PostGrid API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/postgrid/_version.py b/src/postgrid/_version.py index 153b599..c6a7b5f 100644 --- a/src/postgrid/_version.py +++ b/src/postgrid/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "postgrid" -__version__ = "0.0.1-alpha.0" # x-release-please-version +__version__ = "2.0.0" # x-release-please-version