├── .github
└── workflows
│ ├── pr-lint.yml
│ └── test-and-deploy.yml
├── .gitignore
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── CSharpHTTPClient
├── App.config
├── CSharpHTTPClient.csproj
├── CSharpHTTPClient.sln
├── Client.cs
├── Properties
│ └── AssemblyInfo.cs
└── SendGridCSharpHTTPClient.nuspec
├── Example
├── .env_sample
├── App.config
├── Example.cs
├── Example.csproj
├── Properties
│ └── AssemblyInfo.cs
└── packages.config
├── FIRST_TIMERS.md
├── LICENSE
├── Makefile
├── PULL_REQUEST_TEMPLATE.md
├── README.md
├── TROUBLESHOOTING.md
├── USAGE.md
├── UnitTest
├── Properties
│ └── AssemblyInfo.cs
├── RequiredFilesExistTest.cs
├── UnitTest.cs
├── UnitTest.csproj
└── packages.config
├── UseCases
└── README.md
├── csharphttpclient.snk
├── static
└── img
│ ├── github-fork.png
│ └── github-sign-up.png
└── twilio_sendgrid_logo.png
/.github/workflows/pr-lint.yml:
--------------------------------------------------------------------------------
1 | name: Lint PR
2 | on:
3 | pull_request_target:
4 | types: [ opened, edited, synchronize, reopened ]
5 |
6 | jobs:
7 | validate:
8 | name: Validate title
9 | runs-on: ubuntu-latest
10 | steps:
11 | - uses: amannn/action-semantic-pull-request@v4
12 | with:
13 | types: chore docs fix feat test misc
14 | env:
15 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
16 |
--------------------------------------------------------------------------------
/.github/workflows/test-and-deploy.yml:
--------------------------------------------------------------------------------
1 | name: Test and Deploy
2 | on:
3 | push:
4 | branches: [ '*' ]
5 | tags: [ '*' ]
6 | pull_request:
7 | branches: [ main ]
8 | schedule:
9 | # Run automatically at 8AM PST Monday-Friday
10 | - cron: '0 15 * * 1-5'
11 | workflow_dispatch:
12 |
13 | jobs:
14 | test:
15 | name: Test
16 | runs-on: ubuntu-latest
17 | timeout-minutes: 20
18 | steps:
19 | - uses: actions/checkout@v2
20 |
21 | - name: Setup .NET Core SDK
22 | uses: actions/setup-dotnet@v1.8.2
23 | with:
24 | dotnet-version: '3.1.x'
25 |
26 | - name: Build & Test
27 | run: make test
28 | - run: bash <(curl -s https://codecov.io/bash)
29 | deploy:
30 | name: Deploy
31 | if: success() && github.ref_type == 'tag'
32 | needs: [ test ]
33 | runs-on: ubuntu-latest
34 | steps:
35 | - uses: actions/checkout@v2
36 |
37 | - name: Setup .NET Core SDK
38 | uses: actions/setup-dotnet@v1.8.2
39 | with:
40 | dotnet-version: '3.1.x'
41 |
42 | - name: Create GitHub Release
43 | uses: sendgrid/dx-automator/actions/release@main
44 | with:
45 | footer: '**[NuGet](https://www.nuget.org/packages/SendGrid.CSharp.HTTP.Client/${version})**'
46 | env:
47 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
48 |
49 | - name: Publish package to NuGet
50 | run: |
51 | make test
52 | dotnet nuget push **/SendGrid*.nupkg -k ${{ secrets.NUGET_API_KEY }} -s https://api.nuget.org/v3/index.json
53 |
54 | - name: Submit metric to Datadog
55 | uses: sendgrid/dx-automator/actions/datadog-release-metric@main
56 | env:
57 | DD_API_KEY: ${{ secrets.DATADOG_API_KEY }}
58 |
59 | notify-on-failure:
60 | name: Slack notify on failure
61 | if: failure() && github.event_name != 'pull_request' && (github.ref == 'refs/heads/main' || github.ref_type == 'tag')
62 | needs: [ test, deploy ]
63 | runs-on: ubuntu-latest
64 | steps:
65 | - uses: rtCamp/action-slack-notify@v2
66 | env:
67 | SLACK_COLOR: failure
68 | SLACK_ICON_EMOJI: ':github:'
69 | 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) }}
70 | SLACK_TITLE: Action Failure - ${{ github.repository }}
71 | SLACK_USERNAME: GitHub Actions
72 | SLACK_MSG_AUTHOR: twilio-dx
73 | SLACK_FOOTER: Posted automatically using GitHub Actions
74 | SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
75 | MSG_MINIMAL: true
76 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | CSharpHTTPClient/CSharpHTTPClient.sln.DotSettings.user
2 | CSharpHTTPClient/bin/
3 | Example/bin/
4 | CSharpHTTPClient/obj/
5 | Example/obj/
6 | UnitTest/bin/
7 | UnitTest/obj/
8 | *.suo
9 | CSharpHTTPClient/*/bin/
10 | CSharpHTTPClient/*/obj/
11 | .DS_store
12 | CSharpHTTPClient/CSharpHTTPClient.userprefs
13 | CSharpHTTPClient/packages/
14 | CSharpHTTPClient/CSharpHTTPClient.sln.VisualState.xml
15 | *.PublicKey
16 | *.pfx
17 | .vs
18 | .history
19 |
20 | # Environment files
21 | .env/*.*
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 | All notable changes to this project will be documented in this file.
3 |
4 | This project adheres to [Semantic Versioning](http://semver.org/).
5 |
6 | [2022-03-09] Version 3.4.12
7 | ---------------------------
8 | **Library - Chore**
9 | - [PR #109](https://github.com/sendgrid/csharp-http-client/pull/109): push Datadog Release Metric upon deploy success. Thanks to [@eshanholtz](https://github.com/eshanholtz)!
10 |
11 |
12 | [2022-02-09] Version 3.4.11
13 | ---------------------------
14 | **Library - Chore**
15 | - [PR #108](https://github.com/sendgrid/csharp-http-client/pull/108): add gh release to workflow. Thanks to [@shwetha-manvinkurke](https://github.com/shwetha-manvinkurke)!
16 | - [PR #107](https://github.com/sendgrid/csharp-http-client/pull/107): merge test and deploy workflows. Thanks to [@shwetha-manvinkurke](https://github.com/shwetha-manvinkurke)!
17 |
18 |
19 | [2022-01-12] Version 3.4.10
20 | ---------------------------
21 | **Library - Chore**
22 | - [PR #106](https://github.com/sendgrid/csharp-http-client/pull/106): update license year. Thanks to [@JenniferMah](https://github.com/JenniferMah)!
23 |
24 |
25 | [2021-12-01] Version 3.4.9
26 | --------------------------
27 | **Library - Chore**
28 | - [PR #105](https://github.com/sendgrid/csharp-http-client/pull/105): fix gh action release workflow. Thanks to [@eshanholtz](https://github.com/eshanholtz)!
29 | - [PR #104](https://github.com/sendgrid/csharp-http-client/pull/104): migrate to GitHub Actions. Thanks to [@eshanholtz](https://github.com/eshanholtz)!
30 |
31 |
32 | [2020-08-19] Version 3.4.8
33 | --------------------------
34 | **Library - Chore**
35 | - [PR #101](https://github.com/sendgrid/csharp-http-client/pull/101): update GitHub branch references to use HEAD. Thanks to [@thinkingserious](https://github.com/thinkingserious)!
36 |
37 |
38 | [2020-04-15] Version 3.4.7
39 | --------------------------
40 | **Library - Docs**
41 | - [PR #100](https://github.com/sendgrid/csharp-http-client/pull/100): baseline all the templated markdown docs. Thanks to [@childish-sambino](https://github.com/childish-sambino)!
42 |
43 |
44 | [2020-02-19] Version 3.4.6
45 | --------------------------
46 | **Library - Chore**
47 | - [PR #99](https://github.com/sendgrid/csharp-http-client/pull/99): remove unused function. Thanks to [@thinkingserious](https://github.com/thinkingserious)!
48 |
49 |
50 | [2020-02-05] Version 3.4.5
51 | --------------------------
52 | **Library - Chore**
53 | - [PR #98](https://github.com/sendgrid/csharp-http-client/pull/98): add license to nuspec. Thanks to [@thinkingserious](https://github.com/thinkingserious)!
54 |
55 |
56 | [2020-01-29] Version 3.4.4
57 | --------------------------
58 | **Library - Fix**
59 | - [PR #97](https://github.com/sendgrid/csharp-http-client/pull/97): update deploy path for .nupkg. Thanks to [@thinkingserious](https://github.com/thinkingserious)!
60 |
61 |
62 | [2020-01-29] Version 3.4.3
63 | --------------------------
64 | **Library - Chore**
65 | - [PR #96](https://github.com/sendgrid/csharp-http-client/pull/96): add nuget pack. Thanks to [@thinkingserious](https://github.com/thinkingserious)!
66 |
67 |
68 | [2020-01-28] Version 3.4.2
69 | --------------------------
70 | **Library - Chore**
71 | - [PR #95](https://github.com/sendgrid/csharp-http-client/pull/95): updates for auto-deploy via Travis CI. Thanks to [@thinkingserious](https://github.com/thinkingserious)!
72 | - [PR #93](https://github.com/sendgrid/csharp-http-client/pull/93): prep the repo for automated releasing. Thanks to [@thinkingserious](https://github.com/thinkingserious)!
73 | - [PR #71](https://github.com/sendgrid/csharp-http-client/pull/71): Adds a Deploy Travis task that publishes to NuGet. Thanks to [@Gimly](https://github.com/Gimly)!
74 |
75 | **Library - Fix**
76 | - [PR #94](https://github.com/sendgrid/csharp-http-client/pull/94): nuget push flag. Thanks to [@thinkingserious](https://github.com/thinkingserious)!
77 |
78 |
79 | [2019-08-14] Version 3.4.1
80 | --------------------------
81 | ### Added
82 | - [PR #90](https://github.com/sendgrid/csharp-http-client/pull/90): Twilio branding updates.
83 |
84 | ## [3.4.0] - 2019-08-14
85 | ### Added
86 | - [PR #26](https://github.com/sendgrid/csharp-http-client/pull/26): Add a Code Of Conduct. Big thanks to [Henrik Bergqvist](https://github.com/hbbq) for the pull request!
87 | - [PR #31](https://github.com/sendgrid/csharp-http-client/pull/31): Update README.md - moved logo to top, added license and more badges. Big thanks to [Alex](https://github.com/pushkyn) for the pull request!
88 | - [PR #32](https://github.com/sendgrid/csharp-http-client/pull/32): Add more SEO Friendly Section links. Big thanks to [Alex](https://github.com/pushkyn) for the pull request!
89 | - [PR #28](https://github.com/sendgrid/csharp-http-client/pull/28): Add an example of displaying request headers on Client. Big thanks to [Richard D](https://github.com/dutts) for the pull request!
90 | - [PR #38](https://github.com/sendgrid/csharp-http-client/pull/38): Add GitHub PR template. Big thanks to [Alex](https://github.com/pushkyn) for the pull request!
91 | - [PR #52](https://github.com/sendgrid/csharp-http-client/pull/52): Added unittest to check for specific repo files. Big thanks to [Manjiri Tapaswi](https://github.com/mptap) for the pull request!
92 | - [PR #42](https://github.com/sendgrid/csharp-http-client/pull/42): Added example file, updated .gitignore and README.md. Big thanks to [Diego Rocha](https://github.com/dhsrocha) for the pull request!
93 | - [PR #43](https://github.com/sendgrid/csharp-http-client/pull/43): Add USAGE.md. Big thanks to [Rohit Daryanani](https://github.com/rohitdaryanani) for the pull request!
94 | - [PR #68](https://github.com/sendgrid/csharp-http-client/pull/68): Created a UseCases directory. Big thanks to [Ed Parry](https://github.com/ed-parry) for the pull request!
95 | - [PR #78](https://github.com/sendgrid/csharp-http-client/pull/78): Document new Git workflow. Big thanks to [Shivam Agarwal](https://github.com/gr8shivam) for the pull request!
96 | - [PR #74](https://github.com/sendgrid/csharp-http-client/pull/74): Add first-timers.md file for newcomers. Big thanks to [Ely Alamillo](https://github.com/ely-alamillo) for the pull request!
97 | - [PR #62](https://github.com/sendgrid/csharp-http-client/pull/62): Added CodeCov support. Big thanks to [Zac Marcus](https://github.com/ZacMarcus) for the pull request!
98 | - [PR #60](https://github.com/sendgrid/csharp-http-client/pull/60): Add Code Review to Contributing.md. Big thanks to [Derek Neuland](https://github.com/derekneuland) for the pull request!
99 |
100 | ### Fixed
101 | - [PR #24](https://github.com/sendgrid/csharp-http-client/pull/24): Typo in README.md. Big thanks to [Cícero Pablo](https://github.com/ciceropablo) for the pull request!
102 | - [PR #35](https://github.com/sendgrid/csharp-http-client/pull/35): Typo in CONTRIBUTING.md. Big thanks to [Alex](https://github.com/pushkyn) for the pull request!
103 | - [PR #49](https://github.com/sendgrid/csharp-http-client/pull/49): Update the LICENSE copyright year. Big thanks to [Alex](https://github.com/pushkyn) for the pull request!
104 | - [PR #50](https://github.com/sendgrid/csharp-http-client/pull/50): Update the LICENSE date range and add a unit test. Big thanks to [Dawid Treściński](https://github.com/blitzerpl) for the pull request!
105 | - [PR #53](https://github.com/sendgrid/csharp-http-client/pull/53): Make sure LICENSE file test works cross-platform. Big thanks to [Adlan Razalan](https://github.com/adlan) for the pull request!
106 | - [PR #65](https://github.com/sendgrid/csharp-http-client/pull/65): Update license year. Big thanks to [Alex](https://github.com/pushkyn) for the pull request!
107 | - [PR #85](https://github.com/sendgrid/csharp-http-client/pull/85): Fix test looking for LICENSE.txt. Big thanks to [Ashley Roach](https://github.com/aroach) for the pull request!
108 | - [PR #84](https://github.com/sendgrid/csharp-http-client/pull/84): Fix link to license in the README. Big thanks to [Pranjal Vyas](https://github.com/vyaspranjal33) for the pull request!
109 | - [PR #83](https://github.com/sendgrid/csharp-http-client/pull/83): Fix link to feature requests in the README. Big thanks to [Sanjay Singh](https://github.com/sanjaysingh) for the pull request!
110 | - [PR #82](https://github.com/sendgrid/csharp-http-client/pull/82): Fix location of LICENSE.md file. Big thanks to [Chandler Weiner](https://github.com/crweiner) for the pull request!
111 | - [PR #77](https://github.com/sendgrid/csharp-http-client/pull/77): Fix tests looking for wrong file type for License. Big thanks to [Anna](https://github.com/AnnaDodson) for the pull request!
112 | - [PR #70](https://github.com/sendgrid/csharp-http-client/pull/70): Corrected *.md files using Grammarly. Big thanks to [Alex](https://github.com/pushkyn) for the pull request!
113 |
114 | ## [3.3.0] - 2017-04-17
115 | ### Added
116 | - #22: Added cancellation of requests using Cancellation Token
117 | - Big thanks to [aKzenT](https://github.com/aKzenT) for the pull request!
118 |
119 | ## [3.2.0] - 2017-04-11
120 | ### Added
121 | - #23: Timeout Parameter
122 | - Big thanks to [PandaBoy00](https://github.com/PandaBoy00) for the pull request!
123 |
124 | ## [3.1.0] - 2017-03-01
125 | ### Added
126 | - [PR #18](https://github.com/sendgrid/csharp-http-client/pull/18): Cache default httpclient
127 | - Big thanks to [Niels Timmermans](https://github.com/nillis) for the pull request!
128 |
129 | ## [3.0.0] - 2016-07-22
130 | ### BREAKING CHANGE
131 | - While your code may continue to work as before, the async behavior has changed, as we don't block on `Result` anymore
132 | - Fixes [issue #259](https://github.com/sendgrid/sendgrid-csharp/issues/259) in the sendgrid-csharp library
133 | - Updated examples and README to demonstrate await usage
134 |
135 | ## [2.0.7] - 2016-07-19
136 | ### Added
137 | - [Pull request #11](https://github.com/sendgrid/csharp-http-client/pull/11): Adding the option to set WebProxy object to be used on HttpClient
138 | - Big thanks to [Juliano Nunes](https://github.com/julianonunes) for the pull request!
139 |
140 | ## [2.0.6] - 2016-07-18
141 | ### Added
142 | - Sign assembly with a strong name
143 |
144 | ## [2.0.5] - 2016-07-14
145 | ### Fixed
146 | - Solves [issue #7](https://github.com/sendgrid/csharp-http-client/issues/7)
147 | - Solves [issue #256](https://github.com/sendgrid/sendgrid-csharp/issues/256) in the SendGrid C# Client
148 | - Do not try to encode the JSON request payload by replacing single quotes with double quotes
149 | - Updated examples and README to use JSON.NET to encode the payload
150 | - Thanks to [Gunnar Liljas](https://github.com/gliljas) for helping identify the issue!
151 |
152 | ## [2.0.2] - 2016-06-16
153 | ### Added
154 | - Fix async, per https://github.com/sendgrid/sendgrid-csharp/issues/235
155 |
156 | ## [2.0.1] - 2016-06-03
157 | ### Added
158 | - Sign assembly with a strong name
159 |
160 | ## [2.0.0] - 2016-06-03
161 | ### Changed
162 | - Made the Response variables non-redundant. e.g. response.ResponseBody becomes response.Body
163 |
164 | ## [1.0.2] - 2016-03-17
165 | ### Added
166 | - We are live!
167 |
168 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, sex characteristics, gender identity and expression,
9 | level of experience, education, socio-economic status, nationality, personal
10 | appearance, race, religion, or sexual identity and orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | - Using welcoming and inclusive language
18 | - Being respectful of differing viewpoints and experiences
19 | - Gracefully accepting constructive criticism
20 | - Focusing on what is best for the community
21 | - Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | - The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | - Trolling, insulting/derogatory comments, and personal or political attacks
28 | - Public or private harassment
29 | - Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | - Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at open-source@twilio.com. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
72 |
73 | [homepage]: https://www.contributor-covenant.org
74 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | 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.
2 |
3 | All third party contributors acknowledge that any contributions they provide will be made under the same open source license that the open source project is provided under.
4 |
5 | - [Improvements to the Codebase](#improvements-to-the-codebase)
6 | - [Understanding the Code Base](#understanding-the-codebase)
7 | - [Testing](#testing)
8 | - [Style Guidelines & Naming Conventions](#style-guidelines-and-naming-conventions)
9 | - [Creating a Pull Request](#creating-a-pull-request)
10 | - [Code Reviews](#code-reviews)
11 |
12 | There are a few ways to contribute, which we'll enumerate below:
13 |
14 |
15 | ## Improvements to the Codebase
16 |
17 | We welcome direct contributions to the csharp-http-client code base. Thank you!
18 |
19 | ### Development Environment ###
20 |
21 | #### Install and Run Locally ####
22 |
23 | ##### Prerequisites #####
24 |
25 | - .NET version 4.5.2
26 | - Microsoft Visual Studio Community 2015 or greater
27 |
28 | ##### Initial setup: #####
29 |
30 | ```bash
31 | git clone https://github.com/sendgrid/csharp-http-client.git
32 | ```
33 |
34 | Open `csharp-http-client/CSharpHTTPClient/CSharpHTTPClient.sln`
35 |
36 | ##### Execute: #####
37 |
38 | SSee the [Example project](Example) to get started quickly.
39 |
40 |
41 | ## Understanding the Code Base
42 |
43 | **/Example/Example.cs**
44 |
45 | Working examples that demonstrate usage.
46 |
47 | **/CSharpHTTPClient/Client.cs**
48 |
49 | An HTTP client with a fluent interface using method chaining and reflection. By returning a new object on [TryGetMember](CSharpHTTPClient/Client.cs#L191) and [_()](CSharpHTTPClient/Client.cs#L180), we can dynamically build the URL using method chaining and [TryGetMember](CSharpHTTPClient/Client.cs#L191) allows us to dynamically receive the method calls to achieve reflection.
50 |
51 | This allows for the following mapping from a URL to a method chain:
52 |
53 | `/api_client/{api_key_id}/version` maps to `client.api_client._(api_key_id).version.()` where is a supported [Method](CSharpHTTPClient/Client.cs#L71).
54 |
55 |
56 | ## Testing
57 |
58 | All PRs require passing tests before the PR will be reviewed.
59 |
60 | All test files are in the [`UnitTest`](UnitTest) directory.
61 |
62 | For the purposes of contributing to this repo, please update the [`UnitTest.cs`](UnitTest/UnitTest.cs) file with unit tests as you modify the code.
63 |
64 | From the Visual Studio menu: `Tests->Run->All Tests`
65 |
66 |
67 | ## Style Guidelines & Naming Conventions
68 |
69 | Generally, we follow the style guidelines as suggested by the official language. However, we ask that you conform to the styles that already exist in the library. If you wish to deviate, please explain your reasoning. In this case, we generally follow the [C# Naming Conventions](https://msdn.microsoft.com/library/ms229045(v=vs.100).aspx) and the suggestions provided by the Visual Studio IDE.
70 |
71 |
72 | ## Creating a Pull Request
73 |
74 | 1. [Fork](https://help.github.com/fork-a-repo/) the project, clone your fork,
75 | and configure the remotes:
76 |
77 | ```bash
78 | # Clone your fork of the repo into the current directory
79 | git clone https://github.com/sendgrid/csharp-http-client
80 | # Navigate to the newly cloned directory
81 | cd csharp-http-client
82 | # Assign the original repo to a remote called "upstream"
83 | git remote add upstream https://github.com/sendgrid/csharp-http-client
84 | ```
85 |
86 | 2. If you cloned a while ago, get the latest changes from upstream:
87 |
88 | ```bash
89 | git checkout development
90 | git pull upstream development
91 | ```
92 |
93 | 3. Create a new topic branch (off the main project development branch) to
94 | contain your feature, change, or fix:
95 |
96 | ```bash
97 | git checkout -b development
98 | ```
99 |
100 | 4. Commit your changes in logical chunks. Please adhere to these [git commit
101 | message guidelines](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html)
102 | or your code is unlikely to be merged into the main project. Use Git's
103 | [interactive rebase](https://help.github.com/articles/interactive-rebase)
104 | feature to tidy up your commits before making them public.
105 |
106 | 4a. Create tests.
107 |
108 | 4b. Create or update the example code that demonstrates the functionality of this change to the code.
109 |
110 | 5. Locally merge (or rebase) the upstream development branch into your topic branch:
111 |
112 | ```bash
113 | git pull [--rebase] upstream development
114 | ```
115 |
116 | 6. Push your topic branch up to your fork:
117 |
118 | ```bash
119 | git push origin
120 | ```
121 |
122 | 7. [Open a Pull Request](https://help.github.com/articles/using-pull-requests/)
123 | with a clear title and description against the `main` branch. All tests must be passing before we will review the PR.
124 |
125 | ## Code Reviews
126 | 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.
127 |
--------------------------------------------------------------------------------
/CSharpHTTPClient/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/CSharpHTTPClient/CSharpHTTPClient.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Twilio
6 | A Simple Fluent REST API Client.
7 |
8 |
9 | Debug
10 | AnyCPU
11 | {26C4841F-EC62-4EC7-B16E-3A7386EA36DC}
12 | Library
13 | Properties
14 | SendGrid.CSharp.HTTP.Client
15 | SendGrid.CSharp.HTTP.Client
16 | v4.5
17 | 512
18 | true
19 |
20 |
21 |
22 | AnyCPU
23 | true
24 | full
25 | false
26 | bin\Debug\
27 | DEBUG;TRACE
28 | prompt
29 | 4
30 |
31 |
32 | AnyCPU
33 | pdbonly
34 | true
35 | bin\Release\
36 | TRACE
37 | prompt
38 | 4
39 |
40 |
41 |
42 |
43 |
44 | true
45 |
46 |
47 | ../csharphttpclient.snk
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
78 |
79 |
--------------------------------------------------------------------------------
/CSharpHTTPClient/CSharpHTTPClient.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 14
4 | VisualStudioVersion = 14.0.23107.0
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CSharpHTTPClient", "CSharpHTTPClient.csproj", "{26C4841F-EC62-4EC7-B16E-3A7386EA36DC}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example", "..\Example\Example.csproj", "{490AD14D-B821-435A-BEC8-F4DFE34E6556}"
9 | EndProject
10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTest", "..\UnitTest\UnitTest.csproj", "{DF845C59-4B39-4A8A-AC89-E5336B57076B}"
11 | EndProject
12 | Global
13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
14 | Debug|Any CPU = Debug|Any CPU
15 | Release|Any CPU = Release|Any CPU
16 | EndGlobalSection
17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
18 | {26C4841F-EC62-4EC7-B16E-3A7386EA36DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
19 | {26C4841F-EC62-4EC7-B16E-3A7386EA36DC}.Debug|Any CPU.Build.0 = Debug|Any CPU
20 | {26C4841F-EC62-4EC7-B16E-3A7386EA36DC}.Release|Any CPU.ActiveCfg = Release|Any CPU
21 | {26C4841F-EC62-4EC7-B16E-3A7386EA36DC}.Release|Any CPU.Build.0 = Release|Any CPU
22 | {490AD14D-B821-435A-BEC8-F4DFE34E6556}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
23 | {490AD14D-B821-435A-BEC8-F4DFE34E6556}.Debug|Any CPU.Build.0 = Debug|Any CPU
24 | {490AD14D-B821-435A-BEC8-F4DFE34E6556}.Release|Any CPU.ActiveCfg = Release|Any CPU
25 | {490AD14D-B821-435A-BEC8-F4DFE34E6556}.Release|Any CPU.Build.0 = Release|Any CPU
26 | {DF845C59-4B39-4A8A-AC89-E5336B57076B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27 | {DF845C59-4B39-4A8A-AC89-E5336B57076B}.Debug|Any CPU.Build.0 = Debug|Any CPU
28 | {DF845C59-4B39-4A8A-AC89-E5336B57076B}.Release|Any CPU.ActiveCfg = Release|Any CPU
29 | {DF845C59-4B39-4A8A-AC89-E5336B57076B}.Release|Any CPU.Build.0 = Release|Any CPU
30 | EndGlobalSection
31 | GlobalSection(SolutionProperties) = preSolution
32 | HideSolutionNode = FALSE
33 | EndGlobalSection
34 | EndGlobal
35 |
--------------------------------------------------------------------------------
/CSharpHTTPClient/Client.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Dynamic;
4 | using System.Linq;
5 | using System.Net;
6 | using System.Net.Http;
7 | using System.Net.Http.Headers;
8 | using System.Text;
9 | using System.Threading.Tasks;
10 | using System.Web.Script.Serialization;
11 | using System.Web;
12 | using System.Threading;
13 |
14 | namespace SendGrid.CSharp.HTTP.Client
15 | {
16 | public class Response
17 | {
18 | public HttpStatusCode StatusCode;
19 | public HttpContent Body;
20 | public HttpResponseHeaders Headers;
21 |
22 | ///
23 | /// Holds the response from an API call.
24 | ///
25 | /// https://msdn.microsoft.com/en-us/library/system.net.httpstatuscode(v=vs.110).aspx
26 | /// https://msdn.microsoft.com/en-us/library/system.net.http.httpcontent(v=vs.118).aspx
27 | /// https://msdn.microsoft.com/en-us/library/system.net.http.headers.httpresponseheaders(v=vs.118).aspx
28 | public Response(HttpStatusCode statusCode, HttpContent responseBody, HttpResponseHeaders responseHeaders)
29 | {
30 | StatusCode = statusCode;
31 | Body = responseBody;
32 | Headers = responseHeaders;
33 | }
34 |
35 | ///
36 | /// Converts string formatted response body to a Dictionary.
37 | ///
38 | /// https://msdn.microsoft.com/en-us/library/system.net.http.httpcontent(v=vs.118).aspx
39 | /// Dictionary object representation of HttpContent
40 | public virtual Dictionary DeserializeResponseBody(HttpContent content)
41 | {
42 | JavaScriptSerializer jss = new JavaScriptSerializer();
43 | var dsContent = jss.Deserialize>(content.ReadAsStringAsync().Result);
44 | return dsContent;
45 | }
46 |
47 | ///
48 | /// Converts string formatted response headers to a Dictionary.
49 | ///
50 | /// https://msdn.microsoft.com/en-us/library/system.net.http.headers.httpresponseheaders(v=vs.118).aspx
51 | /// Dictionary object representation of HttpRepsonseHeaders
52 | public virtual Dictionary DeserializeResponseHeaders(HttpResponseHeaders content)
53 | {
54 | var dsContent = new Dictionary();
55 | foreach (var pair in content)
56 | {
57 | dsContent.Add(pair.Key, pair.Value.First());
58 | }
59 | return dsContent;
60 | }
61 |
62 | }
63 |
64 |
65 | public class Client : DynamicObject
66 | {
67 | public string Host;
68 | public Dictionary RequestHeaders;
69 | public string Version;
70 | public string UrlPath;
71 | public string MediaType;
72 | public WebProxy WebProxy;
73 | public TimeSpan Timeout;
74 |
75 | public enum Methods
76 | {
77 | DELETE, GET, PATCH, POST, PUT
78 | }
79 |
80 | private int TimeoutDefault = 10;
81 |
82 | ///
83 | /// REST API client.
84 | ///
85 | /// Base url (e.g. https://api.sendgrid.com)
86 | /// A dictionary of request headers
87 | /// API version, override AddVersion to customize
88 | /// Path to endpoint (e.g. /path/to/endpoint)
89 | /// Fluent interface to a REST API
90 | public Client(WebProxy webProxy, string host, Dictionary requestHeaders = null, string version = null, string urlPath = null)
91 | : this(host, requestHeaders, version, urlPath)
92 | {
93 | WebProxy = webProxy;
94 | }
95 |
96 |
97 | ///
98 | /// REST API client.
99 | ///
100 | /// Base url (e.g. https://api.sendgrid.com)
101 | /// A dictionary of request headers
102 | /// API version, override AddVersion to customize
103 | /// Path to endpoint (e.g. /path/to/endpoint)
104 | /// Set an Timeout parameter for the HTTP Client
105 | /// Fluent interface to a REST API
106 | public Client(string host, Dictionary requestHeaders = null, string version = null, string urlPath = null, TimeSpan? timeOut = null)
107 | {
108 | Host = host;
109 | if(requestHeaders != null)
110 | {
111 | AddRequestHeader(requestHeaders);
112 | }
113 | Version = (version != null) ? version : null;
114 | UrlPath = (urlPath != null) ? urlPath : null;
115 | Timeout = (timeOut != null) ? (TimeSpan)timeOut : TimeSpan.FromSeconds(TimeoutDefault);
116 | }
117 |
118 | ///
119 | /// Add requestHeaders to the API call
120 | ///
121 | /// A dictionary of request headers
122 | public void AddRequestHeader(Dictionary requestHeaders)
123 | {
124 | RequestHeaders = (RequestHeaders != null)
125 | ? RequestHeaders.Union(requestHeaders).ToDictionary(pair => pair.Key, pair => pair.Value) : requestHeaders;
126 | }
127 |
128 | ///
129 | /// Build the final URL
130 | ///
131 | /// A string of JSON formatted query parameters (e.g {'param': 'param_value'})
132 | /// Final URL
133 | private string BuildUrl(string queryParams = null)
134 | {
135 | string endpoint = null;
136 |
137 | if( Version != null)
138 | {
139 | endpoint = Host + "/" + Version + UrlPath;
140 | }
141 | else
142 | {
143 | endpoint = Host + UrlPath;
144 | }
145 |
146 | if (queryParams != null)
147 | {
148 | JavaScriptSerializer jss = new JavaScriptSerializer();
149 | var ds_query_params = jss.Deserialize>(queryParams);
150 | var query = HttpUtility.ParseQueryString(string.Empty);
151 | foreach (var pair in ds_query_params)
152 | {
153 | query[pair.Key] = pair.Value.ToString();
154 | }
155 | string queryString = query.ToString();
156 | endpoint = endpoint + "?" + queryString;
157 | }
158 |
159 | return endpoint;
160 | }
161 |
162 | ///
163 | /// Create a new Client object for method chaining
164 | ///
165 | /// Name of url segment to add to the URL
166 | /// A new client object with "name" added to the URL
167 | private Client BuildClient(string name = null)
168 | {
169 | string endpoint;
170 |
171 | if (name != null)
172 | {
173 | endpoint = UrlPath + "/" + name;
174 | }
175 | else
176 | {
177 | endpoint = UrlPath;
178 | }
179 |
180 | UrlPath = null; // Reset the current object's state before we return a new one
181 | return new Client(Host, RequestHeaders, Version, endpoint, Timeout);
182 |
183 | }
184 |
185 | ///
186 | /// Add the authorization header, override to customize
187 | ///
188 | /// Authorization header
189 | /// Authorization value to add to the header
190 | public virtual AuthenticationHeaderValue AddAuthorization(KeyValuePair header)
191 | {
192 | string[] split = header.Value.Split(new char[0]);
193 | return new AuthenticationHeaderValue(split[0], split[1]);
194 | }
195 |
196 | ///
197 | /// Add the version of the API, override to customize
198 | ///
199 | /// Version string to add to the URL
200 | public virtual void AddVersion(string version)
201 | {
202 | Version = version;
203 | }
204 |
205 | ///
206 | /// Deal with special cases and URL parameters
207 | ///
208 | /// Name of URL segment
209 | /// A new client object with "name" added to the URL
210 | public Client _(string name)
211 | {
212 | return BuildClient(name);
213 | }
214 |
215 | ///
216 | /// Reflection. We capture undefined variable access here
217 | ///
218 | /// The calling object properties
219 | /// The callback
220 | /// The callback returns a new client object with "name" added to the URL
221 | public override bool TryGetMember(GetMemberBinder binder, out object result)
222 | {
223 | result = BuildClient(binder.Name);
224 | return true;
225 | }
226 |
227 | ///
228 | /// Reflection. We capture the final method call here
229 | ///
230 | /// The calling object properties
231 | /// The calling object's arguements
232 | /// If "version", returns new client with version attached
233 | /// If "method", returns a Response object
234 | /// The callback is described in "result"
235 | public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
236 | {
237 | if (binder.Name == "version")
238 | {
239 | AddVersion(args[0].ToString());
240 | result = BuildClient();
241 | return true;
242 | }
243 |
244 | if( Enum.IsDefined(typeof(Methods), binder.Name.ToUpper()))
245 | {
246 | CancellationToken cancellationToken = CancellationToken.None;
247 | string queryParams = null;
248 | string requestBody = null;
249 | int i = 0;
250 |
251 | foreach (object obj in args)
252 | {
253 | string name = binder.CallInfo.ArgumentNames.Count > i ?
254 | binder.CallInfo.ArgumentNames[i] : null;
255 | if (name == "queryParams")
256 | {
257 | queryParams = obj.ToString();
258 | }
259 | else if (name == "requestBody")
260 | {
261 | requestBody = obj.ToString();
262 | }
263 | else if (name == "requestHeaders")
264 | {
265 | AddRequestHeader((Dictionary)obj);
266 | }
267 | else if (name == "cancellationToken")
268 | {
269 | cancellationToken = (CancellationToken)obj;
270 | }
271 | i++;
272 | }
273 | result = RequestAsync(binder.Name.ToUpper(), requestBody: requestBody, queryParams: queryParams, cancellationToken: cancellationToken).ConfigureAwait(false);
274 | return true;
275 | }
276 | else
277 | {
278 | result = null;
279 | return false;
280 | }
281 |
282 | }
283 |
284 | ///
285 | /// Make the call to the API server, override for testing or customization
286 | ///
287 | /// Client object ready for communication with API
288 | /// The parameters for the API call
289 | /// A token that allows cancellation of the http request
290 | /// Response object
291 | public async virtual Task MakeRequest(HttpClient client, HttpRequestMessage request, CancellationToken cancellationToken = default(CancellationToken))
292 | {
293 |
294 | HttpResponseMessage response = await client.SendAsync(request, cancellationToken).ConfigureAwait(false);
295 | return new Response(response.StatusCode, response.Content, response.Headers);
296 | }
297 |
298 | ///
299 | /// Prepare for async call to the API server
300 | ///
301 | /// HTTP verb
302 | /// A token that allows cancellation of the http request
303 | /// JSON formatted string
304 | /// JSON formatted queary paramaters
305 | /// Response object
306 | private async Task RequestAsync(string method, String requestBody = null, String queryParams = null, CancellationToken cancellationToken = default(CancellationToken))
307 | {
308 | using (var client = new HttpClient())
309 | {
310 | try
311 | {
312 | // Build the URL
313 | client.BaseAddress = new Uri(Host);
314 | client.Timeout = Timeout;
315 | string endpoint = BuildUrl(queryParams);
316 |
317 |
318 | // Build the request headers
319 | client.DefaultRequestHeaders.Accept.Clear();
320 | if(RequestHeaders != null)
321 | {
322 | foreach (KeyValuePair header in RequestHeaders)
323 | {
324 | if (header.Key == "Authorization")
325 | {
326 | client.DefaultRequestHeaders.Authorization = AddAuthorization(header);
327 | }
328 | else if (header.Key == "Content-Type")
329 | {
330 | MediaType = header.Value;
331 | client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(MediaType));
332 | }
333 | else
334 | {
335 | client.DefaultRequestHeaders.Add(header.Key, header.Value);
336 | }
337 | }
338 | }
339 |
340 | // Build the request body
341 | StringContent content = null;
342 | if (requestBody != null)
343 | {
344 | content = new StringContent(requestBody, Encoding.UTF8, MediaType);
345 | }
346 |
347 | // Build the final request
348 | HttpRequestMessage request = new HttpRequestMessage
349 | {
350 | Method = new HttpMethod(method),
351 | RequestUri = new Uri(endpoint),
352 | Content = content
353 | };
354 | return await MakeRequest(client, request, cancellationToken).ConfigureAwait(false);
355 |
356 | }
357 | catch(TaskCanceledException)
358 | {
359 | throw;
360 | }
361 | catch (Exception ex)
362 | {
363 | HttpResponseMessage response = new HttpResponseMessage();
364 | string message;
365 | message = (ex is HttpRequestException) ? ".NET HttpRequestException" : ".NET Exception";
366 | message = message + ", raw message: \n\n";
367 | response.Content = new StringContent(message + ex.Message);
368 | return new Response(response.StatusCode, response.Content, response.Headers);
369 | }
370 | }
371 | }
372 | }
373 | }
--------------------------------------------------------------------------------
/CSharpHTTPClient/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.InteropServices;
3 |
4 | // General Information about an assembly is controlled through the following
5 | // set of attributes. Change these attribute values to modify the information
6 | // associated with an assembly.
7 | [assembly: AssemblyTitle("SendGrid.CSharp.HTTP.Client")]
8 | [assembly: AssemblyDescription("A Simple Fluent REST API Client.")]
9 | [assembly: AssemblyConfiguration("")]
10 | [assembly: AssemblyCompany("Twilio SendGrid")]
11 | [assembly: AssemblyProduct("SendGrid.CSharp.HTTP.Client")]
12 | [assembly: AssemblyCopyright("Copyright Twilio SendGrid 2020")]
13 |
14 | // Setting ComVisible to false makes the types in this assembly not visible
15 | // to COM components. If you need to access a type in this assembly from
16 | // COM, set the ComVisible attribute to true on that type.
17 | [assembly: ComVisible(false)]
18 |
19 | // The following GUID is for the ID of the typelib if this project is exposed to COM
20 | [assembly: Guid("26c4841f-ec62-4ec7-b16e-3a7386ea36dc")]
21 |
--------------------------------------------------------------------------------
/CSharpHTTPClient/SendGridCSharpHTTPClient.nuspec:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | SendGrid.CSharp.HTTP.Client
5 | $authors$
6 | $title$
7 | $authors$
8 | 3.4.12
9 | https://sendgrid.com/
10 | https://sendgrid.com/wp-content/themes/sgdotcom/pages/resource/brand//2016/SendGrid-Logomark.png
11 | false
12 | Copyright Twilio SendGrid 2020
13 | $description$
14 | MIT
15 | Twilio SendGrid Email Mail Microsoft Azure Transactional .NET Core
16 |
17 |
18 |
--------------------------------------------------------------------------------
/Example/.env_sample:
--------------------------------------------------------------------------------
1 | export SENDGRID_API_KEY=''
--------------------------------------------------------------------------------
/Example/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/Example/Example.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using SendGrid.CSharp.HTTP.Client;
4 | using System.Web.Script.Serialization;
5 | using System.Threading.Tasks;
6 | using Newtonsoft.Json;
7 |
8 | // This is a working example, using the Twilio SendGrid API
9 | // You will need a Twilio SendGrid account and an active API Key
10 | // They key should be stored in an environment variable called SENDGRID_API_KEY
11 | namespace Example
12 | {
13 | class Example
14 | {
15 | static void Main(string[] args)
16 | {
17 | Execute().Wait();
18 | }
19 |
20 | static async Task Execute()
21 | {
22 | String host = "https://e9sk3d3bfaikbpdq7.stoplight-proxy.io";
23 | Dictionary globalRequestHeaders = new Dictionary();
24 | string apiKey = Environment.GetEnvironmentVariable("SENDGRID_API_KEY", EnvironmentVariableTarget.User);
25 | globalRequestHeaders.Add("Authorization", "Bearer " + apiKey);
26 | globalRequestHeaders.Add("Content-Type", "application/json");
27 |
28 | String version = "v3";
29 | dynamic client = new Client(host: host, requestHeaders: globalRequestHeaders, version: version);
30 |
31 | // GET Collection
32 | string queryParams = @"{
33 | 'limit': 100
34 | }";
35 | Dictionary requestHeaders = new Dictionary();
36 | requestHeaders.Add("X-Test", "test");
37 | dynamic response = await client.api_keys.get(queryParams: queryParams, requestHeaders: requestHeaders);
38 | Console.WriteLine(response.StatusCode);
39 | Console.WriteLine(response.Body.ReadAsStringAsync().Result);
40 | Console.WriteLine(response.Headers.ToString());
41 |
42 | Console.WriteLine("\n\nPress any key to continue to POST.");
43 | Console.ReadLine();
44 |
45 | // POST
46 | string requestBody = @"{
47 | 'name': 'My API Key 5',
48 | 'scopes': [
49 | 'mail.send',
50 | 'alerts.create',
51 | 'alerts.read'
52 | ]
53 | }";
54 | Object json = JsonConvert.DeserializeObject