├── examples ├── .env_sample └── Example.java ├── static └── img │ ├── github-fork.png │ └── github-sign-up.png ├── twilio_sendgrid_logo.png ├── src ├── main │ └── java │ │ └── com │ │ └── sendgrid │ │ ├── Method.java │ │ ├── Response.java │ │ ├── SendGridResponseHandler.java │ │ ├── Request.java │ │ └── Client.java └── test │ └── java │ └── com │ └── sendgrid │ ├── LicenseTest.java │ ├── TestRequiredFilesExist.java │ └── ClientTest.java ├── use_cases └── README.md ├── .github └── workflows │ ├── pr-lint.yml │ ├── update-dependencies.yml │ └── test-and-deploy.yml ├── .gitignore ├── TROUBLESHOOTING.md ├── Makefile ├── docker ├── Dockerfile ├── entrypoint.sh ├── USAGE.md └── README.md ├── USAGE.md ├── LICENSE ├── PULL_REQUEST_TEMPLATE.md ├── FIRST_TIMERS.md ├── CODE_OF_CONDUCT.md ├── README.md ├── CONTRIBUTING.md ├── pom.xml └── CHANGELOG.md /examples/.env_sample: -------------------------------------------------------------------------------- 1 | export SENDGRID_API_KEY='' -------------------------------------------------------------------------------- /static/img/github-fork.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sendgrid/java-http-client/HEAD/static/img/github-fork.png -------------------------------------------------------------------------------- /twilio_sendgrid_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sendgrid/java-http-client/HEAD/twilio_sendgrid_logo.png -------------------------------------------------------------------------------- /static/img/github-sign-up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sendgrid/java-http-client/HEAD/static/img/github-sign-up.png -------------------------------------------------------------------------------- /src/main/java/com/sendgrid/Method.java: -------------------------------------------------------------------------------- 1 | package com.sendgrid; 2 | 3 | /** 4 | * Currently supported HTTP methods. 5 | */ 6 | public enum Method { 7 | GET, POST, PATCH, PUT, DELETE 8 | } -------------------------------------------------------------------------------- /use_cases/README.md: -------------------------------------------------------------------------------- 1 | This documentation provides examples for specific SendGrid v3 API use cases. Please [open an issue](https://github.com/sendgrid/java-http-client/issues) or make a pull request for any email use cases you would like us to document here. Thank you! 2 | 3 | # Email Use Cases 4 | 5 | # Non-mail Use Cases 6 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | 3 | # Mobile Tools for Java (J2ME) 4 | .mtj.tmp/ 5 | 6 | # Package Files # 7 | *.war 8 | *.ear 9 | 10 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 11 | hs_err_pid* 12 | 13 | target/ 14 | build/ 15 | gradle.properties 16 | .gradle 17 | repo/ 18 | 19 | # VSCode IDE 20 | .vscode 21 | 22 | # JetBrains IDEs 23 | *.iml 24 | **/.idea/ 25 | /.settings/ 26 | /.classpath 27 | /.project 28 | /bin 29 | 30 | # Environment files 31 | .env/*.* 32 | 33 | java-http-client.jar 34 | -------------------------------------------------------------------------------- /TROUBLESHOOTING.md: -------------------------------------------------------------------------------- 1 | If you have a non-library SendGrid issue, please contact our [support team](https://support.sendgrid.com). 2 | 3 | If you can't find a solution below, please open an [issue](https://github.com/sendgrid/java-http-client/issues). 4 | 5 | ## Table of Contents 6 | 7 | * [Viewing the Request Body](#request-body) 8 | 9 | 10 | ## Viewing the Request Body 11 | 12 | When debugging or testing, it may be useful to examine the raw request body to compare against the [documented format](https://sendgrid.com/docs/API_Reference/api_v3.html). 13 | 14 | You can do this like so: 15 | 16 | ```java 17 | System.out.println(request.getBody()); -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: install package test update-deps test-integration clean 2 | 3 | VERSION := $(shell mvn help:evaluate -Dexpression=project.version --batch-mode | grep -e '^[^\[]') 4 | install: 5 | @java -version || (echo "Java is not installed, please install Java >= 7"; exit 1); 6 | mvn clean install -DskipTests=true -Dgpg.skip -B 7 | cp target/java-http-client-$(VERSION).jar java-http-client.jar 8 | 9 | package: 10 | mvn package -DskipTests=true -Dgpg.skip -B 11 | cp target/java-http-client-$(VERSION).jar java-http-client.jar 12 | 13 | test: 14 | mvn test 15 | 16 | update-deps: 17 | mvn versions:use-latest-releases versions:commit -DallowMajorUpdates=false 18 | 19 | clean: 20 | mvn clean 21 | -------------------------------------------------------------------------------- /docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM store/oracle/serverjre:8 2 | 3 | ENV OAI_SPEC_URL="https://raw.githubusercontent.com/sendgrid/sendgrid-oai/HEAD/oai_stoplight.json" 4 | 5 | RUN yum install -y git 6 | 7 | WORKDIR /root 8 | 9 | # install Prism 10 | ADD https://raw.githubusercontent.com/stoplightio/prism/HEAD/install.sh install.sh 11 | RUN chmod +x ./install.sh && sync && \ 12 | ./install.sh && \ 13 | rm ./install.sh 14 | 15 | # set up default sendgrid env 16 | WORKDIR /root/sources 17 | RUN git clone https://github.com/sendgrid/java-http-client.git 18 | 19 | WORKDIR /root 20 | RUN ln -s /root/sources/java-http-client/sendgrid 21 | 22 | COPY entrypoint.sh entrypoint.sh 23 | RUN chmod +x entrypoint.sh 24 | 25 | # Set entrypoint 26 | ENTRYPOINT ["./entrypoint.sh"] 27 | CMD ["--mock"] -------------------------------------------------------------------------------- /USAGE.md: -------------------------------------------------------------------------------- 1 | # Usage 2 | 3 | - [Example Code](examples) 4 | 5 | The example uses SendGrid, you can get your free account [here](https://sendgrid.com/free?source=java-http-client). 6 | 7 | First, update your environment with your [SENDGRID_API_KEY](https://app.sendgrid.com/settings/api_keys). 8 | 9 | ```bash 10 | echo "export SENDGRID_API_KEY='YOUR_API_KEY'" > sendgrid.env 11 | echo "sendgrid.env" >> .gitignore 12 | source ./sendgrid.env 13 | ``` 14 | 15 | ```bash 16 | mvn package 17 | cd examples 18 | javac -classpath {path_to}/sendgrid-java-http-client-4.0.0-jar.jar:. Example.java && java -classpath {path_to}/sendgrid-java-http-client-4.0.0-jar.jar:. Example 19 | ``` 20 | 21 | ## Environment Variables 22 | 23 | You can do the following to create a .env file: 24 | 25 | ```cp .env_example .env``` 26 | 27 | Then, just add your API Key into your .env file. -------------------------------------------------------------------------------- /src/test/java/com/sendgrid/LicenseTest.java: -------------------------------------------------------------------------------- 1 | package com.sendgrid; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | import java.io.BufferedReader; 7 | import java.io.FileReader; 8 | import java.io.IOException; 9 | import java.util.Calendar; 10 | 11 | public class LicenseTest { 12 | 13 | @Test 14 | public void testLicenseShouldHaveCorrectYear() throws IOException { 15 | String copyrightText = null; 16 | try (BufferedReader br = new BufferedReader(new FileReader("./LICENSE"))) { 17 | for (String line; (line = br.readLine()) != null; ) { 18 | if (line.startsWith("Copyright")) { 19 | copyrightText = line; 20 | break; 21 | } 22 | } 23 | } 24 | String expectedCopyright = String.format("Copyright (C) %d, Twilio SendGrid, Inc. ", Calendar.getInstance().get(Calendar.YEAR)); 25 | Assert.assertEquals("License has incorrect year", copyrightText, expectedCopyright); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /docker/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | clear 3 | 4 | # check for + link mounted libraries: 5 | if [ -d /mnt/java-http-client ] 6 | then 7 | rm /root/sendgrid 8 | ln -s /mnt/java-http-client/sendgrid 9 | echo "Linked mounted java-http-client's code to /root/sendgrid" 10 | fi 11 | 12 | SENDGRID_JAVA_VERSION="1.0.0" 13 | echo "Welcome to java-http-client docker v${SENDGRID_JAVA_VERSION}." 14 | echo 15 | 16 | if [ "$1" != "--no-mock" ] 17 | then 18 | echo "Starting Prism in mock mode. Calls made to Prism will not actually send emails." 19 | echo "Disable this by running this container with --no-mock." 20 | prism run --mock --spec $OAI_SPEC_URL 2> /dev/null & 21 | else 22 | echo "Starting Prism in live (--no-mock) mode. Calls made to Prism will send emails." 23 | prism run --spec $OAI_SPEC_URL 2> /dev/null & 24 | fi 25 | echo "To use Prism, make API calls to localhost:4010. For example," 26 | echo " sg = sendgrid.SendGridAPIClient(" 27 | echo " host='http://localhost:4010/'," 28 | echo " api_key=os.environ.get('SENDGRID_API_KEY_CAMPAIGNS'))" 29 | echo "To stop Prism, run \"kill $!\" from the shell." 30 | 31 | bash -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (C) 2023, Twilio SendGrid, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | of the Software, and to permit persons to whom the Software is furnished to do 10 | so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /docker/USAGE.md: -------------------------------------------------------------------------------- 1 | You can use Docker to easily try out or test java-http-client. 2 | 3 | 4 | # Quickstart 5 | 6 | 1. Install Docker on your machine. 7 | 2. If you have not done so, create a Docker Store account [here](https://store.docker.com/signup?next=%2F) 8 | 3. Navigate [here](https://store.docker.com/images/oracle-serverjre-8) and click the "Proceed to Checkout" link (don't worry, it's free). 9 | 4. On the command line, execute `docker login` and provide your credentials. 10 | 5. Build the Docker image using the command `docker build -t sendgrid/java-http-client -f Dockerfile .` 11 | 6. Run `docker run -it sendgrid/java-http-client`. 12 | 13 | 14 | # Info 15 | 16 | This Docker image contains 17 | - `java-http-client` 18 | - Stoplight's Prism, which lets you try out the API without actually sending email 19 | 20 | Run it in interactive mode with `-it`. 21 | 22 | You can mount repositories in the `/mnt/java-http-client` and `/mnt/java-http-client` directories to use them instead of the default SendGrid libraries. Read on for more info. 23 | 24 | 25 | # Testing 26 | Testing is easy! Run the container, `cd sendgrid`, and run `./gradlew test`. 27 | 28 | ![SendGrid Logo](https://uiux.s3.amazonaws.com/2016-logos/email-logo%402x.png) 29 | -------------------------------------------------------------------------------- /src/main/java/com/sendgrid/Response.java: -------------------------------------------------------------------------------- 1 | package com.sendgrid; 2 | 3 | import java.util.Map; 4 | 5 | /** 6 | * Class Response provides a standard interface to an API's response. 7 | */ 8 | public class Response { 9 | private int statusCode; 10 | private String body; 11 | private Map headers; 12 | 13 | /** 14 | * Set the API's response. 15 | */ 16 | public Response(int statusCode, String responseBody, Map responseHeaders) { 17 | this.statusCode = statusCode; 18 | this.body = responseBody; 19 | this.headers = responseHeaders; 20 | } 21 | 22 | public Response() { 23 | this.reset(); 24 | } 25 | 26 | /** 27 | * Place the object into an empty state. 28 | */ 29 | public void reset() { 30 | this.statusCode = 0; 31 | this.body = ""; 32 | this.headers = null; 33 | } 34 | 35 | public int getStatusCode() { 36 | return this.statusCode; 37 | } 38 | 39 | public String getBody() { 40 | return this.body; 41 | } 42 | 43 | public Map getHeaders() { 44 | return this.headers; 45 | } 46 | 47 | public void setStatusCode(int statusCode) { 48 | this.statusCode = statusCode; 49 | } 50 | 51 | public void setBody(String body) { 52 | this.body = body; 53 | } 54 | 55 | public void setHeaders(Map headers) { 56 | this.headers = headers; 57 | } 58 | } -------------------------------------------------------------------------------- /docker/README.md: -------------------------------------------------------------------------------- 1 | # Supported tags and respective `Dockerfile` links 2 | - `v1.0.0`, `latest` [(Dockerfile)](Dockerfile) 3 | 4 | # Quick reference 5 | Due to Oracle's JDK license, you must build this Docker image using the official Oracle image located in the Docker Store. You will need a Docker store account. Once you have an account, you must accept the Oracle license [here](https://store.docker.com/images/oracle-serverjre-8). On the command line, type `docker login` and provide your credentials. You may then build the image using this command `docker build -t sendgrid/java-http-client -f Dockerfile .` 6 | 7 | - **Where to get help:** 8 | [Contact SendGrid Support](https://support.sendgrid.com/hc/en-us) 9 | 10 | - **Where to file issues:** 11 | https://github.com/sendgrid/java-http-client/issues 12 | 13 | - **Where to get more info:** 14 | [USAGE.md](USAGE.md) 15 | 16 | - **Maintained by:** 17 | [SendGrid Inc.](https://sendgrid.com) 18 | 19 | # Usage examples 20 | - Most recent version: `docker run -it sendgrid/java-http-client`. 21 | - Your own fork: 22 | ```sh-session 23 | $ git clone https://github.com/you/cool-java-http-client.git 24 | $ realpath cool-java-http-client 25 | /path/to/cool-java-http-client 26 | $ docker run -it -v /path/to/cool-java-http-client:/mnt/java-http-client sendgrid/java-http-client 27 | ``` 28 | 29 | For more detailed information, see [USAGE.md](USAGE.md). 30 | 31 | ![SendGrid Logo](https://uiux.s3.amazonaws.com/2016-logos/email-logo%402x.png) 32 | -------------------------------------------------------------------------------- /src/main/java/com/sendgrid/SendGridResponseHandler.java: -------------------------------------------------------------------------------- 1 | package com.sendgrid; 2 | 3 | import java.io.IOException; 4 | 5 | import org.apache.http.HttpEntity; 6 | import org.apache.http.HttpResponse; 7 | import org.apache.http.client.HttpResponseException; 8 | import org.apache.http.impl.client.AbstractResponseHandler; 9 | import org.apache.http.util.EntityUtils; 10 | import java.nio.charset.StandardCharsets; 11 | 12 | /** 13 | * A {@link org.apache.http.client.ResponseHandler} that returns the response body as a String 14 | * for all responses. 15 | *

16 | * If this is used with 17 | * {@link org.apache.http.client.HttpClient#execute( 18 | * org.apache.http.client.methods.HttpUriRequest, org.apache.http.client.ResponseHandler)}, 19 | * HttpClient may handle redirects (3xx responses) internally. 20 | *

21 | * 22 | */ 23 | public class SendGridResponseHandler extends AbstractResponseHandler{ 24 | 25 | /** 26 | * Read the entity from the response body and pass it to the entity handler 27 | * method if the response was successful (a 2xx status code). If no response 28 | * body exists, this returns null. If the response was unsuccessful (>= 500 29 | * status code), throws an {@link HttpResponseException}. 30 | */ 31 | @Override 32 | public String handleResponse(final HttpResponse response) 33 | throws HttpResponseException, IOException { 34 | final HttpEntity entity = response.getEntity(); 35 | return entity == null ? null : handleEntity(entity); 36 | } 37 | 38 | @Override 39 | public String handleEntity(HttpEntity entity) throws IOException { 40 | return EntityUtils.toString(entity, StandardCharsets.UTF_8); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 16 | 17 | # Fixes # 18 | 19 | A short description of what this PR does. 20 | 21 | ### Checklist 22 | - [x] I acknowledge that all my contributions will be made under the project's license 23 | - [ ] I have made a material change to the repo (functionality, testing, spelling, grammar) 24 | - [ ] I have read the [Contribution Guidelines](https://github.com/sendgrid/java-http-client/blob/main/CONTRIBUTING.md) and my PR follows them 25 | - [ ] I have titled the PR appropriately 26 | - [ ] I have updated my branch with the main branch 27 | - [ ] I have added tests that prove my fix is effective or that my feature works 28 | - [ ] I have added the necessary documentation about the functionality in the appropriate .md file 29 | - [ ] I have added inline documentation to the code I modified 30 | 31 | If you have questions, please file a [support ticket](https://support.sendgrid.com), or create a GitHub Issue in this repository. 32 | -------------------------------------------------------------------------------- /.github/workflows/update-dependencies.yml: -------------------------------------------------------------------------------- 1 | name: Update dependencies 2 | on: 3 | schedule: 4 | # Run automatically at 7AM PST Tuesday 5 | - cron: '0 14 * * 2' 6 | workflow_dispatch: 7 | 8 | jobs: 9 | update-dependencies-and-test: 10 | name: Update Dependencies & Test 11 | runs-on: ubuntu-latest 12 | timeout-minutes: 20 13 | strategy: 14 | max-parallel: 1 15 | matrix: 16 | java: [ 8, 11, 17 ] 17 | 18 | steps: 19 | - name: Checkout java-http-client 20 | uses: actions/checkout@v2 21 | 22 | - name: Set up Java 23 | uses: actions/setup-java@v2 24 | with: 25 | distribution: 'temurin' 26 | java-version: ${{ matrix.java }} 27 | cache: 'maven' 28 | 29 | - name: Updating semver dependencies 30 | run: make update-deps 31 | 32 | - run: mvn install -Dgpg.skip -Dmaven.javadoc.skip=true -B -V 33 | 34 | - name: Add & Commit 35 | if: matrix.java == '17' 36 | uses: EndBug/add-and-commit@v8.0.2 37 | env: 38 | GITHUB_TOKEN: ${{ secrets.SG_JAVA_GITHUB_TOKEN }} 39 | with: 40 | add: 'pom.xml' 41 | default_author: 'github_actions' 42 | message: 'chore: update java-http-client dependencies' 43 | 44 | notify-on-failure: 45 | name: Slack notify on failure 46 | if: failure() 47 | needs: [ update-dependencies-and-test ] 48 | runs-on: ubuntu-latest 49 | steps: 50 | - uses: rtCamp/action-slack-notify@v2 51 | env: 52 | SLACK_COLOR: failure 53 | SLACK_ICON_EMOJI: ':github:' 54 | SLACK_MESSAGE: ${{ format('Update dependencies *{0}*, {1}/{2}/actions/runs/{3}', needs.update-dependencies-and-test.result, github.server_url, github.repository, github.run_id) }} 55 | SLACK_TITLE: Action Failure - ${{ github.repository }} 56 | SLACK_USERNAME: GitHub Actions 57 | SLACK_MSG_AUTHOR: twilio-dx 58 | SLACK_FOOTER: Posted automatically using GitHub Actions 59 | SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} 60 | MSG_MINIMAL: true 61 | -------------------------------------------------------------------------------- /src/test/java/com/sendgrid/TestRequiredFilesExist.java: -------------------------------------------------------------------------------- 1 | import org.junit.Test; 2 | 3 | import java.io.File; 4 | 5 | import static org.junit.Assert.assertTrue; 6 | 7 | public class TestRequiredFilesExist { 8 | 9 | // ./Docker or docker/Docker 10 | @Test public void checkDockerExists() { 11 | boolean dockerExists = new File("./docker/Dockerfile").exists() || 12 | new File("./docker/Docker").exists(); 13 | assertTrue(dockerExists); 14 | } 15 | 16 | /* // ./docker-compose.yml or ./docker/docker-compose.yml 17 | @Test public void checkDockerComposeExists() { 18 | boolean dockerComposeExists = new File("./docker-compose.yml").exists() || 19 | new File("./docker/docker-compose.yml").exists(); 20 | assertTrue(dockerComposeExists); 21 | } 22 | // ./.env_sample 23 | @Test public void checkEnvSampleExists() { 24 | assertTrue(new File("./.env_sample").exists()); 25 | } */ 26 | 27 | // ./.gitignore 28 | @Test public void checkGitIgnoreExists() { 29 | assertTrue(new File("./.gitignore").exists()); 30 | } 31 | 32 | 33 | // ./CHANGELOG.md 34 | @Test public void checkChangelogExists() { 35 | assertTrue(new File("./CHANGELOG.md").exists()); 36 | } 37 | 38 | // ./CODE_OF_CONDUCT.md 39 | @Test public void checkCodeOfConductExists() { 40 | assertTrue(new File("./CODE_OF_CONDUCT.md").exists()); 41 | } 42 | 43 | // ./CONTRIBUTING.md 44 | @Test public void checkContributingGuideExists() { 45 | assertTrue(new File("./CONTRIBUTING.md").exists()); 46 | } 47 | 48 | // ./LICENSE 49 | @Test public void checkLicenseExists() { 50 | assertTrue(new File("./LICENSE").exists()); 51 | } 52 | 53 | // ./PULL_REQUEST_TEMPLATE.md 54 | @Test public void checkPullRequestExists() { 55 | assertTrue(new File("./PULL_REQUEST_TEMPLATE.md").exists()); 56 | } 57 | 58 | // ./README.md 59 | @Test public void checkReadMeExists() { 60 | assertTrue(new File("./README.md").exists()); 61 | } 62 | 63 | // ./TROUBLESHOOTING.md 64 | @Test public void checkTroubleShootingGuideExists() { 65 | assertTrue(new File("./TROUBLESHOOTING.md").exists()); 66 | } 67 | 68 | // ./USAGE.md 69 | @Test public void checkUsageGuideExists() { 70 | assertTrue(new File("./USAGE.md").exists()); 71 | } 72 | 73 | /* // ./USE_CASES.md 74 | @Test public void checkUseCases() { 75 | assertTrue(new File("./USE_CASES.md").exists()); 76 | } */ 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/com/sendgrid/Request.java: -------------------------------------------------------------------------------- 1 | package com.sendgrid; 2 | 3 | import java.util.Map; 4 | import java.util.HashMap; 5 | 6 | /** 7 | * Class Response provides a standard interface to an API's HTTP request. 8 | */ 9 | public class Request { 10 | private Method method; 11 | private String baseUri; 12 | private String endpoint; 13 | private String body; 14 | private final Map headers; 15 | private final Map queryParams; 16 | 17 | public Request() { 18 | this.headers = new HashMap(); 19 | this.queryParams = new HashMap(); 20 | this.reset(); 21 | } 22 | 23 | /** 24 | * Place the object into an empty state. 25 | */ 26 | public void reset() { 27 | this.clearMethod(); 28 | this.clearBaseUri(); 29 | this.clearEndpoint(); 30 | this.clearBody(); 31 | this.clearHeaders(); 32 | this.clearQueryParams(); 33 | } 34 | 35 | public void addQueryParam(String key, String value) { 36 | this.queryParams.put(key, value); 37 | } 38 | 39 | public void addHeader(String key, String value) { 40 | this.headers.put(key, value); 41 | } 42 | 43 | public String removeQueryParam(String key) { 44 | return this.queryParams.remove(key); 45 | } 46 | 47 | public String removeHeader(String key) { 48 | return this.headers.remove(key); 49 | } 50 | 51 | public void setMethod(Method method) { 52 | this.method = method; 53 | } 54 | 55 | public void setBaseUri(String baseUri) { 56 | this.baseUri = baseUri; 57 | } 58 | 59 | public void setEndpoint(String endpoint) { 60 | this.endpoint = endpoint; 61 | } 62 | 63 | public void setBody(String body) { 64 | this.body = body; 65 | } 66 | 67 | public Map getHeaders() { 68 | return this.headers; 69 | } 70 | 71 | public Map getQueryParams() { 72 | return this.queryParams; 73 | } 74 | 75 | public Method getMethod() { 76 | return this.method; 77 | } 78 | 79 | public String getBaseUri() { 80 | return this.baseUri; 81 | } 82 | 83 | public String getEndpoint() { 84 | return this.endpoint; 85 | } 86 | 87 | public String getBody() { 88 | return this.body; 89 | } 90 | 91 | public void clearMethod() { 92 | this.method = null; 93 | } 94 | 95 | public void clearBaseUri() { 96 | this.baseUri = ""; 97 | } 98 | 99 | public void clearEndpoint() { 100 | this.endpoint = ""; 101 | } 102 | 103 | public void clearBody() { 104 | this.body = ""; 105 | } 106 | 107 | public void clearQueryParams() { 108 | this.queryParams.clear(); 109 | } 110 | 111 | public void clearHeaders() { 112 | this.headers.clear(); 113 | } 114 | } -------------------------------------------------------------------------------- /FIRST_TIMERS.md: -------------------------------------------------------------------------------- 1 | # How To Contribute to Twilio SendGrid Repositories via GitHub 2 | 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. 3 | 4 | To make a pull request, follow these steps: 5 | 6 | 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. 7 | 8 | 9 | 10 | 2. __[Fork](https://help.github.com/fork-a-repo/)__ the [java-http-client](https://github.com/sendgrid/java-http-client) repository: 11 | 12 | 13 | 14 | 3. __Clone__ your fork via the following commands: 15 | 16 | ```bash 17 | # Clone your fork of the repo into the current directory 18 | git clone https://github.com/your_username/java-http-client 19 | # Navigate to the newly cloned directory 20 | cd java-http-client 21 | # Assign the original repo to a remote called "upstream" 22 | git remote add upstream https://github.com/sendgrid/java-http-client 23 | ``` 24 | 25 | > Don't forget to replace *your_username* in the URL by your real GitHub username. 26 | 27 | 4. __Create a new topic branch__ (off the main project development branch) to contain your feature, change, or fix: 28 | 29 | ```bash 30 | git checkout -b 31 | ``` 32 | 33 | 5. __Commit your changes__ in logical chunks. 34 | 35 | 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. 36 | 37 | 6. __Locally merge (or rebase)__ the upstream development branch into your topic branch: 38 | 39 | ```bash 40 | git pull [--rebase] upstream main 41 | ``` 42 | 43 | 7. __Push__ your topic branch up to your fork: 44 | 45 | ```bash 46 | git push origin 47 | ``` 48 | 49 | 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. 50 | 51 | ## Important notice 52 | 53 | 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. 54 | -------------------------------------------------------------------------------- /.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 | strategy: 19 | matrix: 20 | java: [ 8, 11, 17 ] 21 | steps: 22 | - uses: actions/checkout@v2 23 | 24 | - name: Set up Java 25 | uses: actions/setup-java@v2 26 | with: 27 | distribution: 'temurin' 28 | java-version: ${{ matrix.java }} 29 | cache: 'maven' 30 | 31 | - run: mvn install -DskipTests=true -Dgpg.skip -Dmaven.javadoc.skip=true -B -V 32 | - name: Run Unit Tests 33 | run: mvn test -B 34 | 35 | deploy: 36 | name: Deploy 37 | if: success() && github.ref_type == 'tag' 38 | needs: [ test ] 39 | runs-on: ubuntu-latest 40 | steps: 41 | - uses: actions/checkout@v2 42 | 43 | - name: Set up Sonatype Maven 44 | uses: actions/setup-java@v2 45 | with: 46 | java-version: 8 47 | distribution: temurin 48 | server-id: ossrh 49 | server-username: MAVEN_USERNAME 50 | server-password: MAVEN_PASSWORD 51 | gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }} 52 | gpg-passphrase: GPG_PASSPHRASE 53 | 54 | - name: Install Dependencies 55 | run: make install 56 | 57 | - name: Create GitHub Release 58 | uses: sendgrid/dx-automator/actions/release@main 59 | with: 60 | assets: java-http-client.jar 61 | footer: '**[Maven](https://mvnrepository.com/artifact/com.sendgrid/java-http-client/${version})**' 62 | env: 63 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 64 | 65 | - name: Publish to Maven 66 | env: 67 | MAVEN_USERNAME: ${{ secrets.SONATYPE_USERNAME }} 68 | MAVEN_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} 69 | GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} 70 | run: mvn clean deploy -DskipTests=true -B -U -Prelease 71 | 72 | - name: Submit metric to Datadog 73 | uses: sendgrid/dx-automator/actions/datadog-release-metric@main 74 | env: 75 | DD_API_KEY: ${{ secrets.DATADOG_API_KEY }} 76 | 77 | notify-on-failure: 78 | name: Slack notify on failure 79 | if: failure() && github.event_name != 'pull_request' && (github.ref == 'refs/heads/main' || github.ref_type == 'tag') 80 | needs: [ test, deploy ] 81 | runs-on: ubuntu-latest 82 | steps: 83 | - uses: rtCamp/action-slack-notify@v2 84 | env: 85 | SLACK_COLOR: failure 86 | SLACK_ICON_EMOJI: ':github:' 87 | 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) }} 88 | SLACK_TITLE: Action Failure - ${{ github.repository }} 89 | SLACK_USERNAME: GitHub Actions 90 | SLACK_MSG_AUTHOR: twilio-dx 91 | SLACK_FOOTER: Posted automatically using GitHub Actions 92 | SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} 93 | MSG_MINIMAL: true 94 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /examples/Example.java: -------------------------------------------------------------------------------- 1 | import com.fasterxml.jackson.databind.JsonNode; 2 | import com.fasterxml.jackson.databind.ObjectMapper; 3 | 4 | import com.sendgrid.Client; 5 | import com.sendgrid.Method; 6 | import com.sendgrid.Request; 7 | import com.sendgrid.Response; 8 | 9 | import java.io.IOException; 10 | import java.io.OutputStream; 11 | import java.net.URISyntaxException; 12 | import java.util.HashMap; 13 | import java.util.Map; 14 | 15 | public class Example { 16 | 17 | private static String apiKeyId = ""; 18 | 19 | private static void getCollection(Client client, Request request) throws IOException { 20 | request.setMethod(Method.GET); 21 | request.setEndpoint("/v3/api_keys"); 22 | request.addQueryParam("limit", "100"); 23 | request.addQueryParam("offset", "0"); 24 | try { 25 | processResponse(); 26 | } catch (IOException ex) { 27 | throw ex; 28 | } 29 | request.clearQueryParams(); 30 | } 31 | 32 | private static void post(Client client, Request request) throws IOException { 33 | request.setMethod(Method.POST); 34 | request.setEndpoint("/v3/api_keys"); 35 | request.setBody("{\"name\": \"My api Key\",\"scopes\": [\"mail.send\",\"alerts.create\",\"alerts.read\"]}"); 36 | 37 | try { 38 | processResponse(); 39 | } catch (IOException ex) { 40 | throw ex; 41 | } 42 | String apiKeyId = ""; 43 | try { 44 | ObjectMapper mapper = new ObjectMapper(); 45 | JsonNode json = mapper.readTree(response.getBody()); 46 | apiKeyId = json.path("api_key_id").asText(); 47 | } catch (IOException ex) { 48 | throw ex; 49 | } 50 | request.clearBody(); 51 | } 52 | 53 | private static void getSingle(Client client, Request request) throws IOException { 54 | request.setMethod(Method.GET); 55 | request.setEndpoint("/v3/api_keys/" + apiKeyId); 56 | try { 57 | processResponse(); 58 | } catch (IOException ex) { 59 | throw ex; 60 | } 61 | } 62 | 63 | private static void patch(Client client, Request request) throws IOException { 64 | request.setMethod(Method.PATCH); 65 | request.setBody("{\"name\": \"A New Ho}"); 66 | try { 67 | processResponse(); 68 | } catch (IOException ex) { 69 | throw ex; 70 | } 71 | request.clearBody(); 72 | } 73 | 74 | private static void put(Client client, Request request) throws IOException { 75 | request.setMethod(Method.PUT); 76 | request.setBody("{\"name\": \"A New Hope\",\"scopes\": [\"user.profile.read\",\"user.profile.update\"]}"); 77 | try { 78 | processResponse(); 79 | } catch (IOException ex) { 80 | throw ex; 81 | } 82 | request.clearBody(); 83 | } 84 | 85 | private static void delete(Client client, Request request) throws IOException { 86 | request.setMethod(Method.DELETE); 87 | try { 88 | Response response = client.api(request); 89 | System.out.println(response.getStatusCode()); 90 | System.out.println(response.getHeaders()); 91 | } catch (IOException ex) { 92 | throw ex; 93 | } 94 | } 95 | 96 | public static void main(String[] args) throws IOException { 97 | Client client = new Client(); 98 | 99 | Request request = new Request(); 100 | request.setBaseUri("api.sendgrid.com"); 101 | request.addHeader("Authorization", "Bearer " + System.getenv("SENDGRID_API_KEY")); 102 | 103 | // GET Collection 104 | getCollection(client, request); 105 | 106 | // POST 107 | post(client, request); 108 | 109 | // GET Single 110 | getSingle(client, request); 111 | 112 | // PATCH 113 | patch(client, request); 114 | 115 | // PUT 116 | put(client, request); 117 | 118 | // DELETE 119 | delete(client, request); 120 | } 121 | 122 | //Refactor method 123 | private void processResponse(){ 124 | response = client.api(request); 125 | System.out.println(response.getStatusCode()); 126 | System.out.println(response.getBody()); 127 | System.out.println(response.getHeaders()); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![SendGrid Logo](twilio_sendgrid_logo.png) 2 | 3 | [![Test and Deploy](https://github.com/sendgrid/java-http-client/actions/workflows/test-and-deploy.yml/badge.svg)](https://github.com/sendgrid/java-http-client/actions/workflows/test-and-deploy.yml) 4 | [![Maven Central](https://img.shields.io/maven-central/v/com.sendgrid/java-http-client.svg)](http://mvnrepository.com/artifact/com.sendgrid/java-http-client) 5 | [![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) 6 | [![Twitter Follow](https://img.shields.io/twitter/follow/sendgrid.svg?style=social&label=Follow)](https://twitter.com/sendgrid) 7 | [![GitHub contributors](https://img.shields.io/github/contributors/sendgrid/java-http-client.svg)](https://github.com/sendgrid/java-http-client/graphs/contributors) 8 | 9 | **Quickly and easily access any RESTful or RESTful-like API.** 10 | 11 | If you are looking for the SendGrid API client library, please see [this repo](https://github.com/sendgrid/sendgrid-java). 12 | 13 | # Table of Contents 14 | 15 | * [Announcements](#announcements) 16 | * [Installation](#installation) 17 | * [Quick Start](#quick-start) 18 | * [Usage](./USAGE.md) 19 | * [How to Contribute](#contribute) 20 | * [About](#about) 21 | * [Support](#support) 22 | * [License](#license) 23 | 24 | 25 | # Announcements 26 | 27 | All updates to this project are documented in our [CHANGELOG](CHANGELOG.md). 28 | 29 | 30 | # Installation 31 | 32 | ## Prerequisites 33 | 34 | - Java 8, 11, or 17 35 | 36 | ## Install via Maven w/ Gradle 37 | 38 | ```groovy 39 | ... 40 | dependencies { 41 | ... 42 | compile 'com.sendgrid:java-http-client:4.5.1' 43 | } 44 | 45 | repositories { 46 | mavenCentral() 47 | } 48 | ... 49 | ``` 50 | 51 | ### Maven 52 | 53 | ```xml 54 | 55 | com.sendgrid 56 | java-http-client 57 | 4.5.1 58 | 59 | ``` 60 | 61 | `mvn install` 62 | 63 | ## Dependencies 64 | 65 | - Please see the [pom.xml file](pom.xml) 66 | 67 | 68 | # Quick Start 69 | 70 | Here is a quick example: 71 | 72 | `GET /your/api/{param}/call` 73 | 74 | ```java 75 | Client client = new Client(); 76 | 77 | Request request = new Request(); 78 | request.setBaseUri("api.test.com"); 79 | request.setMethod(Method.GET); 80 | String param = "param"; 81 | request.setEndpoint("/your/api/" + param + "/call"); 82 | 83 | try { 84 | Response response = client.api(request); 85 | System.out.println(response.getStatusCode()); 86 | System.out.println(response.getBody()); 87 | System.out.println(response.getHeaders()); 88 | } catch (IOException ex) { 89 | throw ex; 90 | } 91 | ``` 92 | 93 | `POST /your/api/{param}/call` with headers, query parameters and a request body. 94 | 95 | ```java 96 | request.addHeader("Authorization", "Bearer YOUR_API_KEY"); 97 | request.addQueryParam("limit", "100"); 98 | request.addQueryParam("offset", "0"); 99 | // Will be parsed to categories=cake&categories=pie&categories=baking 100 | request.addQueryParam("categories", "cake&pie&baking"); 101 | request.setBody("{\"name\": \"My Request Body\"}"); 102 | request.setMethod(Method.POST); 103 | String param = "param"; 104 | request.setEndpoint("/your/api/" + param + "/call"); 105 | 106 | try { 107 | Response response = client.api(request); 108 | System.out.println(response.getStatusCode()); 109 | System.out.println(response.getBody()); 110 | System.out.println(response.getHeaders()); 111 | } catch (IOException ex) { 112 | throw ex; 113 | } 114 | ``` 115 | 116 | 117 | # How to Contribute 118 | 119 | We encourage contribution to our projects please see our [CONTRIBUTING](CONTRIBUTING.md) guide for details. 120 | 121 | Quick links: 122 | 123 | - [Feature Request](CONTRIBUTING.md#feature-request) 124 | - [Bug Reports](CONTRIBUTING.md#submit-a-bug-report) 125 | - [Improvements to the Codebase](CONTRIBUTING.md#improvements-to-the-codebase) 126 | - [Review Pull Requests](CONTRIBUTING.md#Code-Reviews) 127 | 128 | 129 | # About 130 | 131 | java-http-client is maintained and funded by Twilio SendGrid, Inc. The names and logos for java-http-client are trademarks of Twilio SendGrid, Inc. 132 | 133 | 134 | # Support 135 | 136 | If you need help using SendGrid, please check the [Twilio SendGrid Support Help Center](https://support.sendgrid.com). 137 | 138 | If you've instead found a bug in the library or would like new features added, go ahead and open issues or pull requests against this repo! 139 | 140 | # License 141 | [The MIT License (MIT)](LICENSE) 142 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | 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. 2 | 3 | - [Improvements to the Codebase](#improvements-to-the-codebase) 4 | - [Understanding the Code Base](#understanding-the-codebase) 5 | - [Testing](#testing) 6 | - [Style Guidelines & Naming Conventions](#style-guidelines-and-naming-conventions) 7 | - [Creating a Pull Request](#creating-a-pull-request) 8 | - [Code Reviews](#code-reviews) 9 | 10 | 11 | ## Improvements to the Codebase 12 | 13 | We welcome direct contributions to the java-http-client code base. Thank you! 14 | 15 | ### Development Environment ### 16 | 17 | #### Install and Run Locally #### 18 | 19 | ##### Prerequisites ##### 20 | 21 | - Java 8 or 11 22 | - Please see [pom.xml](pom.xml) 23 | 24 | ##### Initial setup: ##### 25 | 26 | ```bash 27 | git clone https://github.com/sendgrid/java-http-client.git 28 | cd java-http-client 29 | ``` 30 | 31 | ##### Execute: ##### 32 | 33 | See the [examples folder](examples) to get started quickly. 34 | 35 | You will need to setup the following environment to use the SendGrid example: 36 | 37 | ```bash 38 | echo "export SENDGRID_API_KEY='YOUR_API_KEY'" > sendgrid.env 39 | echo "sendgrid.env" >> .gitignore 40 | source ./sendgrid.env 41 | ``` 42 | 43 | ```bash 44 | ./gradlew build 45 | cd examples 46 | javac -classpath {path_to}/sendgrid-java-http-client-4.2.0-jar.jar:. Example.java && java -classpath {path_to}/sendgrid-java-http-client-4.2.0-jar.jar:. Example 47 | ``` 48 | 49 | 50 | ## Understanding the Code Base 51 | 52 | **/examples** 53 | 54 | Working examples that demonstrate usage. 55 | 56 | **Client.java** 57 | 58 | The main function that does the heavy lifting (and external entry point) is `api`. 59 | 60 | **Method.java** 61 | 62 | These are the supported Methods. 63 | 64 | **Request.java** 65 | 66 | Provides a standard interface to an API's HTTP request. 67 | 68 | **Response.java** 69 | 70 | Provides a standard interface to an API's response. 71 | 72 | 73 | ## Testing 74 | 75 | All PRs require passing tests before the PR will be reviewed. 76 | 77 | All test files are in [`java-http-client/src/test/java/com/sendgrid`](src/test/java/com/sendgrid). 78 | 79 | For the purposes of contributing to this repo, please update the [`ClientTest.java`](src/test/java/com/sendgrid/ClientTest.java) file with unit tests as you modify the code. 80 | 81 | 82 | Run the tests: 83 | 84 | ```java 85 | ./gradlew test -i 86 | ``` 87 | 88 | 89 | ## Style Guidelines & Naming Conventions 90 | 91 | 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. 92 | 93 | Please run your code through: 94 | 95 | - [FindBugs](http://findbugs.sourceforge.net/) 96 | - [CheckStyle](http://checkstyle.sourceforge.net/) with [Google's Java Style Guide](http://checkstyle.sourceforge.net/reports/google-java-style.html). 97 | 98 | 99 | ## Creating a Pull Request 100 | 101 | 1. [Fork](https://help.github.com/fork-a-repo/) the project, clone your fork, 102 | and configure the remotes: 103 | 104 | ```bash 105 | # Clone your fork of the repo into the current directory 106 | git clone https://github.com/sendgrid/java-http-client 107 | # Navigate to the newly cloned directory 108 | cd java-http-client 109 | # Assign the original repo to a remote called "upstream" 110 | git remote add upstream https://github.com/sendgrid/java-http-client 111 | ``` 112 | 113 | 2. If you cloned a while ago, get the latest changes from upstream: 114 | 115 | ```bash 116 | git checkout 117 | git pull upstream 118 | ``` 119 | 120 | 3. Create a new topic branch (off the main project development branch) to 121 | contain your feature, change, or fix: 122 | 123 | ```bash 124 | git checkout -b 125 | ``` 126 | 127 | 4. Commit your changes in logical chunks. Please adhere to these [git commit 128 | message guidelines](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) 129 | or your code is unlikely to be merged into the main project. Use Git's 130 | [interactive rebase](https://help.github.com/articles/interactive-rebase) 131 | feature to tidy up your commits before making them public. 132 | 133 | 4a. Create tests. 134 | 135 | 4b. Create or update the example code that demonstrates the functionality of this change to the code. 136 | 137 | 5. Locally merge (or rebase) the upstream development branch into your topic branch: 138 | 139 | ```bash 140 | git pull [--rebase] upstream main 141 | ``` 142 | 143 | 6. Push your topic branch up to your fork: 144 | 145 | ```bash 146 | git push origin 147 | ``` 148 | 149 | 7. [Open a Pull Request](https://help.github.com/articles/using-pull-requests/) 150 | with a clear title and description against the `main` branch. All tests must be passing before we will review the PR. 151 | 152 | ## Code Reviews 153 | 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/). 154 | -------------------------------------------------------------------------------- /src/test/java/com/sendgrid/ClientTest.java: -------------------------------------------------------------------------------- 1 | package com.sendgrid; 2 | 3 | import org.apache.http.Header; 4 | import org.apache.http.HttpEntity; 5 | import org.apache.http.HttpStatus; 6 | import org.apache.http.HttpVersion; 7 | import org.apache.http.StatusLine; 8 | import org.apache.http.client.ResponseHandler; 9 | import org.apache.http.client.methods.CloseableHttpResponse; 10 | import org.apache.http.client.methods.HttpDelete; 11 | import org.apache.http.client.methods.HttpGet; 12 | import org.apache.http.client.methods.HttpPatch; 13 | import org.apache.http.client.methods.HttpPost; 14 | import org.apache.http.client.methods.HttpPut; 15 | import org.apache.http.client.methods.HttpUriRequest; 16 | import org.apache.http.client.utils.URIBuilder; 17 | import org.apache.http.entity.InputStreamEntity; 18 | import org.apache.http.entity.StringEntity; 19 | import org.apache.http.impl.client.BasicResponseHandler; 20 | import org.apache.http.impl.client.CloseableHttpClient; 21 | import org.apache.http.impl.client.HttpClients; 22 | import org.apache.http.message.BasicHeader; 23 | import org.apache.http.message.BasicStatusLine; 24 | 25 | import org.junit.Assert; 26 | import org.junit.Before; 27 | import org.junit.Test; 28 | 29 | import org.mockito.Matchers; 30 | import org.mockito.Mockito; 31 | 32 | import java.io.ByteArrayInputStream; 33 | import java.io.IOException; 34 | import java.io.PrintWriter; 35 | import java.io.StringWriter; 36 | import java.net.MalformedURLException; 37 | import java.net.URI; 38 | import java.net.URISyntaxException; 39 | import java.net.URL; 40 | import java.util.HashMap; 41 | import java.util.Map; 42 | 43 | public class ClientTest extends Mockito { 44 | 45 | private CloseableHttpClient httpClient; 46 | private CloseableHttpResponse response; 47 | private HttpEntity entity; 48 | private StatusLine statusline; 49 | 50 | @Before 51 | public void setUp() throws Exception { 52 | this.httpClient = mock(CloseableHttpClient.class); 53 | this.response = mock(CloseableHttpResponse.class); 54 | this.entity = mock(HttpEntity.class); 55 | this.statusline = mock(StatusLine.class); 56 | } 57 | 58 | @Test 59 | public void testbuildUri() { 60 | Client client = new Client(); 61 | String baseUri = "api.test.com"; 62 | String endpoint = "/endpoint"; 63 | URI uri = null; 64 | Map queryParams = new HashMap(); 65 | queryParams.put("test1", "1"); 66 | queryParams.put("test2", "2"); 67 | queryParams.put("test3", "3&4&5"); 68 | try { 69 | uri = client.buildUri(baseUri, endpoint, queryParams); 70 | } catch (URISyntaxException ex) { 71 | StringWriter errors = new StringWriter(); 72 | ex.printStackTrace(new PrintWriter(errors)); 73 | Assert.assertTrue(errors.toString(), false); 74 | } 75 | 76 | URL url = null; 77 | try { 78 | url = uri.toURL(); 79 | } catch (MalformedURLException ex) { 80 | StringWriter errors = new StringWriter(); 81 | ex.printStackTrace(new PrintWriter(errors)); 82 | Assert.assertTrue(errors.toString(), false); 83 | } 84 | 85 | Assert.assertTrue(url.getProtocol().equals("https")); 86 | Assert.assertTrue(url.getHost().equals("api.test.com")); 87 | Assert.assertTrue(url.getPath().equals("/endpoint")); 88 | Assert.assertTrue(this.queryParamHasCorrectValue(url, "test1", "1")); 89 | Assert.assertTrue(this.queryParamHasCorrectValue(url, "test2", "2")); 90 | Assert.assertTrue(this.queryParamHasCorrectValue(url, "test3", "3")); 91 | Assert.assertTrue(this.queryParamHasCorrectValue(url, "test3", "4")); 92 | Assert.assertTrue(this.queryParamHasCorrectValue(url, "test3", "5")); 93 | } 94 | 95 | @Test 96 | public void testGetResponse() { 97 | Client client = new Client(); 98 | Response testResponse = new Response(); 99 | Header[] mockedHeaders = null; 100 | try { 101 | when(statusline.getStatusCode()).thenReturn(200); 102 | when(response.getStatusLine()).thenReturn(statusline); 103 | when(response.getEntity()).thenReturn( 104 | new InputStreamEntity( 105 | new ByteArrayInputStream( 106 | "{\"message\":\"success\"}".getBytes()))); 107 | mockedHeaders = new Header[] { new BasicHeader("headerA", "valueA") }; 108 | when(response.getAllHeaders()).thenReturn(mockedHeaders); 109 | when(httpClient.execute(Matchers.any(HttpGet.class))).thenReturn(response); 110 | HttpGet httpGet = new HttpGet("https://api.test.com"); 111 | CloseableHttpResponse resp = httpClient.execute(httpGet); 112 | testResponse = client.getResponse(resp); 113 | resp.close(); 114 | } catch (IOException ex) { 115 | StringWriter errors = new StringWriter(); 116 | ex.printStackTrace(new PrintWriter(errors)); 117 | Assert.assertTrue(errors.toString(), false); 118 | } 119 | 120 | Assert.assertTrue(testResponse.getStatusCode() == 200); 121 | Assert.assertEquals(testResponse.getBody(), "{\"message\":\"success\"}"); 122 | Map headers = new HashMap(); 123 | for (Header h:mockedHeaders) { 124 | headers.put(h.getName(), h.getValue()); 125 | } 126 | Assert.assertEquals(testResponse.getHeaders(), headers); 127 | } 128 | 129 | public void testMethod(Method method, int statusCode) { 130 | Response testResponse = new Response(); 131 | Request request = new Request(); 132 | Header[] mockedHeaders = null; 133 | try { 134 | when(statusline.getStatusCode()).thenReturn(statusCode); 135 | when(response.getStatusLine()).thenReturn(statusline); 136 | when(response.getEntity()).thenReturn( 137 | new InputStreamEntity( 138 | new ByteArrayInputStream( 139 | "{\"message\":\"success\"}".getBytes()))); 140 | mockedHeaders = new Header[] { new BasicHeader("headerA", "valueA") }; 141 | when(response.getAllHeaders()).thenReturn(mockedHeaders); 142 | when(httpClient.execute(Matchers.any(HttpGet.class))).thenReturn(response); 143 | request.setMethod(method); 144 | if ((method == Method.POST) || (method == Method.PATCH) || (method == Method.PUT)) { 145 | request.setBody("{\"test\":\"testResult\"}"); 146 | } 147 | request.setEndpoint("/test"); 148 | request.addHeader("Authorization", "Bearer XXXX"); 149 | Client client = new Client(httpClient); 150 | testResponse = client.get(request); 151 | } catch (URISyntaxException | IOException ex) { 152 | StringWriter errors = new StringWriter(); 153 | ex.printStackTrace(new PrintWriter(errors)); 154 | Assert.assertTrue(errors.toString(), false); 155 | } 156 | 157 | Assert.assertTrue(testResponse.getStatusCode() == statusCode); 158 | if (method != Method.DELETE) { 159 | Assert.assertEquals(testResponse.getBody(), "{\"message\":\"success\"}"); 160 | } 161 | Assert.assertEquals(testResponse.getBody(), "{\"message\":\"success\"}"); 162 | Map headers = new HashMap(); 163 | for (Header h:mockedHeaders) { 164 | headers.put(h.getName(), h.getValue()); 165 | } 166 | Assert.assertEquals(testResponse.getHeaders(), headers); 167 | } 168 | 169 | @Test 170 | public void testGet() { 171 | testMethod(Method.GET, 200); 172 | } 173 | 174 | @Test 175 | public void testPost() { 176 | testMethod(Method.POST, 201); 177 | } 178 | 179 | @Test 180 | public void testPatch() { 181 | testMethod(Method.PATCH, 200); 182 | } 183 | 184 | @Test 185 | public void testPut() { 186 | testMethod(Method.PUT, 200); 187 | } 188 | 189 | @Test 190 | public void testDelete() { 191 | testMethod(Method.DELETE, 204); 192 | } 193 | 194 | private boolean queryParamHasCorrectValue(URL url, String key, String value) { 195 | return url.getQuery().indexOf(key + "=" + value) != -1; 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | org.sonatype.oss 6 | oss-parent 7 | 7 8 | 9 | com.sendgrid 10 | java-http-client 11 | jar 12 | A simple HTTP client 13 | HTTP REST client, simplified for Java 14 | 4.5.1 15 | https://github.com/sendgrid/java-http-client 16 | 17 | 18 | The MIT License (MIT) 19 | https://github.com/sendgrid/java-http-client/blob/HEAD/LICENSE 20 | repo 21 | 22 | 23 | 24 | https://github.com/sendgrid/java-http-client 25 | scm:git:git@github.com:sendgrid/java-http-client.git 26 | scm:git:git@github.com:sendgrid/java-http-client.git 27 | 4.5.1 28 | 29 | 30 | 31 | release 32 | 33 | 34 | release 35 | 36 | 37 | 38 | 39 | 40 | org.sonatype.plugins 41 | nexus-staging-maven-plugin 42 | 1.6.8 43 | true 44 | 45 | ossrh 46 | https://oss.sonatype.org/ 47 | true 48 | 49 | 50 | 51 | org.apache.maven.plugins 52 | maven-source-plugin 53 | 3.0.1 54 | 55 | 56 | attach-sources 57 | 58 | jar-no-fork 59 | 60 | 61 | 62 | 63 | 64 | org.apache.maven.plugins 65 | maven-javadoc-plugin 66 | 2.10.4 67 | 68 | 69 | attach-javadocs 70 | 71 | jar 72 | 73 | 74 | 75 | 76 | 77 | org.apache.maven.plugins 78 | maven-gpg-plugin 79 | 1.6 80 | 81 | 82 | sign-artifacts 83 | verify 84 | 85 | sign 86 | 87 | 88 | ${gpg.keyname} 89 | ${gpg.passphrase} 90 | 91 | --pinentry-mode 92 | loopback 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 1.8 104 | 1.8 105 | 106 | 107 | 108 | junit 109 | junit-dep 110 | 4.11 111 | test 112 | 113 | 114 | org.mockito 115 | mockito-core 116 | 1.10.19 117 | test 118 | 119 | 120 | org.apache.httpcomponents 121 | httpcore 122 | 4.4.15 123 | 124 | 125 | org.apache.httpcomponents 126 | httpclient 127 | 4.5.13 128 | 129 | 130 | 131 | 132 | 133 | org.jacoco 134 | jacoco-maven-plugin 135 | 0.8.5 136 | 137 | 138 | 139 | prepare-agent 140 | 141 | 142 | 143 | report 144 | test 145 | 146 | report 147 | 148 | 149 | 150 | 151 | 152 | org.apache.maven.plugins 153 | maven-compiler-plugin 154 | 3.8.1 155 | 156 | 1.8 157 | 1.8 158 | 159 | 160 | 161 | org.apache.maven.plugins 162 | maven-release-plugin 163 | 2.4.2 164 | 165 | 166 | org.apache.maven.scm 167 | maven-scm-provider-gitexe 168 | 1.8.1 169 | 170 | 171 | 172 | 173 | org.apache.maven.plugins 174 | maven-source-plugin 175 | 2.2.1 176 | 177 | 178 | attach-sources 179 | 180 | jar 181 | 182 | 183 | 184 | 185 | 186 | org.apache.maven.plugins 187 | maven-javadoc-plugin 188 | 2.9.1 189 | 190 | 191 | attach-javadocs 192 | 193 | jar 194 | 195 | 196 | 197 | 198 | 199 | org.apache.maven.plugins 200 | maven-assembly-plugin 201 | 3.1.1 202 | 203 | 204 | jar-with-dependencies 205 | 206 | 207 | 208 | 209 | make-assembly 210 | package 211 | 212 | single 213 | 214 | 215 | 216 | 217 | 218 | 219 | -------------------------------------------------------------------------------- /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-05-18] Version 4.5.1 7 | -------------------------- 8 | **Library - Docs** 9 | - [PR #144](https://github.com/sendgrid/java-http-client/pull/144): Update to align with SendGrid Support. Thanks to [@garethpaul](https://github.com/garethpaul)! 10 | 11 | **Library - Fix** 12 | - [PR #143](https://github.com/sendgrid/java-http-client/pull/143): override default gh token. Thanks to [@beebzz](https://github.com/beebzz)! 13 | 14 | 15 | [2022-03-09] Version 4.5.0 16 | -------------------------- 17 | **Library - Chore** 18 | - [PR #142](https://github.com/sendgrid/java-http-client/pull/142): push Datadog Release Metric upon deploy success. Thanks to [@eshanholtz](https://github.com/eshanholtz)! 19 | 20 | **Library - Feature** 21 | - [PR #141](https://github.com/sendgrid/java-http-client/pull/141): add GH action to update dependencies. Thanks to [@JenniferMah](https://github.com/JenniferMah)! 22 | 23 | 24 | [2022-02-09] Version 4.4.0 25 | -------------------------- 26 | **Library - Chore** 27 | - [PR #139](https://github.com/sendgrid/java-http-client/pull/139): upgrade supported language versions. Thanks to [@childish-sambino](https://github.com/childish-sambino)! 28 | - [PR #138](https://github.com/sendgrid/java-http-client/pull/138): add gh release to workflow. Thanks to [@shwetha-manvinkurke](https://github.com/shwetha-manvinkurke)! 29 | - [PR #137](https://github.com/sendgrid/java-http-client/pull/137): merge test and deploy workflows. Thanks to [@shwetha-manvinkurke](https://github.com/shwetha-manvinkurke)! 30 | 31 | **Library - Feature** 32 | - [PR #134](https://github.com/sendgrid/java-http-client/pull/134): Support multiple duplicate queryparams. Thanks to [@mjjs](https://github.com/mjjs)! 33 | 34 | 35 | [2022-01-12] Version 4.3.9 36 | -------------------------- 37 | **Library - Chore** 38 | - [PR #136](https://github.com/sendgrid/java-http-client/pull/136): update license year. Thanks to [@JenniferMah](https://github.com/JenniferMah)! 39 | 40 | 41 | [2021-12-01] Version 4.3.8 42 | -------------------------- 43 | **Library - Chore** 44 | - [PR #135](https://github.com/sendgrid/java-http-client/pull/135): fix pom for release. Thanks to [@eshanholtz](https://github.com/eshanholtz)! 45 | - [PR #133](https://github.com/sendgrid/java-http-client/pull/133): migrate to github actions. Thanks to [@eshanholtz](https://github.com/eshanholtz)! 46 | 47 | 48 | [2021-05-19] Version 4.3.7 49 | -------------------------- 50 | **Library - Chore** 51 | - [PR #131](https://github.com/sendgrid/java-http-client/pull/131): Bump org.apache.httpcomponents.httpclient from 4.5.12 to 4.5.13. Thanks to [@akunzai](https://github.com/akunzai)! 52 | 53 | 54 | [2020-08-19] Version 4.3.6 55 | -------------------------- 56 | **Library - Chore** 57 | - [PR #128](https://github.com/sendgrid/java-http-client/pull/128): update GitHub branch references to use HEAD. Thanks to [@thinkingserious](https://github.com/thinkingserious)! 58 | 59 | **Library - Docs** 60 | - [PR #97](https://github.com/sendgrid/java-http-client/pull/97): Correcting *.md files using Grammarly. Thanks to [@pushkyn](https://github.com/pushkyn)! 61 | - [PR #101](https://github.com/sendgrid/java-http-client/pull/101): Add first timers file. Thanks to [@Varpie](https://github.com/Varpie)! 62 | 63 | 64 | [2020-08-05] Version 4.3.5 65 | -------------------------- 66 | **Library - Docs** 67 | - [PR #86](https://github.com/sendgrid/java-http-client/pull/86): Moved usage and enviorment variables to USAGE.md. Thanks to [@rareinator](https://github.com/rareinator)! 68 | 69 | 70 | [2020-07-22] Version 4.3.4 71 | -------------------------- 72 | **Library - Fix** 73 | - [PR #109](https://github.com/sendgrid/java-http-client/pull/109): correct the LICENSE.md link in pom.xml. Thanks to [@crweiner](https://github.com/crweiner)! 74 | 75 | **Library - Docs** 76 | - [PR #108](https://github.com/sendgrid/java-http-client/pull/108): Create a Use Cases Directory. Thanks to [@ajloria](https://github.com/ajloria)! 77 | 78 | 79 | [2020-03-04] Version 4.3.3 80 | -------------------------- 81 | **Library - Chore** 82 | - [PR #127](https://github.com/sendgrid/java-http-client/pull/127): fix JDK Travis failures. Thanks to [@childish-sambino](https://github.com/childish-sambino)! 83 | 84 | 85 | [2020-02-19] Version 4.3.2 86 | -------------------------- 87 | **Library - Chore** 88 | - [PR #111](https://github.com/sendgrid/java-http-client/pull/111): Update the Client file documentation. Thanks to [@vinifarias](https://github.com/vinifarias)! 89 | 90 | 91 | [2020-02-05] Version 4.3.1 92 | -------------------------- 93 | **Library - Docs** 94 | - [PR #126](https://github.com/sendgrid/java-http-client/pull/126): baseline all the templated markdown docs. Thanks to [@childish-sambino](https://github.com/childish-sambino)! 95 | 96 | 97 | [2020-02-01] Version 4.3.0 98 | -------------------------- 99 | **Library - Feature** 100 | - [PR #25](https://github.com/sendgrid/java-http-client/pull/25): do not close or manage lifecycle of http-client passed in. Thanks to [@maxxedev](https://github.com/maxxedev)! 101 | - [PR #34](https://github.com/sendgrid/java-http-client/pull/34): Add close method to Client. Thanks to [@tsuyoshizawa](https://github.com/tsuyoshizawa)! 102 | - [PR #67](https://github.com/sendgrid/java-http-client/pull/67): Adding Docker support. Thanks to [@mithunsasidharan](https://github.com/mithunsasidharan)! 103 | 104 | **Library - Fix** 105 | - [PR #26](https://github.com/sendgrid/java-http-client/pull/26): use .equals to compare strings. Thanks to [@maxxedev](https://github.com/maxxedev)! 106 | - [PR #36](https://github.com/sendgrid/java-http-client/pull/36): No longer throwing IOExceptions on non 2xx response codes. Thanks to [@andy-trimble](https://github.com/andy-trimble)! 107 | - [PR #77](https://github.com/sendgrid/java-http-client/pull/77): Closes #72 Update Example.java. Thanks to [@AbdulDroid](https://github.com/AbdulDroid)! 108 | 109 | **Library - Chore** 110 | - [PR #54](https://github.com/sendgrid/java-http-client/pull/54): Added example file, updated .gitignore and README. Thanks to [@dhsrocha](https://github.com/dhsrocha)! 111 | - [PR #58](https://github.com/sendgrid/java-http-client/pull/58): added .codeclimate,yml for codeclimate run. Thanks to [@skshelar](https://github.com/skshelar)! 112 | - [PR #73](https://github.com/sendgrid/java-http-client/pull/73): Fixes #71. Thanks to [@huytranrjc](https://github.com/huytranrjc)! 113 | - [PR #76](https://github.com/sendgrid/java-http-client/pull/76): Update travis - add codecov. Thanks to [@pushkyn](https://github.com/pushkyn)! 114 | - [PR #92](https://github.com/sendgrid/java-http-client/pull/92): update LICENSE - bump year. Thanks to [@pushkyn](https://github.com/pushkyn)! 115 | - [PR #115](https://github.com/sendgrid/java-http-client/pull/115): add [openjdk11] to Travis build. Thanks to [@sullis](https://github.com/sullis)! 116 | - [PR #117](https://github.com/sendgrid/java-http-client/pull/117): Update transitive dependencies. Thanks to [@kebeda](https://github.com/kebeda)! 117 | - [PR #123](https://github.com/sendgrid/java-http-client/pull/123): prep the repo for automated releases. Thanks to [@eshanholtz](https://github.com/eshanholtz)! 118 | 119 | **Library - Docs** 120 | - [PR #59](https://github.com/sendgrid/java-http-client/pull/59): Typos in CONTRIBUTING.md. Thanks to [@rkaranam](https://github.com/rkaranam)! 121 | - [PR #113](https://github.com/sendgrid/java-http-client/pull/113): Add our Developer Experience Engineer career opportunity to the READM…. Thanks to [@mptap](https://github.com/mptap)! 122 | 123 | **Library - Test** 124 | - [PR #66](https://github.com/sendgrid/java-http-client/pull/66): Test to check year in license file. Thanks to [@pushkyn](https://github.com/pushkyn)! 125 | - [PR #93](https://github.com/sendgrid/java-http-client/pull/93): removed tests that were testing for files that didnt exist, bumped gradle version to 4.10.2. Thanks to [@Strum355](https://github.com/Strum355)! 126 | 127 | 128 | [2017-10-30] Version 4.2.0 129 | --------------------------- 130 | ### Added 131 | - [Pull #22](https://github.com/sendgrid/java-http-client/pull/22): Allow setting both `apache http client` and `test` parameters 132 | - BIG thanks to [Maxim Novak](https://github.com/maximn) for the pull request! 133 | 134 | ## [4.1.1] - 2016-10-11 135 | ### Added 136 | - [Pull #23](https://github.com/sendgrid/java-http-client/pull/23): Moved Mockito to test dependency. 137 | - BIG thanks to [Joseph Lust](https://github.com/twistedpair) for the pull request! 138 | 139 | ## [4.1.0] - 2016-10-11 140 | ### Added 141 | - [Pull #17](https://github.com/sendgrid/java-http-client/pull/17): Assign server response outside try block 142 | - BIG thanks to [Michael Dempsey](https://github.com/bluestealth) for the pull request! 143 | 144 | ## [4.0.0] - 2016-10-11 145 | ### BREAKING Change 146 | - [Pull #14](https://github.com/sendgrid/java-http-client/pull/14): Make response have private variables 147 | - Fixed [Issue #12](https://github.com/sendgrid/java-http-client/issues/12): The public Response variables should be private 148 | - The breaking change is that variables that were public are now private and accessible only via getters and setters 149 | - BIG thanks to [Diego Camargo](https://github.com/belfazt) for the pull request! 150 | 151 | ## [3.0.0] - 2016-10-06 152 | ### BREAKING Change 153 | - [Pull #15](https://github.com/sendgrid/java-http-client/pull/15): Update the request object with sensible defaults and access methods 154 | - Fixes [Issue #13](https://github.com/sendgrid/java-http-client/issues/13): Update the Request object with sensible defaults and access methods 155 | - The breaking change is that variables that were public are now private and accessible only via getters and setters 156 | - BIG thanks to [Diego Camargo](https://github.com/belfazt) for the pull request! 157 | 158 | ## [2.3.4] - 2016-08-09 159 | ### Fix 160 | - [Pull #7](https://github.com/sendgrid/java-http-client/pull/7): Fix Response Charset to UTF-8 161 | - Fixes [issue #6](https://github.com/sendgrid/java-http-client/issues/6): Multi-byte character got garbled on received mail 162 | - BIG thanks to [Yoichi Kikuta](https://github.com/kikutaro) for the pull request! 163 | 164 | ## [2.3.3] - 2016-08-08 165 | ### Added 166 | - Pull request [#11](https://github.com/sendgrid/java-http-client/pull/11) 167 | - Solves [issue #10](https://github.com/sendgrid/java-http-client/issues/10): Improve Error Handling 168 | - Now error messages are passed through from the server 169 | - BIG thanks to [shuron](https://github.com/shuron) / [Alexander Holbreich](https://github.com/aholbreich) for the pull request! 170 | 171 | ## [2.3.2] - 2016-07-18 172 | ### Fixed 173 | - 2.3.1 did not upload correctly 174 | 175 | ## [2.3.1] - 2016-07-08 176 | ### Fixed 177 | - [Fix charset: Use "UTF-8" charset instead of default "ISO-8859-1"](https://github.com/sendgrid/java-http-client/pull/5) 178 | - Thanks [DanailMinchev](https://github.com/DanailMinchev)! 179 | 180 | ## [2.3.0] - 2016-06-10 181 | ### Added 182 | - Automatically add Content-Type: application/json when there is a request body 183 | 184 | ## [2.2.1] - 2016-06-08 185 | ### Fixed 186 | - Set client properly when testing 187 | 188 | ## [2.2.0] - 2016-06-08 189 | ### Added 190 | - Can pass test flag to allow for http calls 191 | 192 | ## [2.1.0] - 2016-06-08 193 | ### Added 194 | - DELETE can now have a request body 195 | 196 | ## [2.0.0] - 2016-06-03 197 | ### Changed 198 | - Made the Request and Response variables non-redundant. e.g. request.requestBody becomes request.body 199 | 200 | ## [1.0.0] - 2016-04-15 201 | ### Added 202 | - We are live! 203 | -------------------------------------------------------------------------------- /src/main/java/com/sendgrid/Client.java: -------------------------------------------------------------------------------- 1 | package com.sendgrid; 2 | 3 | import java.io.Closeable; 4 | import java.io.IOException; 5 | import java.io.PrintWriter; 6 | import java.io.StringWriter; 7 | import java.net.URI; 8 | import java.net.URISyntaxException; 9 | import java.nio.charset.Charset; 10 | import java.util.HashMap; 11 | import java.util.Map; 12 | import java.util.Arrays; 13 | import java.util.List; 14 | 15 | import org.apache.http.Header; 16 | import org.apache.http.HttpMessage; 17 | import org.apache.http.client.ClientProtocolException; 18 | import org.apache.http.client.ResponseHandler; 19 | import org.apache.http.client.methods.CloseableHttpResponse; 20 | import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; 21 | import org.apache.http.client.methods.HttpGet; 22 | import org.apache.http.client.methods.HttpPatch; 23 | import org.apache.http.client.methods.HttpPost; 24 | import org.apache.http.client.methods.HttpPut; 25 | import org.apache.http.client.methods.HttpRequestBase; 26 | import org.apache.http.client.utils.URIBuilder; 27 | import org.apache.http.entity.StringEntity; 28 | import org.apache.http.impl.client.CloseableHttpClient; 29 | import org.apache.http.impl.client.HttpClients; 30 | 31 | 32 | /** 33 | * Hack to get DELETE to accept a request body. 34 | */ 35 | class HttpDeleteWithBody extends HttpEntityEnclosingRequestBase { 36 | public static final String METHOD_NAME = "DELETE"; 37 | 38 | @Override 39 | public String getMethod() { 40 | return METHOD_NAME; 41 | } 42 | 43 | public HttpDeleteWithBody(final String uri) { 44 | super(); 45 | setURI(URI.create(uri)); 46 | } 47 | } 48 | 49 | 50 | /** 51 | * Class Client allows for quick and easy access any REST or REST-like API. 52 | */ 53 | public class Client implements Closeable { 54 | 55 | private CloseableHttpClient httpClient; 56 | private Boolean test; 57 | private boolean createdHttpClient; 58 | 59 | 60 | /** 61 | * Constructor for using the default CloseableHttpClient. 62 | */ 63 | public Client() { 64 | this.httpClient = HttpClients.createDefault(); 65 | this.test = false; 66 | this.createdHttpClient = true; 67 | } 68 | 69 | 70 | /** 71 | * Constructor for passing in an httpClient, typically for mocking. Passed-in httpClient will not be closed 72 | * by this Client. 73 | * 74 | * @param httpClient 75 | * an Apache CloseableHttpClient 76 | */ 77 | public Client(CloseableHttpClient httpClient) { 78 | this(httpClient, false); 79 | } 80 | 81 | 82 | /** 83 | * Constructor for passing in a test parameter to allow for http calls. 84 | * 85 | * @param test 86 | * is a Bool 87 | */ 88 | public Client(Boolean test) { 89 | this(HttpClients.createDefault(), test); 90 | } 91 | 92 | 93 | /** 94 | * Constructor for passing in an httpClient and test parameter to allow for http calls. 95 | * 96 | * @param httpClient 97 | * an Apache CloseableHttpClient 98 | * @param test 99 | * is a Bool 100 | */ 101 | public Client(CloseableHttpClient httpClient, Boolean test) { 102 | this.httpClient = httpClient; 103 | this.test = test; 104 | this.createdHttpClient = true; 105 | } 106 | 107 | 108 | /** 109 | * Add query parameters to a URL. 110 | * 111 | * @param baseUri 112 | * (e.g. "api.sendgrid.com") 113 | * @param endpoint 114 | * (e.g. "/your/endpoint/path") 115 | * @param queryParams 116 | * map of key, values representing the query parameters 117 | * @throws URISyntaxException 118 | * in of a URI syntax error 119 | */ 120 | public URI buildUri(String baseUri, String endpoint, Map queryParams) throws URISyntaxException { 121 | URIBuilder builder = new URIBuilder(); 122 | URI uri = null; 123 | 124 | if (this.test == true) { 125 | builder.setScheme("http"); 126 | } else { 127 | builder.setScheme("https"); 128 | } 129 | 130 | builder.setHost(baseUri); 131 | builder.setPath(endpoint); 132 | 133 | if (queryParams != null) { 134 | String multiValueDelimiter = "&"; 135 | 136 | for (Map.Entry entry : queryParams.entrySet()) { 137 | String value = entry.getValue(); 138 | 139 | if (value.indexOf(multiValueDelimiter) != -1) { 140 | List values = Arrays.asList(value.split(multiValueDelimiter)); 141 | for (String val : values) { 142 | builder.addParameter(entry.getKey(), val); 143 | } 144 | } else { 145 | builder.setParameter(entry.getKey(), entry.getValue()); 146 | } 147 | } 148 | } 149 | 150 | try { 151 | uri = builder.build(); 152 | } catch (URISyntaxException ex) { 153 | throw ex; 154 | } 155 | 156 | return uri; 157 | } 158 | 159 | 160 | /** 161 | * Prepare a Response object from an API call via Apache's HTTP client. 162 | * 163 | * @param response 164 | * from a call to a CloseableHttpClient 165 | * @throws IOException 166 | * in case of a network error 167 | * @return the response object 168 | */ 169 | public Response getResponse(CloseableHttpResponse response) throws IOException { 170 | ResponseHandler handler = new SendGridResponseHandler(); 171 | String responseBody = handler.handleResponse(response); 172 | 173 | int statusCode = response.getStatusLine().getStatusCode(); 174 | 175 | Header[] headers = response.getAllHeaders(); 176 | Map responseHeaders = new HashMap(); 177 | for (Header h : headers) { 178 | responseHeaders.put(h.getName(), h.getValue()); 179 | } 180 | 181 | return new Response(statusCode, responseBody, responseHeaders); 182 | } 183 | 184 | 185 | /** 186 | * Make a GET request and provide the status code, response body and 187 | * response headers. 188 | * 189 | * @param request 190 | * the request object 191 | * @throws URISyntaxException 192 | * in case of a URI syntax error 193 | * @throws IOException 194 | * in case of a network error 195 | * @return the response object 196 | */ 197 | public Response get(Request request) throws URISyntaxException, IOException { 198 | URI uri = null; 199 | HttpGet httpGet = null; 200 | 201 | try { 202 | uri = buildUri(request.getBaseUri(), request.getEndpoint(), request.getQueryParams()); 203 | httpGet = new HttpGet(uri.toString()); 204 | } catch (URISyntaxException ex) { 205 | throw ex; 206 | } 207 | 208 | if (request.getHeaders() != null) { 209 | for (Map.Entry entry : request.getHeaders().entrySet()) { 210 | httpGet.setHeader(entry.getKey(), entry.getValue()); 211 | } 212 | } 213 | return executeApiCall(httpGet); 214 | } 215 | 216 | 217 | /** 218 | * Make a POST request and provide the status code, response body and 219 | * response headers. 220 | * 221 | * @param request 222 | * the request object 223 | * @throws URISyntaxException 224 | * in case of a URI syntax error 225 | * @throws IOException 226 | * in case of a network error 227 | * @return the response object 228 | */ 229 | public Response post(Request request) throws URISyntaxException, IOException { 230 | URI uri = null; 231 | HttpPost httpPost = null; 232 | 233 | try { 234 | uri = buildUri(request.getBaseUri(), request.getEndpoint(), request.getQueryParams()); 235 | httpPost = new HttpPost(uri.toString()); 236 | } catch (URISyntaxException ex) { 237 | throw ex; 238 | } 239 | 240 | if (request.getHeaders() != null) { 241 | for (Map.Entry entry : request.getHeaders().entrySet()) { 242 | httpPost.setHeader(entry.getKey(), entry.getValue()); 243 | } 244 | } 245 | 246 | httpPost.setEntity(new StringEntity(request.getBody(), Charset.forName("UTF-8"))); 247 | writeContentTypeIfNeeded(request, httpPost); 248 | 249 | return executeApiCall(httpPost); 250 | } 251 | 252 | 253 | /** 254 | * Make a PATCH request and provide the status code, response body and 255 | * response headers. 256 | * 257 | * @param request 258 | * the request object 259 | * @throws URISyntaxException 260 | * in case of a URI syntax error 261 | * @throws IOException 262 | * in case of a network error 263 | * @return the response object 264 | */ 265 | public Response patch(Request request) throws URISyntaxException, IOException { 266 | URI uri = null; 267 | HttpPatch httpPatch = null; 268 | 269 | try { 270 | uri = buildUri(request.getBaseUri(), request.getEndpoint(), request.getQueryParams()); 271 | httpPatch = new HttpPatch(uri.toString()); 272 | } catch (URISyntaxException ex) { 273 | throw ex; 274 | } 275 | 276 | if (request.getHeaders() != null) { 277 | for (Map.Entry entry : request.getHeaders().entrySet()) { 278 | httpPatch.setHeader(entry.getKey(), entry.getValue()); 279 | } 280 | } 281 | 282 | httpPatch.setEntity(new StringEntity(request.getBody(), Charset.forName("UTF-8"))); 283 | writeContentTypeIfNeeded(request, httpPatch); 284 | 285 | return executeApiCall(httpPatch); 286 | } 287 | 288 | 289 | /** 290 | * Make a PUT request and provide the status code, response body and 291 | * response headers. 292 | * 293 | * @param request 294 | * the request object 295 | * @throws URISyntaxException 296 | * in case of a URI syntax error 297 | * @throws IOException 298 | * in case of a network error 299 | * @return the response object 300 | */ 301 | public Response put(Request request) throws URISyntaxException, IOException { 302 | URI uri = null; 303 | HttpPut httpPut = null; 304 | 305 | try { 306 | uri = buildUri(request.getBaseUri(), request.getEndpoint(), request.getQueryParams()); 307 | httpPut = new HttpPut(uri.toString()); 308 | } catch (URISyntaxException ex) { 309 | throw ex; 310 | } 311 | 312 | if (request.getHeaders() != null) { 313 | for (Map.Entry entry : request.getHeaders().entrySet()) { 314 | httpPut.setHeader(entry.getKey(), entry.getValue()); 315 | } 316 | } 317 | 318 | httpPut.setEntity(new StringEntity(request.getBody(), Charset.forName("UTF-8"))); 319 | writeContentTypeIfNeeded(request, httpPut); 320 | 321 | return executeApiCall(httpPut); 322 | } 323 | 324 | 325 | /** 326 | * Make a DELETE request and provide the status code and response headers. 327 | * 328 | * @param request 329 | * the request object 330 | * @throws URISyntaxException 331 | * in case of a URI syntax error 332 | * @throws IOException 333 | * in case of a network error 334 | * @return the response object 335 | */ 336 | public Response delete(Request request) throws URISyntaxException, IOException { 337 | URI uri = null; 338 | HttpDeleteWithBody httpDelete = null; 339 | 340 | try { 341 | uri = buildUri(request.getBaseUri(), request.getEndpoint(), request.getQueryParams()); 342 | httpDelete = new HttpDeleteWithBody(uri.toString()); 343 | } catch (URISyntaxException ex) { 344 | throw ex; 345 | } 346 | 347 | if (request.getHeaders() != null) { 348 | for (Map.Entry entry : request.getHeaders().entrySet()) { 349 | httpDelete.setHeader(entry.getKey(), entry.getValue()); 350 | } 351 | } 352 | 353 | httpDelete.setEntity(new StringEntity(request.getBody(), Charset.forName("UTF-8"))); 354 | writeContentTypeIfNeeded(request, httpDelete); 355 | 356 | return executeApiCall(httpDelete); 357 | } 358 | 359 | private void writeContentTypeIfNeeded(Request request, HttpMessage httpMessage) { 360 | if (!"".equals(request.getBody())) { 361 | httpMessage.setHeader("Content-Type", "application/json"); 362 | } 363 | } 364 | 365 | 366 | /** 367 | * Makes a call to the client API. 368 | * 369 | * @param httpPost 370 | * the request method object 371 | * @throws IOException 372 | * in case of a network error 373 | * @return the response object 374 | */ 375 | private Response executeApiCall(HttpRequestBase httpPost) throws IOException { 376 | try { 377 | CloseableHttpResponse serverResponse = httpClient.execute(httpPost); 378 | try { 379 | return getResponse(serverResponse); 380 | } finally { 381 | serverResponse.close(); 382 | } 383 | } catch(ClientProtocolException e) { 384 | throw new IOException(e.getMessage()); 385 | } 386 | } 387 | 388 | 389 | /** 390 | * A thin wrapper around the HTTP methods. 391 | * 392 | * @param request 393 | * the request object 394 | * @throws IOException 395 | * in case of a network error 396 | * @return the response object 397 | */ 398 | public Response api(Request request) throws IOException { 399 | try { 400 | if (request.getMethod() == null) { 401 | throw new IOException("We only support GET, PUT, PATCH, POST and DELETE."); 402 | } 403 | switch (request.getMethod()) { 404 | case GET: 405 | return get(request); 406 | case POST: 407 | return post(request); 408 | case PUT: 409 | return put(request); 410 | case PATCH: 411 | return patch(request); 412 | case DELETE: 413 | return delete(request); 414 | default: 415 | throw new IOException("We only support GET, PUT, PATCH, POST and DELETE."); 416 | } 417 | } catch (IOException ex) { 418 | throw ex; 419 | } catch (URISyntaxException ex) { 420 | StringWriter errors = new StringWriter(); 421 | ex.printStackTrace(new PrintWriter(errors)); 422 | throw new IOException(errors.toString()); 423 | } 424 | } 425 | 426 | 427 | /** 428 | * Closes the http client. 429 | * 430 | * @throws IOException 431 | * in case of a network error 432 | */ 433 | @Override 434 | public void close() throws IOException { 435 | this.httpClient.close(); 436 | } 437 | 438 | 439 | /** 440 | * Closes and finalizes the http client. 441 | * 442 | * @throws Throwable 443 | * in case of an error 444 | */ 445 | @Override 446 | public void finalize() throws Throwable { 447 | try { 448 | close(); 449 | } catch(IOException e) { 450 | throw new Throwable(e.getMessage()); 451 | } finally { 452 | super.finalize(); 453 | } 454 | } 455 | } 456 | --------------------------------------------------------------------------------