diff --git a/.env_sample b/.env_sample new file mode 100644 index 0000000..937e999 --- /dev/null +++ b/.env_sample @@ -0,0 +1 @@ +export SENDGRID_API_KEY='' diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..47be0b2 --- /dev/null +++ b/.flake8 @@ -0,0 +1,2 @@ +[flake8] +exclude = build,venv,.svn,CVS,.bzr,.hg,.git,__pycache__,.tox,.eggs,*.egg diff --git a/.github/ISSUE_TEMPLATE b/.github/ISSUE_TEMPLATE deleted file mode 100644 index 373d0e5..0000000 --- a/.github/ISSUE_TEMPLATE +++ /dev/null @@ -1,17 +0,0 @@ -#### Issue Summary - -A summary of the issue and the environment in which it occurs. If suitable, include the steps required to reproduce the bug. Please feel free to include screenshots, screencasts, code examples. - - -#### Steps to Reproduce - -1. This is the first step -2. This is the second step -3. Further steps, etc. - -Any other information you want to share that is relevant to the issue being reported. Especially, why do you consider this to be a bug? What do you expect to happen instead? - -#### Technical details: - -* smtpapi-pythonn Version: master (latest commit: [commit number]) -* Python Version: X.X diff --git a/.github/PULL_REQUEST_TEMPLATE b/.github/PULL_REQUEST_TEMPLATE deleted file mode 100644 index 7ad590b..0000000 --- a/.github/PULL_REQUEST_TEMPLATE +++ /dev/null @@ -1,24 +0,0 @@ - -# Fixes # - -### Checklist -- [ ] I have made a material change to the repo (functionality, testing, spelling, grammar) -- [ ] I have read the [Contribution Guide] and my PR follows them. -- [ ] I updated my branch with the master branch. -- [ ] I have added tests that prove my fix is effective or that my feature works -- [ ] I have added necessary documentation about the functionality in the appropriate .md file -- [ ] I have added in line documentation to the code I modified - -### Short description of what this PR does: -- -- - -If you have questions, please send an email to [Sendgrid](mailto:dx@sendgrid.com), or file a Github Issue in this repository. diff --git a/.github/workflows/pr-lint.yml b/.github/workflows/pr-lint.yml new file mode 100644 index 0000000..2f5232b --- /dev/null +++ b/.github/workflows/pr-lint.yml @@ -0,0 +1,15 @@ +name: Lint PR +on: + pull_request_target: + types: [ opened, edited, synchronize, reopened ] + +jobs: + validate: + name: Validate title + runs-on: ubuntu-latest + steps: + - uses: amannn/action-semantic-pull-request@v4 + with: + types: chore docs fix feat test misc + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/test-and-deploy.yml b/.github/workflows/test-and-deploy.yml new file mode 100644 index 0000000..9638a03 --- /dev/null +++ b/.github/workflows/test-and-deploy.yml @@ -0,0 +1,88 @@ +name: Test and Deploy +on: + push: + branches: [ '*' ] + tags: [ '*' ] + pull_request: + branches: [ main ] + schedule: + # Run automatically at 8AM PST Monday-Friday + - cron: '0 15 * * 1-5' + workflow_dispatch: + +jobs: + test: + name: Test + runs-on: ubuntu-latest + timeout-minutes: 20 + strategy: + matrix: + python-version: [ '2.7', '3.5', '3.6', '3.7', '3.8', '3.9', '3.10' ] + steps: + - name: Checkout sendgrid-python-smtpapi + uses: actions/checkout@v2 + + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + + - name: Build & Test + run: make install test-install test + + deploy: + name: Deploy + if: success() && github.ref_type == 'tag' + needs: [ test ] + runs-on: ubuntu-latest + steps: + - name: Checkout sendgrid-python-smtpapi + uses: actions/checkout@v2 + + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: '3.10' + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install build + pip install wheel + python setup.py sdist bdist_wheel + + - name: Create GitHub Release + uses: sendgrid/dx-automator/actions/release@main + with: + footer: '**[pypi](https://pypi.org/project/smtpapi/${version})**' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Publish package to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + with: + user: __token__ + password: ${{ secrets.PYPI_TOKEN }} + + - name: Submit metric to Datadog + uses: sendgrid/dx-automator/actions/datadog-release-metric@main + env: + DD_API_KEY: ${{ secrets.DATADOG_API_KEY }} + + notify-on-failure: + name: Slack notify on failure + if: failure() && github.event_name != 'pull_request' && (github.ref == 'refs/heads/main' || github.ref_type == 'tag') + needs: [ test, deploy ] + runs-on: ubuntu-latest + steps: + - uses: rtCamp/action-slack-notify@v2 + env: + SLACK_COLOR: failure + SLACK_ICON_EMOJI: ':github:' + SLACK_MESSAGE: ${{ format('Test *{0}*, Deploy *{1}*, {2}/{3}/actions/runs/{4}', needs.test.result, needs.deploy.result, github.server_url, github.repository, github.run_id) }} + SLACK_TITLE: Action Failure - ${{ github.repository }} + SLACK_USERNAME: GitHub Actions + SLACK_MSG_AUTHOR: twilio-dx + SLACK_FOOTER: Posted automatically using GitHub Actions + SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} + MSG_MINIMAL: true diff --git a/.gitignore b/.gitignore index b14d298..301d0e8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,15 @@ -*.pyc *.egg *.egg-info -dist -eggs -build -sdist +*.pyc +.coverage +.env .Python bin/ +build +dist +eggs include/ lib/ +sdist +smtpapi/VERSION.txt +venv/ diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 61cf4db..0000000 --- a/.travis.yml +++ /dev/null @@ -1,17 +0,0 @@ -language: python -python: -- '2.6' -- '2.7' -- '3.4' -- '3.5' -install: python setup.py install -script: python test/__init__.py -notifications: - hipchat: - rooms: - secure: XbJIz0PtZo+/G6UCnCxLgYDPk7OS8fpyiQwpnfGBr2xW1Z5vhWS+jrR6GndAFJfW9mmgcvq9dJL4bSThITebcZJJtztfuWnGUnONKbfSsFcuEr6o3uGPNmfVwZ9JAL78BUU861A/ykcWe/6lkovlUphkDLa2/l1r7t7TGD75f5k= - template: - - '%{repository} - Build %{build_number} on branch %{branch} by %{author}: %{message} - View on GitHub' - format: html diff --git a/CHANGELOG.md b/CHANGELOG.md index 68cdfe9..6d884e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,135 @@ # Change Log -All notable changes to this project will be documented in this file. +All the notable changes to this project will be documented in this file. + +[2022-03-09] Version 0.4.12 +--------------------------- +**Library - Chore** +- [PR #112](https://github.com/sendgrid/smtpapi-python/pull/112): push Datadog Release Metric upon deploy success. Thanks to [@eshanholtz](https://github.com/eshanholtz)! + + +[2022-02-09] Version 0.4.11 +--------------------------- +**Library - Chore** +- [PR #111](https://github.com/sendgrid/smtpapi-python/pull/111): upgrade supported language versions. Thanks to [@childish-sambino](https://github.com/childish-sambino)! +- [PR #110](https://github.com/sendgrid/smtpapi-python/pull/110): add gh release to workflow. Thanks to [@shwetha-manvinkurke](https://github.com/shwetha-manvinkurke)! +- [PR #109](https://github.com/sendgrid/smtpapi-python/pull/109): merge test and deploy gh action workflows. Thanks to [@Hunga1](https://github.com/Hunga1)! + + +[2022-01-12] Version 0.4.10 +--------------------------- +**Library - Chore** +- [PR #108](https://github.com/sendgrid/smtpapi-python/pull/108): update license year. Thanks to [@JenniferMah](https://github.com/JenniferMah)! + + +[2021-12-01] Version 0.4.9 +-------------------------- +**Library - Chore** +- [PR #107](https://github.com/sendgrid/smtpapi-python/pull/107): migrate to GitHub Actions. Thanks to [@JenniferMah](https://github.com/JenniferMah)! + + +[2021-09-22] Version 0.4.8 +-------------------------- +**Library - Chore** +- [PR #106](https://github.com/sendgrid/smtpapi-python/pull/106): test against python v3.9. Thanks to [@shwetha-manvinkurke](https://github.com/shwetha-manvinkurke)! + + +[2020-12-02] Version 0.4.7 +-------------------------- +**Library - Chore** +- [PR #63](https://github.com/sendgrid/smtpapi-python/pull/63): Add .codeclimate.yml and update test/__init__.py. Thanks to [@geomars](https://github.com/geomars)! + + +[2020-09-28] Version 0.4.6 +-------------------------- +**Library - Docs** +- [PR #57](https://github.com/sendgrid/smtpapi-python/pull/57): Few typos and grammatical mistake corrected. Thanks to [@yudhik11](https://github.com/yudhik11)! +- [PR #61](https://github.com/sendgrid/smtpapi-python/pull/61): Add pull request info to CONTRIBUTING and README. Thanks to [@shorrock](https://github.com/shorrock)! + + +[2020-08-19] Version 0.4.5 +-------------------------- +**Library - Docs** +- [PR #72](https://github.com/sendgrid/smtpapi-python/pull/72): Fix .md with Grammar.ly. Thanks to [@ssiddhantsharma](https://github.com/ssiddhantsharma)! +- [PR #70](https://github.com/sendgrid/smtpapi-python/pull/70): Correct *.md files using Grammarly. Thanks to [@myzeprog](https://github.com/myzeprog)! + +**Library - Chore** +- [PR #105](https://github.com/sendgrid/smtpapi-python/pull/105): update GitHub branch references to use HEAD. Thanks to [@thinkingserious](https://github.com/thinkingserious)! + + +[2020-07-08] Version 0.4.4 +-------------------------- +**Library - Fix** +- [PR #86](https://github.com/sendgrid/smtpapi-python/pull/86): Fix and add flake8 to CI. Thanks to [@hugovk](https://github.com/hugovk)! + +**Library - Docs** +- [PR #99](https://github.com/sendgrid/smtpapi-python/pull/99): add use cases directory and README.md file for use cases. Thanks to [@mtroiani](https://github.com/mtroiani)! + + +[2020-04-01] Version 0.4.3 +-------------------------- +**Library - Chore** +- [PR #104](https://github.com/sendgrid/smtpapi-python/pull/104): add Python 3.8 to Travis. Thanks to [@childish-sambino](https://github.com/childish-sambino)! + + +[2020-02-19] Version 0.4.2 +-------------------------- +**Library - Fix** +- [PR #53](https://github.com/sendgrid/smtpapi-python/pull/53): "similar-code" issue in smtpapi/__init__.py. Thanks to [@andre8359](https://github.com/andre8359)! + +**Library - Docs** +- [PR #88](https://github.com/sendgrid/smtpapi-python/pull/88): Fix typos and grammatical errors. Thanks to [@vinayak42](https://github.com/vinayak42)! + + +[2020-01-24] Version 0.4.1 +-------------------------- +**Library - Fix** +- [PR #103](https://github.com/sendgrid/smtpapi-python/pull/103): travis update for autodeploy. Thanks to [@thinkingserious](https://github.com/thinkingserious)! + + +[2020-01-24] Version 0.4.0 +-------------------------- +**Library - Docs** +- [PR #102](https://github.com/sendgrid/smtpapi-python/pull/102): baseline all the templated markdown docs. Thanks to [@childish-sambino](https://github.com/childish-sambino)! +- [PR #77](https://github.com/sendgrid/smtpapi-python/pull/77): add first-timers.md with guide for newcomers. Thanks to [@daniloff200](https://github.com/daniloff200)! +- [PR #79](https://github.com/sendgrid/smtpapi-python/pull/79): Update contribution to use Gitflow workflow. Thanks to [@anatolyyyyyy](https://github.com/anatolyyyyyy)! +- [PR #93](https://github.com/sendgrid/smtpapi-python/pull/93): Added Announcement. Thanks to [@krischoi07](https://github.com/krischoi07)! +- [PR #34](https://github.com/sendgrid/smtpapi-python/pull/34): Add USAGE.md. Thanks to [@geomars](https://github.com/geomars)! +- [PR #27](https://github.com/sendgrid/smtpapi-python/pull/27): update contributing.md - fix typo. Thanks to [@pushkyn](https://github.com/pushkyn)! +- [PR #26](https://github.com/sendgrid/smtpapi-python/pull/26): update README.md. Thanks to [@thepriefy](https://github.com/thepriefy)! +- [PR #23](https://github.com/sendgrid/smtpapi-python/pull/23): ADD ISSUE_TEMPLATE. Thanks to [@prashant0598](https://github.com/prashant0598)! +- [PR #21](https://github.com/sendgrid/smtpapi-python/pull/21): add table of contents in README.md. Thanks to [@thepriefy](https://github.com/thepriefy)! +- [PR #19](https://github.com/sendgrid/smtpapi-python/pull/19): Update README. Thanks to [@micahduron](https://github.com/micahduron)! +- [PR #12](https://github.com/sendgrid/smtpapi-python/pull/12): Update README.md. Thanks to [@ciceropablo](https://github.com/ciceropablo)! +- [PR #20](https://github.com/sendgrid/smtpapi-python/pull/20): Demonstrate how to review the request body for troubleshooting. Thanks to [@mptap](https://github.com/mptap)! + +**Library - Feature** +- [PR #35](https://github.com/sendgrid/smtpapi-python/pull/35): Add docker files and update README. Thanks to [@NdagiStanley](https://github.com/NdagiStanley)! +- [PR #80](https://github.com/sendgrid/smtpapi-python/pull/80): Adding CHANGELOG auto generation script. Thanks to [@freyamade](https://github.com/freyamade)! + +**Library - Chore** +- [PR #37](https://github.com/sendgrid/smtpapi-python/pull/37): Created Code Climate YML file. Thanks to [@prashuchaudhary](https://github.com/prashuchaudhary)! +- [PR #41](https://github.com/sendgrid/smtpapi-python/pull/41): update LICENSE - fix year. Thanks to [@pushkyn](https://github.com/pushkyn)! +- [PR #65](https://github.com/sendgrid/smtpapi-python/pull/65): Fix "Complexity" issue in examples/example.py. Thanks to [@thepriefy](https://github.com/thepriefy)! +- [PR #66](https://github.com/sendgrid/smtpapi-python/pull/66): Update LICENSE.txt. Thanks to [@nocategory](https://github.com/nocategory)! +- [PR #87](https://github.com/sendgrid/smtpapi-python/pull/87): PEP8 fix. Thanks to [@vkmrishad](https://github.com/vkmrishad)! +- [PR #33](https://github.com/sendgrid/smtpapi-python/pull/33): Add .env_sample. Thanks to [@CorneliusIV](https://github.com/CorneliusIV)! +- [PR #29](https://github.com/sendgrid/smtpapi-python/pull/29): Create PULL_REQUEST_TEMPLATE.md. Thanks to [@random-hacktoberfest-participant-2017](https://github.com/random-hacktoberfest-participant-2017)! +- [PR #25](https://github.com/sendgrid/smtpapi-python/pull/25): Added CodeCov Badge. Thanks to [@gr8shivam](https://github.com/gr8shivam)! + +**Library - Test** +- [PR #42](https://github.com/sendgrid/smtpapi-python/pull/42): Issue#40 unittest to check for specific repo files. Thanks to [@bertuss](https://github.com/bertuss)! +- [PR #43](https://github.com/sendgrid/smtpapi-python/pull/43): added unittest to check for specific repo files. Thanks to [@riyasyash](https://github.com/riyasyash)! +- [PR #44](https://github.com/sendgrid/smtpapi-python/pull/44): added test_project.py for testing files in dir. Thanks to [@garuna-m6](https://github.com/garuna-m6)! +- [PR #45](https://github.com/sendgrid/smtpapi-python/pull/45): Add unit test for license year. Thanks to [@pushkyn](https://github.com/pushkyn)! +- [PR #48](https://github.com/sendgrid/smtpapi-python/pull/48): Test added for checking license year. Thanks to [@parth-p](https://github.com/parth-p)! + +**Library - Fix** +- [PR #52](https://github.com/sendgrid/smtpapi-python/pull/52): Update travis - add codecov. Thanks to [@pushkyn](https://github.com/pushkyn)! +- [PR #75](https://github.com/sendgrid/smtpapi-python/pull/75): fixed Travis, converted README and version as file. Thanks to [@StrikerRUS](https://github.com/StrikerRUS)! + + +[2015-10-01] Version 0.3.1 +--------------------------- -## [0.3.1] - 2015-10-01 ### Added -- Added in support for `Decimal` objects in SMTP API JSON messages (via @jstol) \ No newline at end of file +- Added in support for `Decimal` objects in SMTP API JSON messages (via @jstol) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index a0482c5..2f0727e 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,41 +1,73 @@ -# SendGrid Community Code of Conduct +# Contributor Covenant Code of Conduct -The SendGrid open source community is made up of members from around the globe with a diverse set of skills, personalities, and experiences. It is through these differences that our community experiences successes and continued growth. When you're working with members of the community, we encourage you to follow these guidelines, which help steer our interactions and strive to maintain a positive, successful and growing community. +## Our Pledge -### Be Open -Members of the community are open to collaboration, whether it's on pull requests, code reviews, approvals, issues or otherwise. We're receptive to constructive comments and criticism, as the experiences and skill sets of all members contribute to the whole of our efforts. We're accepting of all who wish to take part in our activities, fostering an environment where anyone can participate, and everyone can make a difference. +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, sex characteristics, gender identity and expression, +level of experience, education, socio-economic status, nationality, personal +appearance, race, religion, or sexual identity and orientation. -### Be Considerate -Members of the community are considerate of their peers, which include other contributors and users of SendGrid. We're thoughtful when addressing the efforts of others, keeping in mind that often the labor was completed with the intent of the good of the community. We're attentive in our communications, whether in person or online, and we're tactful when approaching differing views. +## Our Standards -### Be Respectful -Members of the community are respectful. We're respectful of others, their positions, their skills, their commitments and their efforts. We're respectful of the volunteer efforts that permeate the SendGrid community. We're respectful of the processes outlined in the community, and we work within them. When we disagree, we are courteous in raising our issues. Overall, we're good to each other. We contribute to this community not because we have to, but because we want to. If we remember that, these guidelines will come naturally. +Examples of behavior that contributes to creating a positive environment +include: -## Additional Guidance +- Using welcoming and inclusive language +- Being respectful of differing viewpoints and experiences +- Gracefully accepting constructive criticism +- Focusing on what is best for the community +- Showing empathy towards other community members -### Disclose Potential Conflicts of Interest -Community discussions often involve interested parties. We expect participants to be aware when they are conflicted due to employment or other projects they are involved in and disclose those interests to other project members. When in doubt, over-disclose. Perceived conflicts of interest are important to address so that the community’s decisions are credible even when unpopular, difficult or favorable to the interests of one group over another. +Examples of unacceptable behavior by participants include: -### Interpretation -This Code is not exhaustive or complete. It is not a rulebook; it serves to distill our common understanding of a collaborative, shared environment and goals. We expect it to be followed in spirit as much as in the letter. When in doubt, try to abide by [SendGrid’s cultural values](https://sendgrid.com/blog/employee-engagement-the-4h-way) defined by our “4H’s”: Happy, Hungry, Humble and Honest. +- The use of sexualized language or imagery and unwelcome sexual attention or + advances +- Trolling, insulting/derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or electronic + address, without explicit permission +- Other conduct which could reasonably be considered inappropriate in a + professional setting -### Enforcement -Most members of the SendGrid community always comply with this Code, not because of the existence of this Code, but because they have long experience participating in open source communities where the conduct described above is normal and expected. However, failure to observe this Code may be grounds for suspension, reporting the user for abuse or changing permissions for outside contributors. +## Our Responsibilities -## If you have concerns about someone’s conduct -**Initiate Direct Contact** - It is always appropriate to email a community member (if contact information is available), mention that you think their behavior was out of line, and (if necessary) point them to this Code. +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. -**Discuss Publicly** - Discussing publicly is always acceptable. Note, though, that approaching the person directly may be better, as it tends to make them less defensive, and it respects the time of other community members, so you probably want to try direct contact first. +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. -**Contact the Moderators** - You can reach the SendGrid moderators by emailing dx@sendgrid.com. +## Scope -## Submission to SendGrid Repositories -Finally, just a reminder, changes to the SendGrid repositories will only be accepted upon completion of the [SendGrid Contributor Agreement](https://cla.sendgrid.com). +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at open-source@twilio.com. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. ## Attribution -SendGrid thanks the following, on which it draws for content and inspiration: +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html -* [Python Community Code of Conduct](https://www.python.org/psf/codeofconduct/) -* [Open Source Initiative General Code of Conduct](https://opensource.org/codeofconduct) -* [Apache Code of Conduct](https://www.apache.org/foundation/policies/conduct.html) +[homepage]: https://www.contributor-covenant.org diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8d41b42..53bcd2a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,63 +1,19 @@ -Hello! Thank you for choosing to help contribute to one of the SendGrid open source projects. There are many ways you can contribute and help is always welcome. We simply ask that you follow the following contribution policies. +Hello! Thank you for choosing to help contribute to one of the Twilio SendGrid open source projects. There are many ways you can contribute and help is always welcome. We simply ask that you follow the following contribution policies. -- [CLAs and CCLAs](#cla) -- [Roadmap & Milestones](#roadmap) -- [Feature Request](#feature-request) -- [Submit a Bug Report](#submit-a-bug-report) - [Improvements to the Codebase](#improvements-to-the-codebase) - [Understanding the Code Base](#understanding-the-codebase) - [Testing](#testing) - [Style Guidelines & Naming Conventions](#style-guidelines-and-naming-conventions) - [Creating a Pull Request](#creating-a-pull-request) - - -We use [Milestones](https://github.com/sendgrid/smtpapi-python/milestones) to help define current roadmaps, please feel free to grab an issue from the current milestone. Please indicate that you have begun work on it to avoid collisions. Once a PR is made, community review, comments, suggestions and additional PRs are welcomed and encouraged. - - -## CLAs and CCLAs - -Before you get started, SendGrid requires that a SendGrid Contributor License Agreement (CLA) be filled out by every contributor to a SendGrid open source project. - -Our goal with the CLA is to clarify the rights of our contributors and reduce other risks arising from inappropriate contributions. The CLA also clarifies the rights SendGrid holds in each contribution and helps to avoid misunderstandings over what rights each contributor is required to grant to SendGrid when making a contribution. In this way the CLA encourages broad participation by our open source community and helps us build strong open source projects, free from any individual contributor withholding or revoking rights to any contribution. - -SendGrid does not merge a pull request made against a SendGrid open source project until that pull request is associated with a signed CLA. Copies of the CLA are available [here](https://gist.github.com/SendGridDX/98b42c0a5d500058357b80278fde3be8#file-sendgrid_cla). - -When you create a Pull Request, after a few seconds, a comment will appear with a link to the CLA. Click the link and fill out the brief form and then click the "I agree" button and you are all set. You will not be asked to re-sign the CLA unless we make a change. - -There are a few ways to contribute, which we'll enumerate below: - - -## Feature Request - -If you'd like to make a feature request, please read this section. - -The GitHub issue tracker is the preferred channel for library feature requests, but please respect the following restrictions: - -- Please **search for existing issues** in order to ensure we don't have duplicate bugs/feature requests. -- Please be respectful and considerate of others when commenting on issues - - -## Submit a Bug Report - -Note: DO NOT include your credentials in ANY code examples, descriptions, or media you make public. - -A software bug is a demonstrable issue in the code base. In order for us to diagnose the issue and respond as quickly as possible, please add as much detail as possible into your bug report. - -Before you decide to create a new issue, please try the following: - -1. Check the Github issues tab if the identified issue has already been reported, if so, please add a +1 to the existing post. -2. Update to the latest version of this code and check if issue has already been fixed -3. Copy and fill in the Bug Report Template we have provided below - -### Please use our Bug Report Template - -In order to make the process easier, we've included a [sample bug report template](https://github.com/sendgrid/smtpapi-python/.github/ISSUE_TEMPLATE) (borrowed from [Ghost](https://github.com/TryGhost/Ghost/)). The template uses [GitHub flavored markdown](https://help.github.com/articles/github-flavored-markdown/) for formatting. +- [Code Reviews](#code-reviews) ## Improvements to the Codebase We welcome direct contributions to the smtpapi-python code base. Thank you! +Please note that we use the [Gitflow Workflow](https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow) for Git to help keep project development organized and consistent. + ### Development Environment ### #### Install and Run Locally #### @@ -75,7 +31,7 @@ cd smtpapi-python ##### Execute: ##### -See the [examples folder](https://github.com/sendgrid/smtpapi-python/tree/master/examples) to get started quickly. +See the [examples folder](examples) to get started quickly. ## Understanding the Code Base @@ -97,9 +53,9 @@ Builds the SMTPAPI header. All PRs require passing tests before the PR will be reviewed. -All test files are in the [`test`](https://github.com/sendgrid/smtpapi-python/tree/master/test) directory. +All the test files are in the [`test`](test) directory. -For the purposes of contributing to this repo, please update the [`__init__.py`](https://github.com/sendgrid/smtpapi-python/blob/master/test/__init__.py) file with unit tests as you modify the code. +For the purposes of contributing to this repo, please update the [`__init__.py`](test/__init__.py) file with unit tests as you modify the code. ```bash python setup.py install @@ -140,7 +96,7 @@ Please run your code through: git pull upstream ``` -3. Create a new topic branch (off the main project development branch) to +3. Create a new topic branch off the `development` branch to contain your feature, change, or fix: ```bash @@ -149,7 +105,7 @@ Please run your code through: 4. Commit your changes in logical chunks. Please adhere to these [git commit message guidelines](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) - or your code is unlikely be merged into the main project. Use Git's + or your code is unlikely to be merged into the main project. Use Git's [interactive rebase](https://help.github.com/articles/interactive-rebase) feature to tidy up your commits before making them public. @@ -157,10 +113,10 @@ Please run your code through: 4b. Create or update the example code that demonstrates the functionality of this change to the code. -5. Locally merge (or rebase) the upstream development branch into your topic branch: +5. Locally merge (or rebase) the upstream `development` branch into your topic branch: ```bash - git pull [--rebase] upstream master + git pull [--rebase] upstream development ``` 6. Push your topic branch up to your fork: @@ -170,6 +126,10 @@ Please run your code through: ``` 7. [Open a Pull Request](https://help.github.com/articles/using-pull-requests/) - with a clear title and description against the `master` branch. All tests must be passing before we will review the PR. + with a clear title and description against the `development` branch. All tests must be passing before we will review the PR. -If you have any additional questions, please feel free to [email](mailto:dx@sendgrid.com) us or create an issue in this repo. +## Code Reviews +If you can, please look at open PRs and review them. +Give feedback and help us merge these PRs much faster! +If you don't know how, GitHub has some great +[information on how to review a Pull Request](https://help.github.com/articles/about-pull-request-reviews/). diff --git a/Dockerfile b/Dockerfile index 8f5a44f..527ece2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ FROM ubuntu:xenial ENV PYTHON_VERSIONS='python2.6 python2.7 python3.4 python3.5 python3.6' \ - OAI_SPEC_URL="https://raw.githubusercontent.com/sendgrid/sendgrid-oai/master/oai_stoplight.json" + OAI_SPEC_URL="https://raw.githubusercontent.com/sendgrid/sendgrid-oai/HEAD/oai_stoplight.json" # install testing versions of python, including old versions, from deadsnakes RUN set -x \ @@ -17,7 +17,7 @@ RUN set -x \ WORKDIR /root # install Prism -ADD https://raw.githubusercontent.com/stoplightio/prism/master/install.sh install.sh +ADD https://raw.githubusercontent.com/stoplightio/prism/HEAD/install.sh install.sh RUN chmod +x ./install.sh && \ ./install.sh && \ rm ./install.sh diff --git a/FIRST_TIMERS.md b/FIRST_TIMERS.md new file mode 100644 index 0000000..ade7912 --- /dev/null +++ b/FIRST_TIMERS.md @@ -0,0 +1,53 @@ +# How To Contribute to Twilio SendGrid Repositories via GitHub +Contributing to the Twilio SendGrid repositories is easy! All you need to do is find an open issue (see the bottom of this page for a list of repositories containing open issues), fix it and submit a pull request. Once you have submitted your pull request, the team can easily review it before it is merged into the repository. + +To make a pull request, follow these steps: + +1. Log into GitHub. If you do not already have a GitHub account, you will have to create one in order to submit a change. Click the Sign up link in the upper right-hand corner to create an account. Enter your username, password, and email address. If you are an employee of Twilio SendGrid, please use your full name with your GitHub account and enter Twilio SendGrid as your company so we can easily identify you. + + + +2. __[Fork](https://help.github.com/fork-a-repo/)__ the [smtpapi-python](https://github.com/sendgrid/smtpapi-python) repository: + + + +3. __Clone__ your fork via the following commands: + +```bash +# Clone your fork of the repo into the current directory +git clone https://github.com/your_username/smtpapi-python +# Navigate to the newly cloned directory +cd smtpapi-python +# Assign the original repo to a remote called "upstream" +git remote add upstream https://github.com/sendgrid/smtpapi-python +``` + +> Don't forget to replace *your_username* in the URL by your real GitHub username. + +4. __Create a new topic branch__ (off the main project development branch) to contain your feature, change, or fix: + +```bash +git checkout -b +``` + +5. __Commit your changes__ in logical chunks. + +Please adhere to these [git commit message guidelines](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) or your code is unlikely be merged into the main project. Use Git's [interactive rebase](https://help.github.com/articles/interactive-rebase) feature to tidy up your commits before making them public. Probably you will also have to create tests (if needed) or create or update the example code that demonstrates the functionality of this change to the code. + +6. __Locally merge (or rebase)__ the upstream development branch into your topic branch: + +```bash +git pull [--rebase] upstream main +``` + +7. __Push__ your topic branch up to your fork: + +```bash +git push origin +``` + +8. __[Open a Pull Request](https://help.github.com/articles/creating-a-pull-request/#changing-the-branch-range-and-destination-repository/)__ with a clear title and description against the `main` branch. All tests must be passing before we will review the PR. + +## Important notice + +Before creating a pull request, make sure that you respect the repository's constraints regarding contributions. You can find them in the [CONTRIBUTING.md](CONTRIBUTING.md) file. diff --git a/LICENSE.txt b/LICENSE similarity index 59% rename from LICENSE.txt rename to LICENSE index c02b767..3154774 100644 --- a/LICENSE.txt +++ b/LICENSE @@ -1,13 +1,13 @@ -The MIT License (MIT) +MIT License -Copyright (c) 2013-2016 SendGrid, Inc. +Copyright (C) 2023, Twilio SendGrid, Inc. -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. diff --git a/MANIFEST.in b/MANIFEST.in index bb3ec5f..20d9d81 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1 +1,3 @@ -include README.md +include README.rst LICENSE VERSION.txt +recursive-include smtpapi *.py *.txt +prune test diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..3e3bb1a --- /dev/null +++ b/Makefile @@ -0,0 +1,24 @@ +.PHONY: venv install test-install test clean nopyc + +venv: + @python --version || (echo "Python is not installed, please install Python 2 or Python 3"; exit 1); + pip install virtualenv + virtualenv --python=python venv + +install: venv + . venv/bin/activate; pip install . + +test-install: + . venv/bin/activate; pip install -r test/requirements.txt + +test: + . venv/bin/activate; python -m unittest discover -v + . venv/bin/activate; python test/__init__.py + . venv/bin/activate; flake8 --statistics --count + . venv/bin/activate; coverage run test/__init__.py + +clean: nopyc + rm -rf venv + +nopyc: + find . -name \*.pyc -delete diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..8a966bb --- /dev/null +++ b/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,31 @@ + + +# Fixes # + +A short description of what this PR does. + +### Checklist +- [x] I acknowledge that all my contributions will be made under the project's license +- [ ] I have made a material change to the repo (functionality, testing, spelling, grammar) +- [ ] I have read the [Contribution Guidelines](https://github.com/sendgrid/smtpapi-python/blob/main/CONTRIBUTING.md) and my PR follows them +- [ ] I have titled the PR appropriately +- [ ] I have updated my branch with the main branch +- [ ] I have added tests that prove my fix is effective or that my feature works +- [ ] I have added the necessary documentation about the functionality in the appropriate .md file +- [ ] I have added inline documentation to the code I modified + +If you have questions, please file a [support ticket](https://support.sendgrid.com). diff --git a/README.md b/README.md deleted file mode 100644 index 24264ab..0000000 --- a/README.md +++ /dev/null @@ -1,96 +0,0 @@ -![SendGrid Logo](https://uiux.s3.amazonaws.com/2016-logos/email-logo%402x.png) - -[![Travis Badge](https://travis-ci.org/sendgrid/smtpapi-python.svg?branch=master)](https://travis-ci.org/sendgrid/smtpapi-python) -[![Email Notifications Badge](https://dx.sendgrid.com/badge/python)](https://dx.sendgrid.com/newsletter/python) -[![Twitter Follow](https://img.shields.io/twitter/follow/sendgrid.svg?style=social&label=Follow)](https://twitter.com/sendgrid) -[![Codecov branch](https://img.shields.io/codecov/c/github/sendgrid/smtpapi-python/master.svg?style=flat-square&label=Codecov+Coverage)](https://codecov.io/gh/sendgrid/smtpapi-python) -[![GitHub contributors](https://img.shields.io/github/contributors/sendgrid/smtpapi-python.svg)](https://github.com/sendgrid/smtpapi-python/graphs/contributors) -[![MIT Licensed](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE.txt) - -**This module helps build SendGrid's SMTP API headers.** - -Learn more about the SMTP API at [SendGrid's documentation](https://sendgrid.com/docs/API_Reference/SMTP_API/index.html). - -# Announcements - -All updates to this module is documented in our [CHANGELOG](https://github.com/sendgrid/smtpapi-python/blob/master/CHANGELOG.md). - -# Table of Contents -- [Installation](#installation) -- [Quick Start](#quick-start) -- [Usage](#usage) -- [Roadmap](#roadmap) -- [How to Contribute](#contribute) -- [Local set up](#local_setup) -- [About](#about) -- [License](#license) - - -# Installation - -## Prerequisites - -- Python version 2.6, 2.7, 3.4 or 3.5 -- The SendGrid service, starting at the [free level](https://sendgrid.com/free?source=sendgrid-python) - -## Install Package - -```bash -pip install smtpapi -``` - - -# Quick Start - -```python -from smtpapi import SMTPAPIHeader -header = SMTPAPIHeader() -header.add_to('email@email.com') -print(header.json_string()) -``` - - -# Usage - -- [SendGrid Docs](https://sendgrid.com/docs/API_Reference/SMTP_API/index.html) -- [Example Code](https://github.com/sendgrid/smtpapi-python/tree/master/examples) - - -# Roadmap - -If you are interested in the future direction of this project, please take a look at our [milestones](https://github.com/sendgrid/smtpapi-python/milestones). We would love to hear your feedback. - - -# How to Contribute - -We encourage contribution to our projects, please see our [CONTRIBUTING](https://github.com/sendgrid/smtpapi-python/blob/master/CONTRIBUTING.md) guide for details. - -Quick links: - -- [Feature Request](https://github.com/sendgrid/smtpapi-python/blob/master/CONTRIBUTING.md#feature-request) -- [Bug Reports](https://github.com/sendgrid/smtpapi-python/blob/master/CONTRIBUTING.md#submit-a-bug-report) -- [Sign the CLA to Create a Pull Request](https://github.com/sendgrid/smtpapi-python/blob/master/CONTRIBUTING.md#cla) -- [Improvements to the Codebase](https://github.com/sendgrid/smtpapi-python/blob/master/CONTRIBUTING.md#improvements-to-the-codebase) - - - -# Local Setup of the project - -The simplest local development workflow is by using docker. - -> Steps - -1. Install Docker -2. Run `docker-compose build` (This builds the container) -3. Run `docker-compose up` (This runs tests by default) - - -# About - -smtpapi-python is guided and supported by the SendGrid [Developer Experience Team](mailto:dx@sendgrid.com). - -smtpapi-python is maintained and funded by SendGrid, Inc. The names and logos for smtpapi-python are trademarks of SendGrid, Inc. - - -# License -[The MIT License (MIT)](LICENSE.txt) diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..4b6041f --- /dev/null +++ b/README.rst @@ -0,0 +1,131 @@ +.. image:: https://uiux.s3.amazonaws.com/2016-logos/email-logo%402x.png + :target: https://www.sendgrid.com + :alt: SendGrid Logo + +|Test and Deploy Badge| |Twitter Follow| |Codecov branch| |Python Versions| |PyPI Version| |GitHub contributors| |MIT Licensed| + +**This module helps build SendGrid's SMTP API headers.** + +Learn more about the SMTP API at `SendGrid documentation`_. + +Announcements +============= +All the updates to this module are documented in our `CHANGELOG`_. + +Table of Contents +================= + +- `Installation <#installation>`__ +- `Quick Start <#quick-start>`__ +- `Usage <#usage>`__ +- `How to Contribute <#how-to-contribute>`__ +- `Local Setup of the Project <#local-setup-of-the-project>`__ +- `About <#about>`__ +- `License <#license>`__ + +Installation +============ + +Prerequisites +------------- + +- Python version 2.7 or 3.4+ +- The SendGrid service, starting at the `free level`_ + +Install Package +--------------- + +.. code:: bash + + pip install smtpapi + +Setup Environment Variables +--------------------------- + +Update the development environment with your `SENDGRID_API_KEY`_, for example: + +.. code:: bash + + cp .env_sample .env + +In ``.env`` set ``SENDGRID_API_KEY`` to your own API key. + +You can add your environment variables to your environment by sourcing the file: + +.. code:: bash + + source .env + +Quick Start +=========== + +.. code:: python + + from smtpapi import SMTPAPIHeader + + header = SMTPAPIHeader() + header.add_to('email@email.com') + print(header.json_string()) + +Usage +===== + +- `SendGrid documentation`_ +- `Example Code`_ + +How to Contribute +================= + +We encourage contribution to our projects, please see our `CONTRIBUTING`_ guide for more details. + +Quick links: + +- `Improvements to the Codebase`_ +- `Review Pull Requests`_ + +Local Setup of the Project +========================== + +The simplest local development workflow is by using Docker. + +Steps: + +1. Install [Docker](https://www.docker.com/) +2. Run ``docker-compose build`` (this builds the container) +3. Run ``docker-compose up`` (this runs tests by default) + +About +===== + +**smtpapi-python** is maintained and funded by Twilio SendGrid, Inc. +The names and logos for **smtpapi-python** are trademarks of Twilio SendGrid, Inc. + +License +======= + +`The MIT License (MIT)`_ + +.. _SendGrid documentation: https://sendgrid.com/docs/API_Reference/SMTP_API/index.html +.. _CHANGELOG: https://github.com/sendgrid/smtpapi-python/blob/HEAD/CHANGELOG.md +.. _free level: https://sendgrid.com/free?source=sendgrid-python +.. _SENDGRID_API_KEY: https://app.sendgrid.com/settings/api_keys +.. _Example Code: https://github.com/sendgrid/smtpapi-python/tree/HEAD/examples +.. _CONTRIBUTING: https://github.com/sendgrid/smtpapi-python/blob/HEAD/CONTRIBUTING.md +.. _Improvements to the Codebase: https://github.com/sendgrid/smtpapi-python/blob/HEAD/CONTRIBUTING.md#improvements-to-the-codebase +.. _Review Pull Requests: https://github.com/sendgrid/smtpapi-python/blob/HEAD/CONTRIBUTING.md#code-reviews) +.. _The MIT License (MIT): https://github.com/sendgrid/smtpapi-python/blob/HEAD/LICENSE + +.. |Test and Deploy Badge| image:: https://github.com/sendgrid/smtpapi-python/actions/workflows/test-and-deploy.yml/badge.svg + :target: https://github.com/sendgrid/smtpapi-python/actions/workflows/test-and-deploy.yml +.. |Twitter Follow| image:: https://img.shields.io/twitter/follow/sendgrid.svg?style=social&label=Follow + :target: https://twitter.com/sendgrid +.. |Codecov branch| image:: https://img.shields.io/codecov/c/github/sendgrid/smtpapi-python/main.svg?style=flat-square&label=Codecov+Coverage + :target: https://codecov.io/gh/sendgrid/smtpapi-python +.. |Python Versions| image:: https://img.shields.io/pypi/pyversions/smtpapi.svg + :target: https://pypi.org/project/smtpapi/ +.. |PyPI Version| image:: https://img.shields.io/pypi/v/smtpapi.svg + :target: https://pypi.org/project/smtpapi/ +.. |GitHub contributors| image:: https://img.shields.io/github/contributors/sendgrid/smtpapi-python.svg + :target: https://github.com/sendgrid/smtpapi-python/graphs/contributors +.. |MIT Licensed| image:: https://img.shields.io/badge/license-MIT-blue.svg + :target: https://github.com/sendgrid/smtpapi-python/blob/HEAD/LICENSE diff --git a/TROUBLESHOOTING.md b/TROUBLESHOOTING.md index af15ede..717dcfa 100644 --- a/TROUBLESHOOTING.md +++ b/TROUBLESHOOTING.md @@ -1,7 +1,5 @@ If you have a non-library SendGrid issue, please contact our [support team](https://support.sendgrid.com). -If you can't find a solution below, please open an [issue](https://github.com/sendgrid/smtpapi-python/issues). - ## Table of Contents * [Viewing the Request Body](#request-body) diff --git a/USAGE.md b/USAGE.md index 871c292..737d565 100644 --- a/USAGE.md +++ b/USAGE.md @@ -36,9 +36,9 @@ print(header.json_string()) * [Using the SMTP API](#use-smtp-api) * [Settings](#settings) * [Categories](#categories) -* [Schedulling Parameters](#schedulling-parameters) +* [Scheduling Parameters](#scheduling-parameters) * [Section Tags](#section-tags) -* [Subtitution Tags](#subtitution-tags) +* [Substitution Tags](#substitution-tags) * [Suppression Groups](#suppression-groups) * [Unique Arguments](#unique-arguments) @@ -90,7 +90,7 @@ You can add as many of the SMTP API methods as you want to a single large JSON s The above example is formatted for readability. Headers must be wrapped to keep the line length under 72. By RFC 821 no line can be longer than 1,000, so if you are going to generate this string yourself it is a good idea to make sure that you wrap it. ## Requirements and Limitations -While there is a hard limit of 10,000 addresses that can be sent to in a multiple recipient e-mail, it is best to split up large jobs to around 1,000 recipients, to better allow for the processing load to be distributed. Furthermore, if you have a large number of additional substitutions or sections in the headers, it is best to split the send into even smaller groups. +While there is a hard limit of 10,000 addresses that can be sent to in a multiple recipient e-mail, it is best to split up large jobs to around 1,000 recipients, to better allow for the processing load to be distributed. Furthermore, if you have numerous additional substitutions or sections in the headers, it is best to split the group into even smaller groups. # Settings (Filters) @@ -123,7 +123,7 @@ Sends a BCC copy of the email created in this transaction to the address specifi # Categories **This endpoint allows you to add categories to the X-SMTPAPI header of the emails you send via SendGrid** -By adding categories to the X-SMTPAPI header of the emails that you send via SendGrid you can to track emails based on your own categorization system. +By adding categories to the X-SMTPAPI header of the emails that you send via SendGrid you can to track emails based on your categorization system. Categories must be in 7bit encoding using the US-ASCII character set, and should be used to group messages together by broad topic. If you need to attach unique data or identifiers to a message, use [Unique Arguments](https://sendgrid.com/docs/API_Reference/SMTP_API/unique_arguments.html) instead. @@ -151,8 +151,8 @@ You can assign up to 10 categories per message: } ``` - -# Schedulling Parameters + +# Scheduling Parameters **This endpoint allows you to send large volumes of email in queued batches or target individual recipients by specifying a custom UNIX timestamp parameter.** This parameter allows SendGrid to begin processing a customer’s email requests before sending. SendGrid will then queue those messages and release them when the timestamp is exceeded. This technique allows for a more efficient way to distribute large email requests and can improve overall mail delivery time performance. @@ -168,7 +168,7 @@ Using the parameters defined below, you can queue batches of emails targeting in **Note: Using both send_at and send_each_at is not valid and will cause your request to be dropped.** ### Send At -To schedule a send request for a large batch of emails use the send_at parameter which will send all emails at approximately the same time. send_at is a UNIX timestamp. +To schedule a send request for a large batch of emails, use the send_at parameter which will send all emails at approximately the same time. send_at is a UNIX timestamp. Example of **`send_at`** email header @@ -212,8 +212,8 @@ The format of the SMTP API section tag has the form: } ``` - -# Subtitution Tags + +# Substitution Tags **This endpoint allows you to easily generate dynamic content for each recipient on your list.** @@ -332,7 +332,7 @@ You can specify up to 25 groups to display. ``` ## Groups -You can find your group IDs by looking at the Group ID column in the Unsubscribe Groups UI, or by calling the [GET method](https://sendgrid.com/docs/API_Reference/Web_API_v3/Suppression_Management/groups.html#-GET) of the groups resource. +You can find your group IDs by looking at the Group ID column in the Unsubscribe Groups UI, or by calling the [GET method](https://sendgrid.com/docs/API_Reference/Web_API_v3/Suppression_Management/groups.html#-GET) of the group's resource. # Unique Arguments @@ -387,4 +387,4 @@ To apply different unique arguments to individual emails, you may use substituti "customerAccountNumber": "-account_number-" } } -``` \ No newline at end of file +``` diff --git a/VERSION.txt b/VERSION.txt new file mode 100644 index 0000000..75274d8 --- /dev/null +++ b/VERSION.txt @@ -0,0 +1 @@ +0.4.12 diff --git a/changes.py b/changes.py new file mode 100644 index 0000000..79448d5 --- /dev/null +++ b/changes.py @@ -0,0 +1,153 @@ +#!/usr/bin/python +""" +Small python script that, when run, will update the CHANGELOG with information +about all merged pull requests since the previous release. + +This script must be run after tagging the latest version +It checks the log of commits since the previous tag and parses it +""" +import re +import subprocess +import sys +from datetime import datetime + +# Regex patterns +RELEASE_MD_PATTERN = re.compile(r'## \[(\d+\.\d+\.\d+)\]') +MERGED_PR_PATTERN = re.compile( + r'([0-9a-f]{7}) Merge pull request #(\d+) from (.+)/.+' +) +TAG_PATTERN = re.compile( + r'refs/tags/v(\d+\.\d+\.\d+) (\w{3} \w{3} \d{1,2} \d{2}:\d{2}:\d{2} \d{4})' +) + +# PR Type terms +FIX_TERMS = ['fix', 'change', 'update'] + + +# Helper functions +def generate_pr_link(pr_num): + """ + Returns a markdown link to a PR in this repo given its number + """ + return ( + '[PR #{0}](https://github.com/sendgrid/smtpapi-python/pulls/{0})' + ).format(pr_num) + + +def generate_user_link(user): + """ + Returns a markdown link to a user + """ + return '[@{0}](https://github.com/{0})'.format(user) + + +# Get latest tag +command = ['git', 'tag', '--format=%(refname) %(creatordate)'] +res = subprocess.run(command, capture_output=True, text=True) +if res.returncode != 0: + print('Error occurred when running git tag command:', str(res.stderr)) + sys.exit(1) +# Get the last line and get the tag number +latest_release_match = TAG_PATTERN.match( + list(filter(None, res.stdout.split('\n')))[-1], +) +latest_release = latest_release_match[1] +latest_release_date = datetime.strptime( + latest_release_match[2], '%a %b %d %H:%M:%S %Y', +) +print('Generating CHANGELOG for', latest_release) + +# Read in the CHANGELOG file first +with open('CHANGELOG.md') as f: + # Read the text in as a list of lines + old_text = f.readlines() + # Get the latest release (top of the CHANGELOG) + for line in old_text: + match = RELEASE_MD_PATTERN.match(line) + if match: + prev_release = match[1] + break + +if latest_release == prev_release: + print( + 'The latest git tag matches the last release in the CHANGELOG. ' + 'Please tag the repository before running this script.' + ) + sys.exit(1) + +# Use git log to list all commits between that tag and HEAD +command = 'git log --oneline v{}..@'.format(prev_release).split(' ') +res = subprocess.run(command, capture_output=True, text=True) +if res.returncode != 0: + print('Error occurred when running git log command:', str(res.stderr)) + sys.exit(1) + +# Parse the output from the above command to find all commits for merged PRs +merge_commits = [] +for line in res.stdout.split('\n'): + match = MERGED_PR_PATTERN.match(line) + if match: + merge_commits.append(match) + +# Determine the type of PR from the commit message +added, fixes = [], [] +for commit in merge_commits: + # Get the hash of the commit and get the message of it + commit_sha = commit[1] + command = 'git show {} --format=format:%B'.format(commit_sha).split(' ') + res = subprocess.run(command, capture_output=True, text=True) + out = res.stdout.lower() + is_added = True + + # When storing we need the PR title, number and user + data = { + # 3rd line of the commit message is the PR title + 'title': out.split('\n')[2], + 'number': commit[2], + 'user': commit[3], + } + + for term in FIX_TERMS: + if term in out: + fixes.append(data) + is_added = False + break + if is_added: + added.append(data) + +# Now we need to write out the CHANGELOG again +with open('CHANGELOG.md', 'w') as f: + # Write out the header lines first + for i in range(0, 3): + f.write(old_text[i]) + + # Create and write out the new version information + latest_release_date_string = latest_release_date.strftime('%Y-%m-%d') + f.write('## [{}] - {} ##\n'.format( + latest_release, + latest_release_date_string, + )) + # Add the stuff that was added + f.write('### Added\n') + for commit in added: + f.write('- {}: {}{} (via {})\n'.format( + generate_pr_link(commit['number']), + commit['title'], + '.' if commit['title'][-1] != '.' else '', + generate_user_link(commit['user']) + )) + f.write('\n') + # Add the fixes + f.write('### Fixes\n') + for commit in fixes: + f.write('- {}: {}{} (via {})\n'.format( + generate_pr_link(commit['number']), + commit['title'], + '.' if commit['title'][-1] != '.' else '', + generate_user_link(commit['user']) + )) + f.write('\n') + + # Add the old stuff + for i in range(3, len(old_text)): + f.write(old_text[i]) diff --git a/examples/example.py b/examples/example.py index 965a1d0..07c3b15 100644 --- a/examples/example.py +++ b/examples/example.py @@ -1,48 +1,53 @@ # Python 2/3 compatible codebase from __future__ import absolute_import, division, print_function +from smtpapi import SMTPAPIHeader import time +from os import path, sys if __name__ == '__main__' and __package__ is None: - from os import sys, path sys.path.append(path.dirname(path.dirname(path.abspath(__file__)))) - from smtpapi import SMTPAPIHeader -from smtpapi import SMTPAPIHeader header = SMTPAPIHeader() # [To](http://sendgrid.com/docs/API_Reference/SMTP_API/index.html) -#header.add_to('test@example.com') +# header.add_to('test@example.com') header.set_tos(['test1@example.com', 'test2@example.com']) -# [Substitutions](http://sendgrid.com/docs/API_Reference/SMTP_API/substitution_tags.html) -#header.add_substitution('key', 'value') +# [Substitutions] +# (http://sendgrid.com/docs/API_Reference/SMTP_API/substitution_tags.html) +# header.add_substitution('key', 'value') header.set_substitutions({'key': ['value1', 'value2']}) -# [Unique Arguments](http://sendgrid.com/docs/API_Reference/SMTP_API/unique_arguments.html) -#header.add_unique_arg('key', 'value') -header.set_unique_args({'key':'value'}) +# [Unique Arguments] +# (http://sendgrid.com/docs/API_Reference/SMTP_API/unique_arguments.html) +# header.add_unique_arg('key', 'value') +header.set_unique_args({'key': 'value'}) # [Categories](http://sendgrid.com/docs/API_Reference/SMTP_API/categories.html) -#header.add_category('category') +# header.add_category('category') header.set_categories(['category1', 'category2']) # [Sections](http://sendgrid.com/docs/API_Reference/SMTP_API/section_tags.html) -#header.add_section('key', 'section') -header.set_sections({'key1':'section1', 'key2':'section2'}) +# header.add_section('key', 'section') +header.set_sections({'key1': 'section1', 'key2': 'section2'}) -# [Filters](http://sendgrid.com/docs/API_Reference/SMTP_API/apps.html) +# [Filters] +# (http://sendgrid.com/docs/API_Reference/SMTP_API/apps.html) header.add_filter('filter', 'setting', 'value') -# [ASM Group ID](https://sendgrid.com/docs/User_Guide/advanced_suppression_manager.html) +# [ASM Group ID] +# (https://sendgrid.com/docs/User_Guide/advanced_suppression_manager.html) header.set_asm_group_id('value') -# [IP Pools](https://sendgrid.com/docs/API_Reference/Web_API_v3/IP_Management/ip_pools.html) +# [IP Pools] +# (https://sendgrid.com/docs/API_Reference/Web_API_v3/IP_Management/ip_pools.html) header.set_ip_pool("testPool") -# [Scheduling Parameters](https://sendgrid.com/docs/API_Reference/SMTP_API/scheduling_parameters.html) -#header.add_send_each_at(unix_timestamp) # must be a unix timestamp -#header.set_send_each_at([]) # must be a unix timestamp -header.set_send_at(int(time.time())) # must be a unix timestamp +# [Scheduling Parameters] +# (https://sendgrid.com/docs/API_Reference/SMTP_API/scheduling_parameters.html) +# header.add_send_each_at(unix_timestamp) # must be a unix timestamp +# header.set_send_each_at([]) # must be a unix timestamp +header.set_send_at(int(time.time())) # must be a unix timestamp -print(header.json_string()) \ No newline at end of file +print(header.json_string()) diff --git a/setup.py b/setup.py index 81f015d..1777899 100644 --- a/setup.py +++ b/setup.py @@ -1,12 +1,37 @@ +import io +import os +from distutils.file_util import copy_file from setuptools import setup, find_packages + +dir_path = os.path.abspath(os.path.dirname(__file__)) +readme = io.open(os.path.join(dir_path, 'README.rst'), encoding='utf-8').read() +version = io.open( + os.path.join(dir_path, 'VERSION.txt'), + encoding='utf-8', +).read().strip() +copy_file(os.path.join(dir_path, 'VERSION.txt'), + os.path.join(dir_path, 'smtpapi', 'VERSION.txt'), + verbose=0) setup( name='smtpapi', - version='0.3.1', + version=version, author='Yamil Asusta, Kane Kim', author_email='yamil@sendgrid.com, kane.isturm@sendgrid.com', - packages=find_packages(), + url='https://github.com/sendgrid/smtpapi-python/', + packages=find_packages(exclude=["test"]), + include_package_data=True, license='MIT License', description='Simple wrapper to use SendGrid SMTP API', - long_description='Simple wrapper to use SendGrid SMTP API', + long_description=readme, + classifiers=[ + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', + ], ) diff --git a/smtpapi/__init__.py b/smtpapi/__init__.py index 43cbb59..1ed55a7 100644 --- a/smtpapi/__init__.py +++ b/smtpapi/__init__.py @@ -1,13 +1,17 @@ -import json, decimal +import json +import decimal + class _CustomJSONEncoder(json.JSONEncoder): def default(self, o): if isinstance(o, decimal.Decimal): return float(o) - # Provide a fallback to the default encoder if we haven't implemented special support for the object's class + # Provide a fallback to the default encoder if we haven't implemented + # special support for the object's class return super(_CustomJSONEncoder, self).default(o) + class SMTPAPIHeader(object): def __init__(self): @@ -34,40 +38,42 @@ def add_substitution(self, key, value): def set_substitutions(self, subs): self.data['sub'] = subs + def _add_key_value(self, index, key, value): + if index not in self.data: + self.data[index] = {} + self.data[index][key] = value + + def _add_key(self, index, key): + if index not in self.data: + self.data[index] = [] + self.data[index].append(key) + def add_unique_arg(self, key, value): - if 'unique_args' not in self.data: - self.data['unique_args'] = {} - self.data['unique_args'][key] = value + self._add_key_value('unique_args', key, value) def set_unique_args(self, value): self.data['unique_args'] = value def add_category(self, category): - if 'category' not in self.data: - self.data['category'] = [] - self.data['category'].append(category) + self._add_key('category', category) def set_categories(self, category): self.data['category'] = category def add_section(self, key, section): - if 'section' not in self.data: - self.data['section'] = {} - self.data['section'][key] = section + self._add_key_value('section', key, section) def set_sections(self, value): self.data['section'] = value def add_send_each_at(self, time): - if 'send_each_at' not in self.data: - self.data['send_each_at'] = [] - self.data['send_each_at'].append(time) + self._add_key('send_each_at', time) def set_send_each_at(self, time): - self.data['send_each_at'] = time + self.data['send_each_at'] = time def set_send_at(self, time): - self.data['send_at'] = time + self.data['send_at'] = time def add_filter(self, app, setting, val): if 'filters' not in self.data: diff --git a/static/img/github-fork.png b/static/img/github-fork.png new file mode 100644 index 0000000..3c2335f Binary files /dev/null and b/static/img/github-fork.png differ diff --git a/static/img/github-sign-up.png b/static/img/github-sign-up.png new file mode 100644 index 0000000..08766f7 Binary files /dev/null and b/static/img/github-sign-up.png differ diff --git a/test/__init__.py b/test/__init__.py index 315fc88..417fadc 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -1,6 +1,16 @@ -import unittest, json, decimal +import decimal +import json +import os +import datetime + from smtpapi import SMTPAPIHeader +try: + import unittest2 as unittest +except ImportError: + import unittest + + class TestSMTPAPI(unittest.TestCase): def setUp(self): @@ -40,7 +50,11 @@ def test_add(self): def test_set(self): header = SMTPAPIHeader() - header.set_tos(["test@email.com", "test2@email.com", "test3@email.com"]) + header.set_tos([ + "test@email.com", + "test2@email.com", + "test3@email.com", + ]) header.set_substitutions({ "subKey": ["subValue"], "decimalKey": [decimal.Decimal("1.23456789")] @@ -71,6 +85,59 @@ def test_drop_empty(self): header.add_filter('testFilter', 'filter', 'filterValue') self.assertEqual(self.dropsHeader, json.loads(header.json_string())) + def test_license_year(self): + LICENSE_FILE = 'LICENSE' + copyright_line = '' + with open(LICENSE_FILE, 'r') as f: + for line in f: + if line.startswith('Copyright'): + copyright_line = line.strip() + break + self.assertEqual( + 'Copyright (C) %s, Twilio SendGrid, Inc. ' + % datetime.datetime.now().year, + copyright_line + ) + + +class TestRepository(unittest.TestCase): + + def setUp(self): + + self.required_files = [ + './Dockerfile', + './.env_sample', + './PULL_REQUEST_TEMPLATE.md', + './.gitignore', + './CHANGELOG.md', + './CODE_OF_CONDUCT.md', + './CONTRIBUTING.md', + './LICENSE', + './README.rst', + './TROUBLESHOOTING.md', + './USAGE.md', + './VERSION.txt', + ] + + self.file_not_found_message = 'File "{0}" does not exist in repo!' + + def test_repository_files_exists(self): + + for file_path in self.required_files: + if isinstance(file_path, list): + # multiple file paths: assert that any one of the files exists + self.assertTrue( + any(os.path.exists(f) for f in file_path), + msg=self.file_not_found_message.format( + '" or "'.join(file_path) + ), + ) + else: + self.assertTrue( + os.path.exists(file_path), + msg=self.file_not_found_message.format(file_path), + ) + if __name__ == '__main__': unittest.main() diff --git a/test/requirements.txt b/test/requirements.txt new file mode 100644 index 0000000..89035cf --- /dev/null +++ b/test/requirements.txt @@ -0,0 +1,3 @@ +sendgrid +coverage +flake8 diff --git a/test/test_project.py b/test/test_project.py new file mode 100644 index 0000000..888cc41 --- /dev/null +++ b/test/test_project.py @@ -0,0 +1,69 @@ +import os +import unittest + + +class ProjectTests(unittest.TestCase): + + # ./Docker or docker/Docker + def test_docker_dir(self): + self.assertTrue( + os.path.isfile("./Dockerfile") + or os.path.isdir("./docker/Dockerfile") + ) + + # ./docker-compose.yml or ./docker/docker-compose.yml + def test_docker_compose(self): + self.assertTrue( + os.path.isfile('./docker-compose.yml') + or os.path.isfile('./docker/docker-compose.yml') + ) + + # ./.env_sample + def test_env(self): + self.assertTrue(os.path.isfile('./.env_sample')) + + # ./.gitignore + def test_gitignore(self): + self.assertTrue(os.path.isfile('./.gitignore')) + + # ./CHANGELOG.md + def test_changelog(self): + self.assertTrue(os.path.isfile('./CHANGELOG.md')) + + # ./CODE_OF_CONDUCT.md + def test_code_of_conduct(self): + self.assertTrue(os.path.isfile('./CODE_OF_CONDUCT.md')) + + # ./CONTRIBUTING.md + def test_contributing(self): + self.assertTrue(os.path.isfile('./CONTRIBUTING.md')) + + # ./LICENSE + def test_license(self): + self.assertTrue(os.path.isfile('./LICENSE')) + + # ./PULL_REQUEST_TEMPLATE.md + def test_pr_template(self): + self.assertTrue( + os.path.isfile('./PULL_REQUEST_TEMPLATE.md') + ) + + # ./README.rst + def test_readme(self): + self.assertTrue(os.path.isfile('./README.rst')) + + # ./TROUBLESHOOTING.md + def test_troubleshooting(self): + self.assertTrue(os.path.isfile('./TROUBLESHOOTING.md')) + + # ./USAGE.md + def test_usage(self): + self.assertTrue(os.path.isfile('./USAGE.md')) + + # ./VERSION.txt + def test_use_cases(self): + self.assertTrue(os.path.isfile('./VERSION.txt')) + + +if __name__ == '__main__': + unittest.main() diff --git a/twilio_sendgrid_logo.png b/twilio_sendgrid_logo.png new file mode 100644 index 0000000..a4c2223 Binary files /dev/null and b/twilio_sendgrid_logo.png differ diff --git a/use_cases/README.md b/use_cases/README.md new file mode 100644 index 0000000..4d00af9 --- /dev/null +++ b/use_cases/README.md @@ -0,0 +1,3 @@ +This directory provides examples for specific use cases. Please [open an issue](https://github.com/sendgrid/smtpapi-python/issues) or make a pull request for any use cases you would like to see here. Thank you! + +# Table of Contents