pax_global_header00006660000000000000000000000064142122061420014504gustar00rootroot0000000000000052 comment=e57bc908da6d0fce76457392c6c8444031032ff4 rest-2.6.9/000077500000000000000000000000001421220614200124775ustar00rootroot00000000000000rest-2.6.9/.env_sample000066400000000000000000000000321421220614200146240ustar00rootroot00000000000000export SENDGRID_API_KEY=''rest-2.6.9/.github/000077500000000000000000000000001421220614200140375ustar00rootroot00000000000000rest-2.6.9/.github/ISSUE_TEMPLATE/000077500000000000000000000000001421220614200162225ustar00rootroot00000000000000rest-2.6.9/.github/ISSUE_TEMPLATE/config.yml000066400000000000000000000005551421220614200202170ustar00rootroot00000000000000contact_links: - name: Twilio SendGrid Support url: https://support.sendgrid.com about: Get Support - name: Stack Overflow url: https://stackoverflow.com/questions/tagged/rest+or+sendgrid+go about: Ask questions on Stack Overflow - name: Documentation url: https://sendgrid.com/docs/for-developers/ about: View Reference Documentation rest-2.6.9/.github/workflows/000077500000000000000000000000001421220614200160745ustar00rootroot00000000000000rest-2.6.9/.github/workflows/test.yml000066400000000000000000000042251421220614200176010ustar00rootroot00000000000000name: Test and Deploy on: push: branches: [ '*' ] tags: [ '*' ] pull_request: branches: [ main ] schedule: # Run automatically at 8AM PST Monday-Friday - cron: '0 15 * * 1-5' workflow_dispatch: jobs: test: name: Build & Test runs-on: ubuntu-latest timeout-minutes: 20 strategy: matrix: go: [ '1.14', '1.15', '1.16', '1.17' ] steps: - name: Checkout rest uses: actions/checkout@v2 - name: Setup Go environment uses: actions/setup-go@v2 with: go-version: ${{ matrix.go }} - name: Set Go env vars run: | echo "GOPATH=$HOME" >> $GITHUB_ENV echo "GOBIN=$HOME/bin" >> $GITHUB_ENV echo "GO111MODULE=off" >> $GITHUB_ENV - name: Run Tests run: make test deploy: name: Deploy if: success() && github.ref_type == 'tag' needs: [ test ] runs-on: ubuntu-latest steps: - name: Checkout rest uses: actions/checkout@v2 - name: Create GitHub Release uses: sendgrid/dx-automator/actions/release@main env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Submit metric to Datadog uses: sendgrid/dx-automator/actions/datadog-release-metric@main env: DD_API_KEY: ${{ secrets.DATADOG_API_KEY }} notify-on-failure: name: Slack notify on failure if: failure() && github.event_name != 'pull_request' && (github.ref == 'refs/heads/main' || github.ref_type == 'tag') needs: [ test, deploy ] runs-on: ubuntu-latest steps: - uses: rtCamp/action-slack-notify@v2 env: SLACK_COLOR: failure SLACK_ICON_EMOJI: ':github:' SLACK_MESSAGE: ${{ format('Test *{0}*, Deploy *{1}*, {2}/{3}/actions/runs/{4}', needs.test.result, needs.deploy.result, github.server_url, github.repository, github.run_id) }} SLACK_TITLE: Action Failure - ${{ github.repository }} SLACK_USERNAME: GitHub Actions SLACK_MSG_AUTHOR: twilio-dx SLACK_FOOTER: Posted automatically using GitHub Actions SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} MSG_MINIMAL: true rest-2.6.9/.gitignore000066400000000000000000000004471421220614200144740ustar00rootroot00000000000000# Compiled Object files, Static and Dynamic libs (Shared Objects) *.o *.a *.so # Folders _obj _test # Architecture specific extensions/prefixes *.[568vq] [568vq].out *.cgo1.go *.cgo2.c _cgo_defun.c _cgo_gotypes.go _cgo_export.* _testmain.go *.exe *.test *.prof .env .settings.json temp.go rest-2.6.9/CHANGELOG.md000066400000000000000000000151071421220614200143140ustar00rootroot00000000000000# Change Log All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). [2022-03-09] Version 2.6.9 -------------------------- **Library - Chore** - [PR #110](https://github.com/sendgrid/rest/pull/110): push Datadog Release Metric upon deploy success. Thanks to [@eshanholtz](https://github.com/eshanholtz)! [2022-02-09] Version 2.6.8 -------------------------- **Library - Chore** - [PR #109](https://github.com/sendgrid/rest/pull/109): upgrade supported language versions. Thanks to [@childish-sambino](https://github.com/childish-sambino)! - [PR #108](https://github.com/sendgrid/rest/pull/108): add gh release to workflow. Thanks to [@shwetha-manvinkurke](https://github.com/shwetha-manvinkurke)! [2022-01-12] Version 2.6.7 -------------------------- **Library - Chore** - [PR #107](https://github.com/sendgrid/rest/pull/107): update license year. Thanks to [@JenniferMah](https://github.com/JenniferMah)! [2021-12-15] Version 2.6.6 -------------------------- **Library - Chore** - [PR #106](https://github.com/sendgrid/rest/pull/106): migrate to gh actions. Thanks to [@beebzz](https://github.com/beebzz)! [2021-09-22] Version 2.6.5 -------------------------- **Library - Chore** - [PR #105](https://github.com/sendgrid/rest/pull/105): add tests against v1.16. Thanks to [@shwetha-manvinkurke](https://github.com/shwetha-manvinkurke)! [2021-05-05] Version 2.6.4 -------------------------- **Library - Chore** - [PR #103](https://github.com/sendgrid/rest/pull/103): follow up context.Context support. Thanks to [@johejo](https://github.com/johejo)! [2021-03-15] Version 2.6.3 -------------------------- **Library - Fix** - [PR #92](https://github.com/sendgrid/rest/pull/92): add SendWithContext function. Thanks to [@someone1](https://github.com/someone1)! [2020-10-14] Version 2.6.2 -------------------------- **Library - Fix** - [PR #101](https://github.com/sendgrid/rest/pull/101): Pass empty client instead of http.DefaultClient. Thanks to [@mateorider](https://github.com/mateorider)! [2020-08-19] Version 2.6.1 -------------------------- **Library - Chore** - [PR #100](https://github.com/sendgrid/rest/pull/100): update GitHub branch references to use HEAD. Thanks to [@thinkingserious](https://github.com/thinkingserious)! [2020-02-19] Version 2.6.0 -------------------------- **Library - Feature** - [PR #73](https://github.com/sendgrid/rest/pull/73): Dockerize sendgrid/rest. Thanks to [@graystevens](https://github.com/graystevens)! [2020-02-05] Version 2.5.1 -------------------------- **Library - Docs** - [PR #77](https://github.com/sendgrid/rest/pull/77): Run Grammarly on *.md files. Thanks to [@obahareth](https://github.com/obahareth)! - [PR #86](https://github.com/sendgrid/rest/pull/86): Fixed link to bug report template. Thanks to [@alxshelepenok](https://github.com/alxshelepenok)! [2020-01-30] Version 2.5.0 -------------------------- **Library - Docs** - [PR #97](https://github.com/sendgrid/rest/pull/97): baseline all the templated markdown docs. Thanks to [@childish-sambino](https://github.com/childish-sambino)! - [PR #88](https://github.com/sendgrid/rest/pull/88): add our Developer Experience Engineer career opportunity to the README. Thanks to [@mptap](https://github.com/mptap)! - [PR #65](https://github.com/sendgrid/rest/pull/65): added "Code Review" section to CONTRIBUTING.md. Thanks to [@aleien](https://github.com/aleien)! - [PR #80](https://github.com/sendgrid/rest/pull/80): add first timers guide for newcomers. Thanks to [@daniloff200](https://github.com/daniloff200)! - [PR #82](https://github.com/sendgrid/rest/pull/82): update contribution guide with new workflow. Thanks to [@radlinskii](https://github.com/radlinskii)! - [PR #62](https://github.com/sendgrid/rest/pull/62): update CONTRIBUTING.md with environment variables section. Thanks to [@thepriefy](https://github.com/thepriefy)! **Library - Chore** - [PR #96](https://github.com/sendgrid/rest/pull/96): prep repo for automation. Thanks to [@thinkingserious](https://github.com/thinkingserious)! - [PR #94](https://github.com/sendgrid/rest/pull/94): add current Go version to Travis. Thanks to [@pangaunn](https://github.com/pangaunn)! - [PR #93](https://github.com/sendgrid/rest/pull/93): add current Go versions to Travis. Thanks to [@gliptak](https://github.com/gliptak)! - [PR #83](https://github.com/sendgrid/rest/pull/83): follow godoc deprecation standards. Thanks to [@vaskoz](https://github.com/vaskoz)! - [PR #74](https://github.com/sendgrid/rest/pull/74): create README.md in use-cases. Thanks to [@ajloria](https://github.com/ajloria)! **Library - Feature** - [PR #72](https://github.com/sendgrid/rest/pull/72): do not swallow the error code. Thanks to [@Succo](https://github.com/Succo)! [2018-04-09] Version 2.4.1 -------------------------- ### Fixed - Pull #71, Solves #70 - Fix Travis CI Build - Special thanks to [Vasko Zdravevski](https://github.com/vaskoz) for the PR! ## [2.4.0] - 2017-4-10 ### Added - Pull #18, Solves #17 - Add RestError Struct for an error handling - Special thanks to [Takahiro Ikeuchi](https://github.com/iktakahiro) for the PR! ## [2.3.1] - 2016-10-14 ### Changed - Pull #15, solves Issue #7 - Moved QueryParams processing into BuildRequestObject - Special thanks to [Gábor Lipták](https://github.com/gliptak) for the PR! ## [2.3.0] - 2016-10-04 ### Added - Pull [#10] [Allow for custom Content-Types](https://github.com/sendgrid/rest/issues/10) ## [2.2.0] - 2016-07-28 ### Added - Pull [#9](https://github.com/sendgrid/rest/pull/9): Allow for setting a custom HTTP client - [Here](rest_test.go#L127) is an example of usage - This enables usage of the [sendgrid-go library](https://github.com/sendgrid/sendgrid-go) on [Google App Engine (GAE)](https://cloud.google.com/appengine/) - Special thanks to [Chris Broadfoot](https://github.com/broady) and [Sridhar Venkatakrishnan](https://github.com/sridharv) for providing code and feedback! ## [2.1.0] - 2016-06-10 ### Added - Automatically add Content-Type: application/json when there is a request body ## [2.0.0] - 2016-06-03 ### Changed - Made the Request and Response variables non-redundant. e.g. request.RequestBody becomes request.Body ## [1.0.2] - 2016-04-07 ### Added - these changes are thanks to [deckarep](https://github.com/deckarep). Thanks! - more updates to error naming convention - more error handing on HTTP request ## [1.0.1] - 2016-04-07 ### Added - these changes are thanks to [deckarep](https://github.com/deckarep). Thanks! - update error naming convention - explicitly define supported HTTP verbs - better error handling on HTTP request ## [1.0.0] - 2016-04-05 ### Added - We are live! rest-2.6.9/CODE_OF_CONDUCT.md000066400000000000000000000062631421220614200153050ustar00rootroot00000000000000# Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: - Using welcoming and inclusive language - Being respectful of differing viewpoints and experiences - Gracefully accepting constructive criticism - Focusing on what is best for the community - Showing empathy towards other community members Examples of unacceptable behavior by participants include: - The use of sexualized language or imagery and unwelcome sexual attention or advances - Trolling, insulting/derogatory comments, and personal or political attacks - Public or private harassment - Publishing others' private information, such as a physical or electronic address, without explicit permission - Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at open-source@twilio.com. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html [homepage]: https://www.contributor-covenant.org rest-2.6.9/CONTRIBUTING.md000066400000000000000000000134711421220614200147360ustar00rootroot00000000000000Hello! 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. - [Feature Request](#feature-request) - [Submit a Bug Report](#submit-a-bug-report) - [Improvements to the Codebase](#improvements-to-the-codebase) - [Understanding the Code Base](#understanding-the-codebase) - [Testing](#testing) - [Style Guidelines & Naming Conventions](#style-guidelines-and-naming-conventions) - [Creating a Pull Request](#creating-a-pull-request) - [Code Reviews](#code-reviews) ## Feature Request If you'd like to make a feature request, please read this section. The GitHub issue tracker is the preferred channel for library feature requests, but please respect the following restrictions: - Please **search for existing issues** in order to ensure we don't have duplicate bugs/feature requests. - Please be respectful and considerate of others when commenting on issues ## Submit a Bug Report Note: DO NOT include your credentials in ANY code examples, descriptions, or media you make public. A software bug is a demonstrable issue in the code base. In order for us to diagnose the issue and respond as quickly as possible, please add as much detail as possible into your bug report. Before you decide to create a new issue, please try the following: 1. Check the Github issues tab if the identified issue has already been reported, if so, please add a +1 to the existing post. 2. Update to the latest version of this code and check if issue has already been fixed 3. Copy and fill in the Bug Report Template we have provided below ### Please use our Bug Report Template In order to make the process easier, we've included a [sample bug report template](ISSUE_TEMPLATE.md). ## Improvements to the Codebase We welcome direct contributions to the rest code base. Thank you! ### Development Environment ### #### Install and Run Locally #### ##### Supported Versions ##### - Go version 1.14, 1.15 or 1.16 ##### Initial setup: ##### ```bash git clone https://github.com/sendgrid/rest.git cd rest ``` ### Environment Variables First, get your free SendGrid account [here](https://sendgrid.com/free?source=rest). Next, update your environment with your [SENDGRID_API_KEY](https://app.sendgrid.com/settings/api_keys) if you will test with Swift Mailer. ``` echo "export SENDGRID-API-KEY='YOUR-API-KEY'" > sendgrid.env echo "sendgrid.env" >> .gitignore source ./sendgrid.env go run examples/example.go ``` ##### Execute: ##### See the [examples folder](examples) to get started quickly. ## Understanding the Code Base **/examples** Working examples that demonstrate usage. **rest.go** There is a struct to hold both the request and response to the API server. The main function that does the heavy lifting (and external entry point) is `API`. ## Testing All PRs require passing tests before the PR will be reviewed. All test files are in [`rest-test.go`](rest_test.go). For the purposes of contributing to this repo, please update the [`rest-test.go`](rest_test.go) file with unit tests as you modify the code. Run the test: ```bash go test -v ``` ## Style Guidelines & Naming Conventions 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. - [Go Code Review Comments](https://github.com/golang/go/wiki/CodeReviewComments) Please run your code through: - [fmt](https://blog.golang.org/go-fmt-your-code) ## Creating a Pull Request 1. [Fork](https://help.github.com/fork-a-repo/) the project, clone your fork, and configure the remotes: ```bash # Clone your fork of the repo into the current directory git clone https://github.com/sendgrid/rest # Navigate to the newly cloned directory cd rest # Assign the original repo to a remote called "upstream" git remote add upstream https://github.com/sendgrid/rest ``` 2. If you cloned a while ago, get the latest changes from upstream: ```bash git checkout development git pull upstream development ``` 3. Create a new topic branch off the `development` branch to contain your feature, change, or fix: ```bash git checkout -b ``` 4. Commit your changes in logical chunks. Please adhere to these [git commit message guidelines](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) or your code is unlikely to be merged into the main project. Use Git's [interactive rebase](https://help.github.com/articles/interactive-rebase) feature to tidy up your commits before making them public. 4a. Create tests. 4b. Create or update the example code that demonstrates the functionality of this change to the code. 5. Locally merge (or rebase) the upstream development branch into your topic branch: ```bash git pull [--rebase] upstream development ``` 6. Push your topic branch up to your fork: ```bash git push origin ``` 7. [Open a Pull Request](https://help.github.com/articles/using-pull-requests/) with a clear title and description against the `development` branch. All tests must be passing before we will review the PR. ## Code Reviews If you can, please look at open PRs and review them. Give feedback and help us merge these PRs much faster! If you don't know how, Github has some great [information on how to review a Pull Request](https://help.github.com/articles/about-pull-request-reviews/). rest-2.6.9/FIRST_TIMERS.md000066400000000000000000000151241421220614200147760ustar00rootroot00000000000000# How To Contribute to Twilio SendGrid Repositories via GitHub Contributing to the Twilio SendGrid repositories is easy! All you need to do is find an open issue (see the bottom of this page for a list of repositories containing open issues), fix it and submit a pull request. Once you have submitted your pull request, the team can easily review it before it is merged into the repository. To make a pull request, follow these steps: 1. Log into GitHub. If you do not already have a GitHub account, you will have to create one in order to submit a change. Click the Sign up link in the upper right-hand corner to create an account. Enter your username, password, and email address. If you are an employee of Twilio SendGrid, please use your full name with your GitHub account and enter Twilio SendGrid as your company so we can easily identify you. 2. __[Fork](https://help.github.com/fork-a-repo/)__ the [rest](https://github.com/sendgrid/rest) repository: 3. __Clone__ your fork via the following commands: ```bash # Clone your fork of the repo into the current directory git clone https://github.com/your_username/rest # Navigate to the newly cloned directory cd rest # Assign the original repo to a remote called "upstream" git remote add upstream https://github.com/sendgrid/rest ``` > Don't forget to replace *your_username* in the URL by your real GitHub username. 4. __Create a new topic branch__ (off the main project development branch) to contain your feature, change, or fix: ```bash git checkout -b ``` 5. __Commit your changes__ in logical chunks. Please adhere to these [git commit message guidelines](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) or your code is unlikely be merged into the main project. Use Git's [interactive rebase](https://help.github.com/articles/interactive-rebase) feature to tidy up your commits before making them public. Probably you will also have to create tests (if needed) or create or update the example code that demonstrates the functionality of this change to the code. 6. __Locally merge (or rebase)__ the upstream development branch into your topic branch: ```bash git pull [--rebase] upstream main ``` 7. __Push__ your topic branch up to your fork: ```bash git push origin ``` 8. __[Open a Pull Request](https://help.github.com/articles/creating-a-pull-request/#changing-the-branch-range-and-destination-repository/)__ with a clear title and description against the `main` branch. All tests must be passing before we will review the PR. ## Important notice Before creating a pull request, make sure that you respect the repository's constraints regarding contributions. You can find them in the [CONTRIBUTING.md](CONTRIBUTING.md) file. ## Repositories with Open, Easy, Help Wanted, Issue Filters * [Python SDK](https://github.com/sendgrid/sendgrid-python/issues?utf8=%E2%9C%93&q=is%3Aopen+label%3A%22difficulty%3A+easy%22+label%3A%22status%3A+help+wanted%22) * [PHP SDK](https://github.com/sendgrid/sendgrid-php/issues?utf8=%E2%9C%93&q=is%3Aopen+label%3A%22difficulty%3A+easy%22+label%3A%22status%3A+help+wanted%22) * [C# SDK](https://github.com/sendgrid/sendgrid-csharp/issues?utf8=%E2%9C%93&q=is%3Aopen+label%3A%22difficulty%3A+easy%22+label%3A%22status%3A+help+wanted%22) * [Ruby SDK](https://github.com/sendgrid/sendgrid-ruby/issues?utf8=%E2%9C%93&q=is%3Aopen+label%3A%22difficulty%3A+easy%22+label%3A%22status%3A+help+wanted%22) * [Node.js SDK](https://github.com/sendgrid/sendgrid-nodejs/issues?utf8=%E2%9C%93&q=is%3Aopen+label%3A%22difficulty%3A+easy%22+label%3A%22status%3A+help+wanted%22) * [Java SDK](https://github.com/sendgrid/sendgrid-java/issues?utf8=%E2%9C%93&q=is%3Aopen+label%3A%22difficulty%3A+easy%22+label%3A%22status%3A+help+wanted%22) * [Go SDK](https://github.com/sendgrid/sendgrid-go/issues?utf8=%E2%9C%93&q=is%3Aopen+label%3A%22difficulty%3A+easy%22+label%3A%22status%3A+help+wanted%22) * [Python SMTPAPI Client](https://github.com/sendgrid/smtpapi-python/issues?utf8=%E2%9C%93&q=is%3Aopen+label%3A%22difficulty%3A+easy%22+label%3A%22status%3A+help+wanted%22) * [PHP SMTPAPI Client](https://github.com/sendgrid/smtpapi-php/issues?utf8=%E2%9C%93&q=is%3Aopen+label%3A%22difficulty%3A+easy%22+label%3A%22status%3A+help+wanted%22) * [C# SMTPAPI Client](https://github.com/sendgrid/smtpapi-csharp/issues?utf8=%E2%9C%93&q=is%3Aopen+label%3A%22difficulty%3A+easy%22+label%3A%22status%3A+help+wanted%22) * [Ruby SMTPAPI Client](https://github.com/sendgrid/smtpapi-ruby/issues?utf8=%E2%9C%93&q=is%3Aopen+label%3A%22difficulty%3A+easy%22+label%3A%22status%3A+help+wanted%22) * [Node.js SMTPAPI Client](https://github.com/sendgrid/smtpapi-nodejs/issues?utf8=%E2%9C%93&q=is%3Aopen+label%3A%22difficulty%3A+easy%22+label%3A%22status%3A+help+wanted%22) * [Java SMTPAPI Client](https://github.com/sendgrid/smtpapi-java/issues?utf8=%E2%9C%93&q=is%3Aopen+label%3A%22difficulty%3A+easy%22+label%3A%22status%3A+help+wanted%22) * [Go SMTPAPI Client](https://github.com/sendgrid/smtpapi-go/issues?utf8=%E2%9C%93&q=is%3Aopen+label%3A%22difficulty%3A+easy%22+label%3A%22status%3A+help+wanted%22) * [Python HTTP Client](https://github.com/sendgrid/python-http-client/issues?utf8=%E2%9C%93&q=is%3Aopen+label%3A%22difficulty%3A+easy%22+label%3A%22status%3A+help+wanted%22) * [PHP HTTP Client](https://github.com/sendgrid/php-http-client/issues?utf8=%E2%9C%93&q=is%3Aopen+label%3A%22difficulty%3A+easy%22+label%3A%22status%3A+help+wanted%22) * [C# HTTP Client](https://github.com/sendgrid/csharp-http-client/issues?utf8=%E2%9C%93&q=is%3Aopen+label%3A%22difficulty%3A+easy%22+label%3A%22status%3A+help+wanted%22) * [Java HTTP Client](https://github.com/sendgrid/java-http-client/issues?utf8=%E2%9C%93&q=is%3Aopen+label%3A%22difficulty%3A+easy%22+label%3A%22status%3A+help+wanted%22) * [Ruby HTTP Client](https://github.com/sendgrid/ruby-http-client/issues?utf8=%E2%9C%93&q=is%3Aopen+label%3A%22difficulty%3A+easy%22+label%3A%22status%3A+help+wanted%22) * [Go HTTP Client](https://github.com/sendgrid/rest/issues?utf8=%E2%9C%93&q=is%3Aopen+label%3A%22difficulty%3A+easy%22+label%3A%22status%3A+help+wanted%22) * [Open API Definition](https://github.com/sendgrid/sendgrid-oai/issues?utf8=%E2%9C%93&q=is%3Aopen+label%3A%22difficulty%3A+easy%22+label%3A%22status%3A+help+wanted%22) * [DX Automator](https://github.com/sendgrid/dx-automator/issues?utf8=%E2%9C%93&q=is%3Aopen+label%3A%22difficulty%3A+easy%22+label%3A%22status%3A+help+wanted%22) * [Documentation](https://github.com/sendgrid/docs/issues?utf8=%E2%9C%93&q=is%3Aopen+label%3A%22difficulty%3A+easy%22+label%3A%22status%3A+help+wanted%22) rest-2.6.9/ISSUE_TEMPLATE.md000066400000000000000000000013321421220614200152030ustar00rootroot00000000000000 ### Issue Summary A summary of the issue and the environment in which it occurs. If suitable, include the steps required to reproduce the bug. Please feel free to include screenshots, screencasts, or code examples. ### Steps to Reproduce 1. This is the first step 2. This is the second step 3. Further steps, etc. ### Code Snippet ```go # paste code here ``` ### Exception/Log ``` # paste exception/log here ``` ### Technical details: * rest version: * go version: rest-2.6.9/LICENSE000066400000000000000000000021111421220614200134770ustar00rootroot00000000000000MIT License Copyright (C) 2022, Twilio SendGrid, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. rest-2.6.9/Makefile000066400000000000000000000001411421220614200141330ustar00rootroot00000000000000.PHONY: test install install: go get -t -v ./... test: install go test -race -cover -v ./... rest-2.6.9/PULL_REQUEST_TEMPLATE.md000066400000000000000000000031421421220614200163000ustar00rootroot00000000000000 # Fixes # A short description of what this PR does. ### Checklist - [x] I acknowledge that all my contributions will be made under the project's license - [ ] I have made a material change to the repo (functionality, testing, spelling, grammar) - [ ] I have read the [Contribution Guidelines](https://github.com/sendgrid/rest/blob/main/CONTRIBUTING.md) and my PR follows them - [ ] I have titled the PR appropriately - [ ] I have updated my branch with the main branch - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] I have added the necessary documentation about the functionality in the appropriate .md file - [ ] I have added inline documentation to the code I modified If you have questions, please file a [support ticket](https://support.sendgrid.com), or create a GitHub Issue in this repository. rest-2.6.9/README.md000066400000000000000000000137001421220614200137570ustar00rootroot00000000000000![SendGrid Logo](twilio_sendgrid_logo.png) [![BuildStatus](https://github.com/sendgrid/rest/actions/workflows/test.yml/badge.svg)](https://github.com/sendgrid/rest/actions/workflows/test.yml) [![GoDoc](https://godoc.org/github.com/sendgrid/rest?status.png)](http://godoc.org/github.com/sendgrid/rest) [![Go Report Card](https://goreportcard.com/badge/github.com/sendgrid/rest)](https://goreportcard.com/report/github.com/sendgrid/rest) [![Twitter Follow](https://img.shields.io/twitter/follow/sendgrid.svg?style=social&label=Follow)](https://twitter.com/sendgrid) [![GitHub contributors](https://img.shields.io/github/contributors/sendgrid/rest.svg)](https://github.com/sendgrid/rest/graphs/contributors) [![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) **Quickly and easily access any RESTful or RESTful-like API.** If you are looking for the SendGrid API client library, please see [this repo](https://github.com/sendgrid/sendgrid-go). # Announcements **The default branch name for this repository has been changed to `main` as of 07/27/2020.** All updates to this library is documented in our [CHANGELOG](CHANGELOG.md). # Table of Contents - [Installation](#installation) - [Quick Start](#quick-start) - [Usage](#usage) - [How to Contribute](#contribute) - [About](#about) - [License](#license) # Installation ## Supported Versions This library supports the following Go implementations: * Go 1.14 * Go 1.15 * Go 1.16 * Go 1.17 ## Install Package ```bash go get github.com/sendgrid/rest ``` ## Setup Environment Variables ### Initial Setup ```bash cp .env_sample .env ``` ### Environment Variable Update the development environment with your [SENDGRID_API_KEY](https://app.sendgrid.com/settings/api_keys), for example: ```bash echo "export SENDGRID_API_KEY='YOUR_API_KEY'" > sendgrid.env echo "sendgrid.env" >> .gitignore source ./sendgrid.env ``` ## With Docker A Docker image has been created to allow you to get started with `rest` right away. ```bash docker-compose up -d --build # Ensure the container is running with 'docker ps' docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 40c8d984a620 rest_go "tail -f /dev/null" About a minute ago Up About a minute rest_go_1 ``` With the container running, you can execute your local `go` scripts using the following: ```bash # docker exec docker exec rest_go_1 go run docker/example.go 200 { "args": {}, "headers": { "Accept-Encoding": "gzip", "Connection": "close", "Host": "httpbin.org", "User-Agent": "Go-http-client/1.1" }, "origin": "86.180.177.202", "url": "https://httpbin.org/get" } map[Access-Control-Allow-Origin:[*] Access-Control-Allow-Credentials:[true] Via:[1.1 vegur] Connection:[keep-alive] Server:[gunicorn/19.9.0] Date:[Tue, 02 Oct 2018 18:20:43 GMT] Content-Type:[application/json] Content-Length:[233]] # You can install libraries too, using the same command # NOTE: Any libraries installed will be removed when the container is stopped. docker exec rest_go_1 go get github.com/uniplaces/carbon ``` Your go files will be executed relative to the root of this directory. So in the example above, to execute the `example.go` file within the `docker` directory, we run `docker exec rest_go_1 go run docker/example.go`. If this file was in the root of this repository (next to README.exe, rest.go etc.), you would run `docker exec rest_go_1 go run my_go_script.go` # Quick Start `GET /your/api/{param}/call` ```go package main import "github.com/sendgrid/rest" import "fmt" func main() { const host = "https://api.example.com" param := "myparam" endpoint := "/your/api/" + param + "/call" baseURL := host + endpoint method := rest.Get request := rest.Request{ Method: method, BaseURL: baseURL, } response, err := rest.Send(request) if err != nil { fmt.Println(err) } else { fmt.Println(response.StatusCode) fmt.Println(response.Body) fmt.Println(response.Headers) } } ``` `POST /your/api/{param}/call` with headers, query parameters and a request body. ```go package main import "github.com/sendgrid/rest" import "fmt" func main() { const host = "https://api.example.com" param := "myparam" endpoint := "/your/api/" + param + "/call" baseURL := host + endpoint Headers := make(map[string]string) key := os.Getenv("API_KEY") Headers["Authorization"] = "Bearer " + key Headers["X-Test"] = "Test" var Body = []byte(`{"some": 0, "awesome": 1, "data": 3}`) queryParams := make(map[string]string) queryParams["hello"] = "0" queryParams["world"] = "1" method := rest.Post request = rest.Request{ Method: method, BaseURL: baseURL, Headers: Headers, QueryParams: queryParams, Body: Body, } response, err := rest.Send(request) if err != nil { fmt.Println(err) } else { fmt.Println(response.StatusCode) fmt.Println(response.Body) fmt.Println(response.Headers) } } ``` # Usage - [Usage Examples](USAGE.md) # How to Contribute We encourage contribution to our projects, please see our [CONTRIBUTING](CONTRIBUTING.md) guide for details. Quick links: - [Feature Request](CONTRIBUTING.md#feature-request) - [Bug Reports](CONTRIBUTING.md#submit-a-bug-report) - [Improvements to the Codebase](CONTRIBUTING.md#improvements-to-the-codebase) - [Code Reviews](CONTRIBUTING.md#code-reviews) # About rest is maintained and funded by Twilio SendGrid, Inc. The names and logos for rest are trademarks of Twilio SendGrid, Inc. If you need help installing or using the library, please check the [Twilio SendGrid Support Help Center](https://support.sendgrid.com). 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! # License [The MIT License (MIT)](LICENSE) rest-2.6.9/TROUBLESHOOTING.md000066400000000000000000000027531421220614200153170ustar00rootroot00000000000000## Table of Contents * [Viewing the Request Body](#request-body) ## Viewing the Request Body 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). Example Code ```go package main import "github.com/sendgrid/rest" import "fmt" func main() { const host = "https://api.example.com" param := "myparam" endpoint := "/your/api/" + param + "/call" baseURL := host + endpoint Headers := make(map[string]string) key := os.Getenv("API_KEY") Headers["Authorization"] = "Bearer " + key Headers["X-Test"] = "Test" var Body = []byte(`{"some": 0, "awesome": 1, "data": 3}`) queryParams := make(map[string]string) queryParams["hello"] = "0" queryParams["world"] = "1" method := rest.Post request = rest.Request{ Method: method, BaseURL: baseURL, Headers: Headers, QueryParams: queryParams, Body: Body, } response, err := rest.API(request) if err != nil { fmt.Println(err) } else { fmt.Println(response.StatusCode) fmt.Println(response.Body) fmt.Println(response.Headers) } } ``` You can do this right before you call `response, err := rest.API(request)` like so: ```go fmt.Printf("Request Body: %v \n", string(request.Body)) req, e := BuildRequestObject(request) requestDump, err := httputil.DumpRequest(req, true) if err != nil { t.Errorf("Error : %v", err) } fmt.Printf("Request : %v \n", string(requestDump)) ```rest-2.6.9/USAGE.md000066400000000000000000000070421421220614200136700ustar00rootroot00000000000000# Usage Usage examples for SendGrid REST library ## Initialization ```go package main import ( "encoding/json" "fmt" "os" "github.com/sendgrid/rest" ) // Build the URL const host = "https://api.sendgrid.com" endpoint := "/v3/api_keys" baseURL := host + endpoint // Build the request headers key := os.Getenv("SENDGRID_API_KEY") Headers := make(map[string]string) Headers["Authorization"] = "Bearer " + key ``` ## Table of Contents - [GET](#get) - [DELETE](#delete) - [POST](#post) - [PUT](#put) - [PATCH](#patch) ## GET #### GET Single ```go method = rest.Get // Make the API call request = rest.Request{ Method: method, BaseURL: baseURL + "/" + apiKey, Headers: Headers, } response, err = rest.API(request) if err != nil { fmt.Println(err) } else { fmt.Println(response.StatusCode) fmt.Println(response.Body) fmt.Println(response.Headers) } ``` #### GET Collection ```go method := rest.Get // Build the query parameters queryParams := make(map[string]string) queryParams["limit"] = "100" queryParams["offset"] = "0" // Make the API call request := rest.Request{ Method: method, BaseURL: baseURL, Headers: Headers, QueryParams: queryParams, } response, err := rest.API(request) if err != nil { fmt.Println(err) } else { fmt.Println(response.StatusCode) fmt.Println(response.Body) fmt.Println(response.Headers) } ``` ## DELETE ```go method = rest.Delete // Make the API call request = rest.Request{ Method: method, BaseURL: baseURL + "/" + apiKey, Headers: Headers, QueryParams: queryParams, } response, err = rest.API(request) if err != nil { fmt.Println(err) } else { fmt.Println(response.StatusCode) fmt.Println(response.Headers) } ``` ## POST ```go method = rest.Post // Build the request body var Body = []byte(`{ "name": "My API Key", "scopes": [ "mail.send", "alerts.create", "alerts.read" ] }`) // Make the API call request = rest.Request{ Method: method, BaseURL: baseURL, Headers: Headers, QueryParams: queryParams, Body: Body, } response, err = rest.API(request) if err != nil { fmt.Println(err) } else { fmt.Println(response.StatusCode) fmt.Println(response.Body) fmt.Println(response.Headers) } // Get a particular return value. // Note that you can unmarshall into a struct if // you know the JSON structure in advance. b := []byte(response.Body) var f interface{} err = json.Unmarshal(b, &f) if err != nil { fmt.Println(err) } m := f.(map[string]interface{}) apiKey := m["api_key_id"].(string) ``` ## PUT ```go method = rest.Put // Build the request body Body = []byte(`{ "name": "A New Hope", "scopes": [ "user.profile.read", "user.profile.update" ] }`) // Make the API call request = rest.Request{ Method: method, BaseURL: baseURL + "/" + apiKey, Headers: Headers, Body: Body, } response, err = rest.API(request) if err != nil { fmt.Println(err) } else { fmt.Println(response.StatusCode) fmt.Println(response.Body) fmt.Println(response.Headers) } ``` ## PATCH ```go method = rest.Patch // Build the request body Body = []byte(`{ "name": "A New Hope" }`) // Make the API call request = rest.Request{ Method: method, BaseURL: baseURL + "/" + apiKey, Headers: Headers, Body: Body, } response, err = rest.API(request) if err != nil { fmt.Println(err) } else { fmt.Println(response.StatusCode) fmt.Println(response.Body) fmt.Println(response.Headers) } ```rest-2.6.9/docker-compose.yml000066400000000000000000000001161421220614200161320ustar00rootroot00000000000000version: '3' services: go: build: ./docker/ volumes: - ./:/app rest-2.6.9/docker/000077500000000000000000000000001421220614200137465ustar00rootroot00000000000000rest-2.6.9/docker/Dockerfile000066400000000000000000000004241421220614200157400ustar00rootroot00000000000000FROM golang:1.11 VOLUME ["/app"] WORKDIR /app # Get sendgrid-go RUN mkdir -p /go/src/github.com/sendgrid && \ cd /go/src/github.com/sendgrid && \ git clone https://www.github.com/sendgrid/rest && \ cd rest && \ go get ENTRYPOINT ["tail", "-f", "/dev/null"] rest-2.6.9/docker/example.go000066400000000000000000000010121421220614200157220ustar00rootroot00000000000000package main import "github.com/sendgrid/rest" import "fmt" func main() { const host = "https://httpbin.org" param := "get" endpoint := "/" + param baseURL := host + endpoint method := rest.Get request := rest.Request{ Method: method, BaseURL: baseURL, } response, err := rest.Send(request) if err != nil { fmt.Println(err) } else { fmt.Println(response.StatusCode) fmt.Println(response.Body) fmt.Println(response.Headers) } } rest-2.6.9/examples/000077500000000000000000000000001421220614200143155ustar00rootroot00000000000000rest-2.6.9/examples/example.go000066400000000000000000000062671421220614200163120ustar00rootroot00000000000000package main import ( "encoding/json" "fmt" "os" "github.com/sendgrid/rest" ) func main() { // Build the URL const host = "https://api.sendgrid.com" endpoint := "/v3/api_keys" baseURL := host + endpoint // Build the request headers key := os.Getenv("SENDGRID_API_KEY") Headers := make(map[string]string) Headers["Authorization"] = "Bearer " + key // GET Collection method := rest.Get // Build the query parameters queryParams := make(map[string]string) queryParams["limit"] = "100" queryParams["offset"] = "0" // Make the API call request := rest.Request{ Method: method, BaseURL: baseURL, Headers: Headers, QueryParams: queryParams, } response, err := rest.Send(request) if err != nil { fmt.Println(err) } else { fmt.Println(response.StatusCode) fmt.Println(response.Body) fmt.Println(response.Headers) } // POST method = rest.Post var Body = []byte(` { "name": "My API Key", "scopes": [ "mail.send", "alerts.create", "alerts.read" ] }`) request = rest.Request{ Method: method, BaseURL: baseURL, Headers: Headers, QueryParams: queryParams, Body: Body, } response, err = rest.Send(request) if err != nil { fmt.Println(err) } else { fmt.Println(response.StatusCode) fmt.Println(response.Body) fmt.Println(response.Headers) } // Get a particular return value. // Note that you can unmarshall into a struct if // you know the JSON structure in advance. b := []byte(response.Body) var f interface{} err = json.Unmarshal(b, &f) if err != nil { fmt.Println(err) } m := f.(map[string]interface{}) apiKey := m["api_key_id"].(string) // GET Single method = rest.Get // Make the API call request = rest.Request{ Method: method, BaseURL: baseURL + "/" + apiKey, Headers: Headers, } response, err = rest.Send(request) if err != nil { fmt.Println(err) } else { fmt.Println(response.StatusCode) fmt.Println(response.Body) fmt.Println(response.Headers) } // PATCH method = rest.Patch Body = []byte(`{ "name": "A New Hope" }`) request = rest.Request{ Method: method, BaseURL: baseURL + "/" + apiKey, Headers: Headers, Body: Body, } response, err = rest.Send(request) if err != nil { fmt.Println(err) } else { fmt.Println(response.StatusCode) fmt.Println(response.Body) fmt.Println(response.Headers) } // PUT method = rest.Put Body = []byte(`{ "name": "A New Hope", "scopes": [ "user.profile.read", "user.profile.update" ] }`) request = rest.Request{ Method: method, BaseURL: baseURL + "/" + apiKey, Headers: Headers, Body: Body, } response, err = rest.Send(request) if err != nil { fmt.Println(err) } else { fmt.Println(response.StatusCode) fmt.Println(response.Body) fmt.Println(response.Headers) } // DELETE method = rest.Delete request = rest.Request{ Method: method, BaseURL: baseURL + "/" + apiKey, Headers: Headers, QueryParams: queryParams, } response, err = rest.Send(request) if err != nil { fmt.Println(err) } else { fmt.Println(response.StatusCode) fmt.Println(response.Headers) } } rest-2.6.9/rest.go000066400000000000000000000105341421220614200140060ustar00rootroot00000000000000// Package rest allows for quick and easy access any REST or REST-like API. package rest import ( "bytes" "context" "io/ioutil" "net/http" "net/url" ) // Version represents the current version of the rest library const Version = "2.6.9" // Method contains the supported HTTP verbs. type Method string // Supported HTTP verbs. const ( Get Method = "GET" Post Method = "POST" Put Method = "PUT" Patch Method = "PATCH" Delete Method = "DELETE" ) // Request holds the request to an API Call. type Request struct { Method Method BaseURL string // e.g. https://api.sendgrid.com Headers map[string]string QueryParams map[string]string Body []byte } // RestError is a struct for an error handling. type RestError struct { Response *Response } // Error is the implementation of the error interface. func (e *RestError) Error() string { return e.Response.Body } // DefaultClient is used if no custom HTTP client is defined var DefaultClient = &Client{HTTPClient: &http.Client{}} // Client allows modification of client headers, redirect policy // and other settings // See https://golang.org/pkg/net/http type Client struct { HTTPClient *http.Client } // Response holds the response from an API call. type Response struct { StatusCode int // e.g. 200 Body string // e.g. {"result: success"} Headers map[string][]string // e.g. map[X-Ratelimit-Limit:[600]] } // AddQueryParameters adds query parameters to the URL. func AddQueryParameters(baseURL string, queryParams map[string]string) string { baseURL += "?" params := url.Values{} for key, value := range queryParams { params.Add(key, value) } return baseURL + params.Encode() } // BuildRequestObject creates the HTTP request object. func BuildRequestObject(request Request) (*http.Request, error) { // Add any query parameters to the URL. if len(request.QueryParams) != 0 { request.BaseURL = AddQueryParameters(request.BaseURL, request.QueryParams) } req, err := http.NewRequest(string(request.Method), request.BaseURL, bytes.NewBuffer(request.Body)) if err != nil { return req, err } for key, value := range request.Headers { req.Header.Set(key, value) } _, exists := req.Header["Content-Type"] if len(request.Body) > 0 && !exists { req.Header.Set("Content-Type", "application/json") } return req, err } // MakeRequest makes the API call. func MakeRequest(req *http.Request) (*http.Response, error) { return DefaultClient.HTTPClient.Do(req) } // BuildResponse builds the response struct. func BuildResponse(res *http.Response) (*Response, error) { body, err := ioutil.ReadAll(res.Body) response := Response{ StatusCode: res.StatusCode, Body: string(body), Headers: res.Header, } res.Body.Close() // nolint return &response, err } // Deprecated: API supports old implementation func API(request Request) (*Response, error) { return Send(request) } // Send uses the DefaultClient to send your request func Send(request Request) (*Response, error) { return SendWithContext(context.Background(), request) } // SendWithContext uses the DefaultClient to send your request with the provided context. func SendWithContext(ctx context.Context, request Request) (*Response, error) { return DefaultClient.SendWithContext(ctx, request) } // The following functions enable the ability to define a // custom HTTP Client // MakeRequest makes the API call. func (c *Client) MakeRequest(req *http.Request) (*http.Response, error) { return c.HTTPClient.Do(req) } // Deprecated: API supports old implementation func (c *Client) API(request Request) (*Response, error) { return c.Send(request) } // Send will build your request, make the request, and build your response. func (c *Client) Send(request Request) (*Response, error) { return c.SendWithContext(context.Background(), request) } // SendWithContext will build your request passing in the provided context, make the request, and build your response. func (c *Client) SendWithContext(ctx context.Context, request Request) (*Response, error) { // Build the HTTP request object. req, err := BuildRequestObject(request) if err != nil { return nil, err } // Pass in the user provided context req = req.WithContext(ctx) // Build the HTTP client and make the request. res, err := c.MakeRequest(req) if err != nil { return nil, err } // Build Response object. return BuildResponse(res) } rest-2.6.9/rest_test.go000066400000000000000000000211641421220614200150460ustar00rootroot00000000000000package rest import ( "errors" "fmt" "io/ioutil" "net/http" "net/http/httptest" "net/http/httputil" "os" "regexp" "strings" "testing" "time" "golang.org/x/net/context" ) func TestBuildURL(t *testing.T) { t.Parallel() host := "http://api.test.com" queryParams := make(map[string]string) queryParams["test"] = "1" queryParams["test2"] = "2" testURL := AddQueryParameters(host, queryParams) if testURL != "http://api.test.com?test=1&test2=2" { t.Error("Bad BuildURL result") } } func TestBuildRequest(t *testing.T) { t.Parallel() method := Get baseURL := "http://api.test.com" key := "API_KEY" Headers := make(map[string]string) Headers["Content-Type"] = "application/json" Headers["Authorization"] = "Bearer " + key queryParams := make(map[string]string) queryParams["test"] = "1" queryParams["test2"] = "2" request := Request{ Method: method, BaseURL: baseURL, Headers: Headers, QueryParams: queryParams, } req, e := BuildRequestObject(request) if e != nil { t.Errorf("Rest failed to BuildRequest. Returned error: %v", e) } if req == nil { t.Errorf("Failed to BuildRequest.") } //Start PrintRequest requestDump, err := httputil.DumpRequest(req, true) if err != nil { t.Errorf("Error : %v", err) } fmt.Println("Request : ", string(requestDump)) //End Print Request } func TestBuildBadRequest(t *testing.T) { t.Parallel() request := Request{ Method: Method("@"), } req, e := BuildRequestObject(request) if e == nil { t.Errorf("Expected an error for a bad HTTP Method") } if req != nil { t.Errorf("If there's an error there shouldn't be a Request.") } } func TestBuildBadAPI(t *testing.T) { t.Parallel() request := Request{ Method: Method("@"), } res, e := API(request) if e == nil { t.Errorf("Expected an error for a bad HTTP Method") } if res != nil { t.Errorf("If there's an error there shouldn't be a Response.") } } func TestBuildResponse(t *testing.T) { t.Parallel() fakeServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "{\"message\": \"success\"}") })) defer fakeServer.Close() baseURL := fakeServer.URL method := Get request := Request{ Method: method, BaseURL: baseURL, } req, e := BuildRequestObject(request) if e != nil { t.Error("Failed to BuildRequestObject", e) } res, e := MakeRequest(req) if e != nil { t.Error("Failed to MakeRequest", e) } response, e := BuildResponse(res) if response.StatusCode != 200 { t.Error("Invalid status code in BuildResponse") } if len(response.Body) == 0 { t.Error("Invalid response body in BuildResponse") } if len(response.Headers) == 0 { t.Error("Invalid response headers in BuildResponse") } if e != nil { t.Errorf("Rest failed to make a valid API request. Returned error: %v", e) } //Start Print Request requestDump, err := httputil.DumpRequest(req, true) if err != nil { t.Errorf("Error : %v", err) } fmt.Println("Request :", string(requestDump)) //End Print Request } type panicResponse struct{} func (*panicResponse) Read([]byte) (n int, err error) { return 0, errors.New("test error") } func (*panicResponse) Close() error { return nil } func TestBuildBadResponse(t *testing.T) { t.Parallel() res := &http.Response{ Body: new(panicResponse), } _, e := BuildResponse(res) if e == nil { t.Errorf("This was a bad response and error should be returned") } } func TestRest(t *testing.T) { t.Parallel() testingAPI(t, Send) testingAPI(t, API) } func testingAPI(t *testing.T, fn func(request Request) (*Response, error)) { fakeServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "{\"message\": \"success\"}") })) defer fakeServer.Close() host := fakeServer.URL endpoint := "/test_endpoint" baseURL := host + endpoint key := "API_KEY" Headers := make(map[string]string) Headers["Content-Type"] = "application/json" Headers["Authorization"] = "Bearer " + key method := Get queryParams := make(map[string]string) queryParams["test"] = "1" queryParams["test2"] = "2" request := Request{ Method: method, BaseURL: baseURL, Headers: Headers, QueryParams: queryParams, } //Start Print Request req, e := BuildRequestObject(request) if e != nil { t.Errorf("Error during BuildRequestObject: %v", e) } requestDump, err := httputil.DumpRequest(req, true) if err != nil { t.Errorf("Error : %v", err) } fmt.Println("Request :", string(requestDump)) //End Print Request response, e := fn(request) if response.StatusCode != 200 { t.Error("Invalid status code") } if len(response.Body) == 0 { t.Error("Invalid response body") } if len(response.Headers) == 0 { t.Error("Invalid response headers") } if e != nil { t.Errorf("Rest failed to make a valid API request. Returned error: %v", e) } } func TestDefaultContentTypeWithBody(t *testing.T) { t.Parallel() host := "http://localhost" method := Get request := Request{ Method: method, BaseURL: host, Body: []byte("Hello World"), } response, _ := BuildRequestObject(request) if response.Header.Get("Content-Type") != "application/json" { t.Error("Content-Type not set to the correct default value when a body is set.") } //Start Print Request fmt.Println("Request Body: ", string(request.Body)) requestDump, err := httputil.DumpRequest(response, true) if err != nil { t.Errorf("Error : %v", err) } fmt.Println("Request :", string(requestDump)) //End Print Request } func TestCustomContentType(t *testing.T) { t.Parallel() host := "http://localhost" Headers := make(map[string]string) Headers["Content-Type"] = "custom" method := Get request := Request{ Method: method, BaseURL: host, Headers: Headers, Body: []byte("Hello World"), } response, _ := BuildRequestObject(request) if response.Header.Get("Content-Type") != "custom" { t.Error("Content-Type not modified correctly") } //Start Print Request requestDump, err := httputil.DumpRequest(response, true) if err != nil { t.Errorf("Error : %v", err) } fmt.Println("Request :", string(requestDump)) //End Print Request } func TestCustomHTTPClient(t *testing.T) { t.Parallel() fakeServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { time.Sleep(time.Millisecond * 20) fmt.Fprintln(w, "{\"message\": \"success\"}") })) defer fakeServer.Close() host := fakeServer.URL endpoint := "/test_endpoint" baseURL := host + endpoint method := Get request := Request{ Method: method, BaseURL: baseURL, } customClient := &Client{&http.Client{Timeout: time.Millisecond * 10}} _, err := customClient.Send(request) if err == nil { t.Error("A timeout did not trigger as expected") } if !strings.Contains(err.Error(), "Client.Timeout exceeded while awaiting headers") { t.Error("We did not receive the Timeout error") } } func TestRestError(t *testing.T) { t.Parallel() headers := make(map[string][]string) headers["Content-Type"] = []string{"application/json"} response := &Response{ StatusCode: 400, Body: `{"result": "failure"}`, Headers: headers, } var err error = &RestError{Response: response} if err.Error() != `{"result": "failure"}` { t.Error("Invalid error message.") } } func TestRepoFiles(t *testing.T) { files := []string{".env_sample", ".gitignore", ".github/workflows/test.yml", "CHANGELOG.md", "CODE_OF_CONDUCT.md", "CONTRIBUTING.md", "ISSUE_TEMPLATE.md", "LICENSE", "PULL_REQUEST_TEMPLATE.md", "README.md", "TROUBLESHOOTING.md", "USAGE.md"} for _, file := range files { if _, err := os.Stat(file); os.IsNotExist(err) { t.Errorf("Repo file does not exist: %v", file) } } } func TestLicenseYear(t *testing.T) { t.Parallel() dat, err := ioutil.ReadFile("LICENSE") currentYear := time.Now().Year() r := fmt.Sprintf("%d", currentYear) match, _ := regexp.MatchString(r, string(dat)) if err != nil { t.Error("License File Not Found") } if !match { t.Error("Incorrect Year in License Copyright") } } func TestSendWithContext(t *testing.T) { t.Parallel() fakeServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { time.Sleep(time.Millisecond * 20) fmt.Fprintln(w, "{\"message\": \"success\"}") })) defer fakeServer.Close() host := fakeServer.URL endpoint := "/test_endpoint" baseURL := host + endpoint method := Get request := Request{ Method: method, BaseURL: baseURL, } ctx, _ := context.WithTimeout(context.Background(), time.Millisecond*10) _, err := SendWithContext(ctx, request) if err == nil { t.Error("A timeout did not trigger as expected") } if !strings.Contains(err.Error(), "context deadline exceeded") { t.Error("We did not receive the Timeout error") } } rest-2.6.9/static/000077500000000000000000000000001421220614200137665ustar00rootroot00000000000000rest-2.6.9/static/img/000077500000000000000000000000001421220614200145425ustar00rootroot00000000000000rest-2.6.9/static/img/github-fork.png000066400000000000000000000531521421220614200174770ustar00rootroot00000000000000PNG  IHDRVkEsBITOtEXtSoftwareShutterc IDATxwXSߛf1&R* :ꨭmV۟:]WV[ku֪UʔYJ`l<<ћss=sC()   R` AAAAA@AAB   9sv tXz   ">yiB                -  4w:A/D=(3  MvKg  HbJ]Wd1} @d2뤩ĀZJҤ{h=묇  \N{wVR{vjq!ڢ^u R)**lԈul//:ij1@Ym A4j qPFpȭ\FS7M4tp@Abo7Do&8qg/|aa`  ?EW @]sF®Gk(70˗CgyOi Ê3t?+ =o&(|  RL}1-gS˹;9bЁ{q37J\םhh];/3Apt-w# T*ˏ?HPT1p;os8ghj-_Eķ5 "e`{wq4MٷȄk@9uo1qBR}Jo9AF}ԇIΛQ~$1^bo޴Upuoݾ !h@Y*..xT*hF="Ѝwy&NR_<{#ޔ˟N&{;gc)g 9<^Z)sOy9ǔ˟.OT|d)(RO,&m#) )/n?R3/6|p# @kiy"%,Z*@0RAowXdlؓ>T2qsŤ<ѝgNWJ&n h\RwM<Q-sY ^ ug5(ɱ3Wzo4¯j`㗼3ij0wȜX,VPl_|e:ne%gS^YW.}xEZZZZZzZz'Obniuf7oͭ,gܟ£S;ٿ߸'dkTW_Ǚq7J _5uEѐ5;{|$ffa?Lnf zcEKk#ݻw2m ߯}Lx?Ž}$t|ʎߦ偗W-;cM }k3`ɚM}y3A6.aϓ4/XF d[ z%;2Ѕ^ bz.۾W~!M3 #32ƈ|a@d=ҫ|qF7ovچ.BI yw?s_ Ņk™GEGx19Bxq[\1vcP%sgm*\|άxI ԒFaʃ;sʈ<ixp21d~~?tf ̕O_iltF._c֭AL:WWg~6c`…?4i:raţlvpOR݇0[ڽ$%v qsn=/[) 5qaW`e //O:Śo+.?PSS2qn&n_z dYAw@ri .@VZJ45vXVcn'kONN|$A=?u1vD+~]z{JoHfG T2TDz/˓] i=?;֞uEHy cyǎSj~%ߛd.N* z6$&!^iɐSkw@|hugzB 4P~~akr,\y%Th^ؙ6 % [2b>fޤ;Bh D on$LJqxeA+XCQ*v_[˵ХsdZju8!@v*IʠÃ܊%BQңld_DZ=fflNי npsV)`ˎA'~3~dR'tfٵڷc$핈` b[ۿ/5v5 .vꗞBٟ:rFbo!~~[M5 Eb&&]LH;F1b! [7^|oSAfd4%;8#oiԨ71&obըQ~/rH6-DzUn}v̍ %n掴)tu~S<+e=leڵ8$%D! ږ=B+ ?mܢG3<T<D%/CxQi$4L/9̲Y@< <4~/r;nUFy-CsBΓ ;O)" 8r}:~bt/D A1k&*|Y4])A@Ӵ֧ (?]H* إ z;Qdt >G;EnE= B6k@ _=Wr hGB9B틝e]8يW6&-4q&pt1` \ֺINFoM 6clc.}릎Noyd˶ϟxa/ w` C\A1z7D";neӴ8>zl7ژN>bȞ)`>=.g*k،>Č0雘]ȳѾ=1>z~h{s_<AS337\5!96ٍ̦MǴ'zmnG6j[ӊgO̠o=1>>3Ӟ+8i^7pu!]\d(n9ޑtIH"d};cIU_uOܹlˌcުqqΥu;q!}CH }9/wȀ͏ydxP|/C#@V#?sEu8˓*]#ts((AA+I!vg1G'xۙSg|/a^`Z7=_>y=ׁ4P'o޿w[ر`Ϙt>ؤsآ7vC DّcEpkO{]JV1o5[^:^7 *r|7:qO?&zYM(.^5Ǫ[^:Ҕ~Skո X  .x5֣lݥ7yJݦ)wK$~G 6֒IX3t`;JElNZfHS EE]Ԫ3Wɪ_p AZ}ad؞܃h[򏟲W&Psmށn#+%= ~̉~طoߍyK$Edm [bDRbVX޷oߊr ށBh:ڿ?M)ʢ_ϜLb2' HqtixɭǃU/p?4>6  zߐv>8DoHKn5 ?|қ7V_X?5ykl>cW3ķW6,si٬ukm~|B-|RKȹgڼ;NHY7-lgn+ķ/ 0Su3M-3x<#=8xi2ǫ,ܭ뼘UxU BvÖNݕ a@/}tSztfGI>s~Í]39q|%lQVM 8>o^Dr;"VZclٶcֵ랇Պ/[rh+Ҿ ݑc}VB#KWSnʵ'W/t-TϞ0 = /4ߑ'<ԋӿhEARٱS"x rELj~07xE1 xlΑgr+"\-\>r$\SٯO^14ՆY1Sjcednۙ? "ӛÑ;%~[_3,ڑ~Mzxoq pxmJ9/+n܅NEgA۶5wq~ӧddu^G3jiZ*T*WWdFҰ٤RIQu_O$ƍ*'_VsOAFhJGUc~p_5<˟s_eQ##jo͟򱳵gϭzɊ-v2oQ!]A΀T* IDATΚ}=w~?|>>W8;kgEC,) Y?k?U;&(hѼ -[AH($I Ʌ{ϐIף n~T*rkwYryhH;JxkcnWvհi#Uw}[WL'U;=X|p{ͷTv ~nfi d8oV-jfSj֮̾lp*Ҟ'Β‡7Wcc:;@ҕ×3 RJ5yʴz5jvJ/W<}a77l޼Y^Of.wؔgRu9atg.XO=)JvF{@gHA@QQQީ]G|+^æh4ROm?@6\;xh̩_iQL&#IDx{ @PP\@?/z@ 0aY'o@xˢc×Dxqqì+5;Ŭ+G$Y/OaA|gỏ2һ~w>"z( }W-85ouڠ5{ r7%x>݂@e^{{b[v5*YUpk-hXO(CVW(>(){Q{:h_;w9=.3YKz^9 Qs>t:6n>zZẇ7D.}bHZrq|T2x={oQdvQ%*kW"gHd4%vv~qWL.QpS.Y3L3}ɻ )oN94uKl!3?X  iz蛱q^noiw57sNvNs/{$,jrXQ yP v?O[0c]<O,[ǧco8krPu~t[^4LJo|qW젪%.|:(ff=wfywh9YIvvMØfP%z׶jdN4O ~}:'ytQV" bȑKM)oX\{(Ɗ6}3_ L-o߰iOH/{ }W=k쾬ؽ' |^>7]ГeT)TAPx-tlBPW #Iu/IVjMCѰ[I-{o0ڐsyK][Z6'\{Mkmw=QU4`_A7SȻs/٬m ߀w,rdp@54a_i[ݳ'Y6C=8o^9Lys? HRSS.|TnCT $G| EACH^} :l@3>nȀg72dR Hw-jdҾBݟz4 Y2MES /<$[7H90TB0,f ١o/ hNߖd [Iο(i]'='m0!4Y1TJ:i3&-;_XۊO'q~kվq-l'!%60`ШRcJ&&JM[; ^Dx}G}սwSQɀ9b ^=Y ~Wk d}I7,IyH4 Iz6(Xz_@EO *{F%A^l9 &SϵL!QYv_!ge+Y zl8ǯUc$)YE7>i^ Yb;b0j Kú_մZܵ{d! T9Vλ|d;îY:' $(sPMUJ7?x^ ѿBZU[ui?)?itg>@ O$3Y!4l-,5&[Pzf|%Bٓ-L9H\ϡų7 n){Da8T@ o>|ӡtqr凧K&kaıJL{ʌ){/ 5[l²qa!@Jo8*($@ kh0<<(F RR(^ Mȧ'k(=.UdS R[93srrΝ0q?|wPJHKn#;p@93u~$j&y %EE]!)w&آ{XXrb#Uӥ[ߓ{ wnq=?Y1ރFʷUԲ=+=}o딙ϝ_jI7;dq::2\[Iivd<ϱnEas.X|Bb;I wNytًo̟T06`2G"-!ȷQ>}2oC+)ɪ)mKze4x:EgCB`~VbH%AV}A׭;qyU:GFl\+'vḷw>6eYY OW┲@EvvY$-uA$xy"aEO$*)\(60Mϲ JO]'֑X(%Eqgi^N5%y-8JZBFOXdTӧׯ=5]1 {og.T@Ț.Ur :H"/4/|D?7K B_uMnPWv ;9Om8*GTA^!IXQ‚ I䙽LD Vg@۹ܹck֭rrrg͉=w~NNn֭vZ ӗSkNl8ju+D}(}$K[ywVtc%ȿm6MkN%+$g0XI&j'(oo\ $'QwQ!<}L~zY|}?&II,^s8ˣWi^r/r;P+|xǹnz+X [TKvחrrsY,KAA7bqxNn7ϒS +|M[7^.?^,/B'w}J4)<{f6 ͻwC RS;$ٹRT0XSGw~ⷃۏܔwjs"#igO&SԳ]d]Pk9B*Izt|kSÐ~`ui uڦ Ҭusxrbf P/1 (J)&%A^u\۽s'tC~6;zW[Ɉm8xu~ܚb|MO|utC2Rغ }^8 {^9yػ- † Oiҧ}}v 9}t}3oA;A(-n'nU̔%aAxDH %bqJn*Q#O_\Q1V7}nԱRI'GtdSsa>kfxrzϑ3~Ӗg Hkhէ@jDfAo'mA)F<=<O%EEyWM;rT*ɘl61;KWI)9^q3دW H! Hk]ʅ+'$w~ӳ z klvk]]]]rL&\)Ax?X|!ZAq~P@d2y<C HǫAAAAA@AAAAA@AAAAA@AAAAA@AAa-ͥIbAA!hF+   H= !  :  3   :  3   :  3   :  3   :  3   :  3   :  3   :  3   :  3   Ű,ֈ e2|_R) l6IM~߅ iH50xz @I h6)4IfS)5ʙ">A W+p_ܴm#s’ [d}نʧSV!5ia7cIRA,qZ Vޑϗa_͕d܀YsX_gʲΊ,Ur2}dl.IՔJ[{۲`<(JZ]1[)*f0|.O$2[ ʔj໺1v)R*4M96ۄ?'LfkT-:jm PNokOf}mlKSYR{+濹Ux灨d2GdI*ch‘ hh +|ȎcVhd2JyrJ6M?jZ.J% \<--UP.|OHds8A( RiRf'm-hZ7 nJK4%*'sҲmN7/SZ 75hy6}I`"չ6V<[|S30FJRjƅKߚkM~V|oK}3UέvJ맑:f+-].vV5=mI[AL+ד{7\ӠԪGmJ]n2sYN[avK+[p+WVj+k^حJpy\E]RR֨b̪UZ&yL&UPJ{wK,s5EP,/"u[L)a(uSt eci/SГ9iYN0?)TMԠ=,e@'ε ݰT_ ˟1~f*`28JVaYXT*Rz*XZVC1CDŶ@o+aYɸUuMoV`m1RF5f`2f^Vo*д>`V2`Vj-h%x<"@Z"h4D*kV[jf˶BDŽ; L&SCkAe䘟},e2Qc~f#cEe\}lIV((e:}`#eJ ((F \0_K8BN!R+0G>`eF[Jm=g2P}Q8B La[vz}pe@TDE4oYmUD*}"B8Dp C$( Jbe)1>B>SNJqtL!;*V2?D9ٖ+3>vk;s ӖFWHKnK+[\9P12`טB9$RXYy1UZ6+;˕tP;2HܙZZ'5Оr$5ge9hhQVֆerZURRw3L [*b Ag'd?^qְiv&UAAT?2࠘BvZ9< 2?2Pijp'5㒖<}9.n4gL!PSHW3`eУ f,7 { 6cݚBF֥XܽTRh cϚ-U1LbJAq=\WhP<O&jiT'Bh˵J(.*)u<4HUShV5hd=82`>@WҾX TOP锁@W, +6^ఘBJ93Ɯ7+۶`Z8m3=9q2+PeWX Hpe 8|@]S,eV֯2Q \l}*>@צ|{wHpo/a}̈)Sc aL!)daL! k D1hBNSvBPS1BeʀEE@BNS)8>L;J'ߍa nfw;kgDh4[ n6\_D1 @kh *e7b1ܻٙreԞ/KZJvʀw#FgA2N hw#v݈PnĀm%}VE4KU*Ye,k% Q̍)T\9`2P$iTVl 6]_TCgAPp2`}BV)6ލ>`1?F\sa2P;L-)hԦڊZeiSο ,2d[ \ŢiL&WT)ξ1:H),&`?^V r+h{mV/)p9VI07aeL!KWL&A0hj(`oen%e2@[J}7bb7bm,42V]w#v}a`tMؓtj.ͮ߈1aL!݈kn2rbZPezZ@MѲՂ݈-ݩn1v7bфh b\.(J)y<.ɪ;]I @:rþlLQ @݉)D|L!ªBB1Gm SFxc 14JA Ì]>0 8(P%eE\.TIL!ma{m:wUFE^v[0O EXkKSUSIDAT+T@v#v2|}WԦ݈_2 &KVQ%ŤCg(V(&ZZ 8nĴsF\=eR}128j4UJRX*܅tmN1h+M0P)SYc 1<BCQԥB)Mkᘷw)D>1WGpH1}Be) V)V뻹4oH0 d Ϫ)ίek2+hgY9PCQ5s2Pwv#&r1LMjJEQ7v#V)jJIvqes݈+N X8Lp@>mPc*h42 ]RQjh ,,yeo Z+1tL!{B1S .4(%Eu`7bTd@2LsjK1*Pm֘B 8#()At$M0je86]"RrZZi4201ǯڲT*Bnz}s9 \L(W3%V_2LTD&W|2@WB*2@G7ϰSL!I7,4]y܌BnĴu 6-Ih<^%D1L@Y\TjRlUVK1mfZŪ@*R gdu;S͊%3M)^Vs+4d00 `Iliʎ'`0l4GP@ӥcUehMyaд#e)AtY $4Vkhhc)MP;| 9ke/`0 )0R*C\B ɴ{4lL(( dҨg BP+c ʁzSw#Z9PfI JZE0@0l X1 hhZє8I.O;`l]1yjV(k^ I5nBRBIQJ`p&#@ 6U?tNX/;\,Wت6?ҝ>+ {Unʀ=2@)Ta"e+2(`juZd3X$V P4jFʧ%4  Ldr8 Ci; 6̪Q_d`hˡVQ\ &hlfÔ ia$AFxhqN vVhJ̰ ` [͉kd6}#Sc aL!{+,S^zd]/j(ZcUS1,;` d A0L?qu1PލB/ ҽ0˕ѠRQRL_c 3Pe1 fq6Bۜm:g(]WV8Z CH:r@l6M*smĂ;Ա6 Xrbҕ2?mQ)ʖN2`AL}jTL! x\TU"XNٶ-د$ Zມ5uMWp7j>N1rB&ھ=pBh7bʀ*n;Ntc>ػ'I@3r}9dS= [ 0hz2PwWBvVh>@;gL!#@})d2`>VN /d²!J2]<{YsmJ|Q@ -e@WhL&Ӗ :iZP@V(10P݈) Ԓ#v#1n]%4!h(q/wXпAʁ%k >5Ee}TbV ܽ[t7{#1TW Uc>h|xbFN4^M{o)C{K~&yPo%?WؼGDx&ew v``%TԒ݈m(Ts7b+0è݈ '؍ؼFmmw5k\3w#knfWv|p7b;Fl=ˡBdlZ(}o=w7"y-Щ ջD^>k^سJы]7Yw@ dO8䋯6<}Ɂ2B{vlڳѢ'ܒz7bT`0 NR: L݈kL!ʀy@ lu">"~V"".$LIـ`ہP*R%M6lrz !zpreFc~WL!fml =g2`b@=6P9۪SV.e@*n 78lƊU~8^XV<_& |Uza@V 0yvz'YsmI) B1+?vgn}lz1)&j5mTWec~e#[UF I01\cBFGgeGw A{˹1/G-*Лnjb*fY5BiZG-ѴRn8K'LL.:h3UM(F/[Zj% v7٬.TQ*h):@i<y뾧>bʩoFdپpt֙)S%[ګDsVϝ:,Ԇ/>?i~ 7/n&Tک~2G &Tz~%;Uo=8}a1x7b)1q "0P-)d݈)T=lSB2Wȹq k %ϳUxVވ[;}+Tي́j/+:M,wtxlI#Z+y-:t{vx8E(ۆ1P@dع:z 1RU/,e1?|}82`ᘟp*eva>V)WXSVc~kUeV8L>`Z l"\$bɋ?ǜVob-[͟RO~\nBA*kTl &ESnn,XljIQٻ2\OPk4/~wsgRqs !B @& +iJN]6W"ò6ǻ=|C4uq9Rm]K^HV |˰K 8N 'ۜ:cN6lPP8|c#Rd5t,$NL(&|~rʼЂpo^}>4@B{i}y) o"Ջ':LqO gG5xɣ @,tUXWt4 L}qʲxő{%,vM < G5g7Q2b۵l 0||BG?9Q^<wM輇$P,َhB1 Mkw/ߪ+}ӢyQ&0G0l^Ȉ9$$DYaAo/d@9d2 U7ؾ pn$s<[fzw2:xNg!c57dfoZiQa0m†U%4BB{IfU6Z3cw_e>d`eǭuNlY,dHÍYShȀHB9qT7bq#dv&ATJ~U* B1'2 'r!i Hss@7 =qT{ER  =>¸ngGS]y_w)xN6Aj4?tF:x6|BEO"U"C@w]Js$ >u1Q7bh J7bܸKn1{Ī)Q̍t(;NT x>JPLXg ɀrHȚBj Qm%Κ2wdSj)Khv`ژՅxr4{ q|JLUnYQ8h 'M]!;IYSHjBR5f_2 P*) M鿲̑rVy91Íֿeq LB`j땻.Leksͯh3t7cw}.2Z@E_:ϕ62o'Gܼ6/~Oni'8{lX@Ykr_݆{x՛+]hqogEN$#8SV!{vNXޯXk5XuPcGRcfV.2۶@ϞQ[V~!X7} ?f9ammu3ӺE=V?7*њeq˦Jzц B0=d@ȀF,؍E0F>)nēi(bYS)LxcbD+ omksN` 밝wܾk/9u6RE xU^0qG|h&B9XO]}IKWE>9)I`h5)‹ D<5Z#߶?6źɜπ' Mk5}Cb@|]U'0'u}h}tb}l841}Eidt,Bj-}E:Ew4Uvז i턴76 |vd1%ʚBd`:SC| d?Pfg_IENDB`rest-2.6.9/static/img/github-sign-up.png000066400000000000000000002121241421220614200201140ustar00rootroot00000000000000PNG  IHDRUBsBITOtEXtSoftwareShutterc IDATxw\7j aeTusRgQPQ+uUU+⪵[*j2,$( Z#Q䎑\?>>9ׯ@Y::8! P.BH#^%2f*|r9qWHT*R@HaTZW7qrH$ 2Gjw_H U T*{Wimݤaƍ >|t443qvǒ*4jS=544,Ys}9vе<t!POjNAA\I"JH7i'|>O(p ''MPկ_n:j>KGC } ݱm)u\u͜6>v @-1Ujrޖ-?w9|`UJO>Ta·ZDoRH+|q#wzkxlTKtCs+'%' [a^#>}5 {#Ǐ96ttr,tOn-,J̐ˉnݺ"EHP,ZC DETl};yWcnj14l(sr^j:F c1ޥ-10_[H.W<~x_5v%["~SPTjxVa귓ʍh,8\@DOrr$7SK"*7pǛj#Swc]/#.%J͏vЮ_)sS|a.ԇԤANL EN?Cp-)>hfKmLM򇆗0ף"lǚ<?MyBI[2^~v:uCۄ8wkIq]z:>5)z< oIMO=6ˡbw\a7ǧ&E߸rY;[ÛU00߷Oljܔ/]_M[혿)웡 hѸGwakZ{L2PkJu .f}5@4i-|w8 ; l\,mjzӬS?[] +?7#J u!}~*d8o\D],@^|B5U) {I2o`ci|1yO1W\ZO^Jh"NV֣6: UJKPs|y᫝,Ǖ5>%Uh׶Mlgνj9\}''%S ۵msjk%D9W]dl5*%].HeN] Lڹilկ^"m?xIk֢ww4uY%)jK⥈?\'$t|c+U S'DlO@CO,-VFFFLrD/:%%\^W"I ΨD7[~vq۫{s+tvIG I{t]-yoin]:=^q+W{.5]Ǣn$߸rb_H>9-G&߸r|"'fu7N13^ WrM L=leDGcȢߺD&E_>ElBz@O/e98%"2>HX[R1A._/a\;g}Qf~"ky._qᕃJVM; yEVG5zqv^Gthxgl8u>B'Uf׼M._NMvz/UP~*ƹ ^cyݞ[oUlu7_׋CLG_^aSq8n<рqY3nI'Zĵ7a|Ml}|T:D0?r%ɉ'1NN% 8l9$tkdu@bڅ7g+7GG WrբWPvC[gtD@m..!1?NZSj[[z+>B>*.?4瀶.CV<tԍїrv rcMѩIgvz|t~-tKԤA]{ztC<*> ?sjݲuZ*h ?oo׸]n-vbB:CYtK=sp; ]';tz+'v-qԤA|m()>Jh?{=']qІ<&'Rol`JʹaTaq=> Mmw:zq쓜Sl5KcV0l>Uxh3-( >s𰹻NW/'EiF-iĊ8gg[9q^jc{yN^oyN 8NXDzV 0 y`wߋM Ǐ 0X eA}R= ~l6\=oQ̸\{|^YЎtz1:5ԂgL Y&cf2Ohُ6F1"KzdX =>أ{~fYOqXi` k*$xKL ?yR3\( оm_tEcs֩Ӷ{.wډ.NQ;.غ{.=wusmU#'[Nz֮m=>hnJRR*"ڸ}^b>QvZLYсqaMSZL\98刈刈L ] ,ݽzmaǙ& e.JU# {-:"'_nKRA' RyvoDF+,Kĩ/>_i;l?&"O .rD(y+"M*CDRق)buw1j?_= 3vH^7 /k1"JKYȗ?UuvMXL6)DtfHA*X%*ҤK6$%U2NK.m}+K^a9v8;3:'n_q=#|9YͫH~pTo1-/r;qM@&OȹRmXIrq8iD" zq%%+SnNw)/\E EHܱ;7gv۷Ȝvi J UW [Yh/ > SM/9VJfaPĸLJqP_^921j*6 / f.i tt[ri/;Q᧲ճ!G<!FloI6L<w=|-N@Y[[KeYWz{F34d~nӽDl"QFLd2GD\ډs3R%;dYf)y27KODʉ &E2u(7I=cu.12^yRr:_l9ww'{@ r6ܥC{)#\,NbN6DJN&"R_:WP@iI GD$IN$"k>ˊb 4lMfoԨQWOq;^,S*MJW=udo DL7,sSX#Secʀ>x/+BV :Ϋr9J>iikuҸآjsx>8l^;*I><cWYCDvVpM9K͔!]&.S݊:rъ3 Jdz5gM;q  tV{1S=;$8H/< ^X4K*/ a1_="RTe pqn⌋quzqUʮ ge-Kn UT|^>nZ&>7Q戧ǖXUѹc_,˩]OaK,,'x HB/FDs1]b]V\WFͲtteRILo[{q\J+RI% luGDٿ f?ߦC/i{8V{%Uc\TLPb,{ 7rw읪7Y|hC+ok᫗(zW:̚hK6 {M` D6-{ ,'^ZEʫszYioן}d%wɣNʹJNhg+9"U忣 Kw>j}j玑5LVrYj /}%Fl2O}MLOj 6x'9/D9{뻻^/+ڴv0x%'>yj[f7%7kdQ]ۻ8'7cՒ3"$ .J 5ڳ> P=KHS.2oɱa);d<4B8v1:u0+V}MxkǩrUQӪȲ7w73h!qj#]TLD :L <spl)2jH:vhqyKJNiѢen(%7S۷mѣW}7({'X=(K"H#b6R딻p("m{W.|IRmEY 8gOܾZ=S> k>3`ż=Un fnZĐ 9Ӧi" U^mn IDATQ3Yfl1:#HXB""~Öm-౪,i^kgP& 4u]oUɓc6N_tDYM\,_=[;{E{K]&"J۱<֩ Ey&"YwoӤ&v)xJJmoll$&<6+-*ܑ3&OWϞ2c Uɓ/n5mw9]ԡ[KWص[30$g*Q}Mikǽ6-Xy v^v}G͝<aĀR.xmGl0_QʚS(hۧT'-PYEo(dRÔrauekgGLfË*7uAD2}e2W^#_xgj@5mvz\-NynqŖ" *y#BwG7˧%9rӼaMI%i&ͭ)?7L+LVnT$nJf"l`WG[.,rp!4m샋,]ܺ]쯞P#xE|pRV15z\S710zzꕛi6hx|3a)ezC=|S^禘e坽kDߺ\'"h,ʢCO>X,K^'n~չ4Isq~P uxì̫8/s0!Ņ5˒i33_dg#4fS"Dd14bw^Ǝὖ&V~cSBpxpwsozW۳w>Dykx\&Bc=o%̻+=pP/Eg6lB= ;"W,$QIS٫_V"0 ea,Iu+*2Noڡ%QƉ}k ac(%Sw {kej,;G?ay6sV/=ݤ'&Bo?Ia}z̻w޳ILnW UɥWWXqH֩c O1*,(xW+] z޸.*yʅBVE֮WGʶMi>DV=l4qڔD}9pPzvo)yԙN|ig{*+Pˠ+ax%:uT*7L$x?HZa[ק108~CFmoZVZ^GS#> ԭ+744˹T*.7zAՓ*pyy#G?W3S~}zmkRrrP*f/((+? _W'Mhocñe_u.Dnu_Vq˜uؾMkڨT5._c=_(~5%{yؽ}'1>#K޷pБ [Rk? y@-VPX8|xys J=z» <Y|pl/xXE>T?~_ya./㸂_Ė'ܥG?jG=)((D(jRAAaA>\o0oU _V=R=xi;kޤ ZWizm- T*R@H U>Br۝~q{~a{8@ *R@H< P%2j a(@9xV*R@H U T*R@HR@H U|$2}q~~AmF}뛙P# zHǡc~АW+70?>w~8NkaaQB֎U#t@XjSS!OCT+aju 5X;W cQXPhjQNȦP#"zH2ھ5gc? 6^8Bv.DJyC*{V&eZcBίlF<闾6F-xVo{zjjeO#kԊP^#"WVɈ?o231٧w *N H>Hfu0poz{7׷sz)ʚ:y#/KjW?Coۈ@>oj 6ly_oekXGݻF^uvʴy9 ۚR8;SgB7esfCe+=0wx\:Buxwf-תq >7C#띮DWߙzO\_b Y2#f8h׺1ޱ,m?ndQ(G5~uQ:'4t7ܼvܒƯ慨QSw(: s'C*?$:0Kq%>X .wTVGDgLM-$}NOf ȋel@`|2kd(Nx42˝(Y="33C'ΆN97?@v055NEϚzGT޵hN)QrSA$K7d'do&Hiұ^K}}iRX=^)N[qCuk?vGܳKOA$,9F65v b:e'n˵l|/Qĥpd1o{KgiiRȢѝO!"SJoEvC}ɕ??G(~>'솔#F{Xlg"RwKV{EFt3OlT_`yI6;bRGnw,7 ;d&"",MM6C}^'%y$ƍgffn9o&?i'V00҉Zڈ p_\\+ 7j}v5{F[Kw~""JK۵#ۥ,'UHrWA܀OKwh2QaB{ts 먯NzvɹNI+PgF9DT cY}Þ%ù ,͊?50jS,'z6U6!Es*Bʜ17(p#"jTv#icѽ+=K̈VL n7c@ ^4//_wmhيRYzTòDԶM]yC~(jKX)Ϳ%mA7%RRKn*رbdkci~D~VlY!G]Yg}`ތM}A6P&Zv4\+'}.Hd[jÇy/_n-1}}Kx']+z$7#dϑˉj]ZþHKN[/[򨚈1D׼o~MX*dlDBʈRE8Ʈ}MT׭[~6559l?saMMMnuV*JYbM 8n71d˪ vc fe+ evs|v<Ƹ ܝNT%&+Iho^P.G@bNuZrZVdة9xWAk.枪sF,ٶi#_kl#Hf+DD[&HICdlݘ!h;o>Č4|ʂ>h0b߂E'[oOYLM.5j֭GsXcFjժ%ԳzO_c׎@`K-Q}ȏ\8""뒞<0@pecXUD,b"vg8O:bCy5ƍ-m @"ڶ-qc MXEO(,Z3fJCD܍׈(=%R[:|e=*Jehg2OAEks:6P{Ϛqu<Xrêog7S24t(^޳쒇u2gWh˗\VNҵ;MhƼjY t4dd8۬u ӳYϮDTxWߖ{pn>M|8hv\" ]}6NOBW8Iҷ[e<&ko[>5vc~yOYdۖ㇣!ظ^Ar DJ">e'D)a=fKPzywt#֫"IǧLMi}D%.??Q&`_"[MXs*f_;PB_bm:95{uMTKWٜ_?\b샹 Ju< ;KY)LE@=]:X-|VH樞ИGŴĉ<8hV5-*QVR4aMoo'8Q_5'mxj{B UO(>mp]=hN>KFfWsZ*WzW|s3GjW8S1Kȍzj?weq}n$VWv/u[Oyt$}TZhp"8!hdHbr˽r#cPϱ{VϙR߈9~$޸8DR,޽{٢mhר׎ ٶ?Nz=[Zi4jԊSGJ2e) .W/Zj豃kc!?5jz_|gDxk\U簭\<߾r %WӯOEiYt:ygfA =ÀW̳ Yr)yRs_gY8lYhkPFאA Ng8':*.Z^@ձ^"222"zJ 3Z'1^M|ceut];fߞ>Y4nlm/±EcJdlm5Ddج1dZsdMw+M_gʣ~?\o|hw+㖆8OjQRy[7Khۊ)cuڼYsDʘ+,Kӧ鷍5jFٱc=IOs o¬~~3jB_v Q^_㷅?5>ǷУt{|受w{wbF O`~кeA_ kњu?<@PQ۵|J8}vo9W?̙(+Ƣ_SXlWGO rj]4N@طv3Y,[~9ȩWo\ާC8q*ШQ? 6, f_>|`;w> %64|;a ;bZuPS_!UOR[aC `||¯=mʷ^H[tjl#ha5M`碫I,ϸ qvvrvvB>TgDbc}qU5Ҙғ_-hҪT4P#*| ꮘna?k?=1ktb<$] 37v>||M/Mm@D. Uh,"{= deDzB8wG6zը߫o;#/\Hϯg[C\jeZ= v U T*R@H U T*R Pj)}\BuG`Mk:[-dɽG'Q:+o9YwPBUr6cqn5|$RF֜-vPcT*R@H U>BFZF8|bbc=R76ߪUKDM9ٽ[*|8}JV_O7i 0p_PPhttt ӫȼ{W*MW,vTTC}_zld 8hR6{Zde)}u~Px]↓g޽?VM>Ӧ:::ډ 6פּ}ܼLIn|]ǏpW٧&'''??^52 cqy:::g{K_ORTfGzV82cJ>Z]++olG7o!ݍ_݀9rmm&2vN. `WZkƙ(/Jtc{q]8Uy3vbRrwvFD~\;q:u}7xEo671'nDܜ|NXKNX[5Ӧ:::͚},i԰DeK{r=sɤ۪g㛨>~n$m"iBAi}BAY"   & lR@(T N[_)*Ђ4H$3IG$id>1ܹsssUà]kaV"y|U++Z6*RYY7矶oV/]yTI#El{h7`ȟ]5fܖ_K$uC}7u7kׯ6u;f]>{n48ę3;Ք_fMuu`C+9܃_웤<-Μ3GJӵk]֣G޻wNWlFN- n?)ypF}olÿX42 nn`ТR'ٙt^ ooРJxz=?Ԁ ja$2 %}К=Wz} ~n Nk4xz+vhժ(n+A:N:p6DWOlڥӺYKo?C,pA9Ww)\6:+~_9x~ioP+&mMIS?sk7m "F,[X_7B)Jfl<>Cz_.TYiշL^:vv n?aAڠA~0|䥃ꃝ.|.]i]G}y6c{'wl5媷؜Tx#ڿL Cv YyL9{a/ȽnؠF^+/0i-KDDDDDV*`'Nv*/g׶jaKjaa{vm/]8ei/upzWߢۄ<>ɇku=WK /xG?Lt[nGξ3W8n3y稪VR勤BUVQ=EcGzpVo_:i`ͭ;.>]=c1Mӑ37}h*7s ޸jQE7ΟVriVW.]4h4ghT7aW"^i+AeqyP1R.pgr^Zi7 Nr)ކ҂_--va9,;A7j3xpQ굇#C(xt<2뫅sGT;ӼC npUOn:7Kؿ8} ~#h0hvpzϿ%ɼDDDDDT1MKdNҏ)o8`)H_zág%tR.ԖD SGsNo ;c:H?y[d??K@IW%zجo>noC{K;WP\J xr}M T_4{ќJ?cYV <-C T 7_k}_p:Jt-7z6eǨ@NDz7/ަ h[*2Pl+W ^j-ڥ(- jl((`,@ίNh#?{ny{|fu/5|oR%? n_ޥ_agEe\J/pYޫڪG*T`Fja@UvP*A @?moUe0 jGTՃr;:[K:ҰW[MϦ5(:8i-I?*kDDDDDqo\Y)bXlbHlۆс!Y@jդd9 E[֥ v ͎ {D"Aokv%0W_׮GzƵPҶ?'UJ}w}SO sƆG֍n26*bc0X^˝?ys%%û|*}D''O0NYRnd~.@`?G$*Wv&$yxrj^9zg/BӶcoǬIg_8E [-F@)t򆄨>̮ށ/'+Qv2X%3 (Ý,J(7QX Ne, @Q P @6Pfu,7 RrDU*z]J%""""i*L21;;Xɾ;S#>;6eתȻqL:K+65ޛ̊d|OJg<+dk]Mzpũ#-G޿*v_ߒ;9rҘv-gv/?p? !eG~~pDb%eNHWlձ'y00pF~>Gմ߮^ܳHRSk8-ƗXص̸_ng@ݰ3q5z|hqĽfM;W 8('Xy,P~4T,9K;yHp$в 6~w6Yx՞ulxu_- as<ЅT C"?.+1XfH^y޸`GoM"1~l3 JF_82* y.5}~W+x]L>eYަ P=*Xu5dj塝= Q.8r,K7mՎk* z ,#z 9vk)ǯIW,8~P",srTϳ>z,ԓ[wTWlcDbӒ-`?&?5 GgOZ-bs1(xtqi+>kNpno ! zXfQ7}О?ݚ5ڍ_ je9p1 Ppȱ h!,? =3mׁU5垤ZUBU6tO%pAQrr N%"""""~~֏=4]$|AzS6|le/txEK[J#L)>Fԡ(؇1uv{VnQՁc78通ȃG ѩEmIskl"83d=;7|1~݋Of1 DIO֝fMc)JrJR٤2}~ҕRM^1(j垯SW}K:Nt͠~oL7ߥkl?ߨgwL$VVk6k&+~ㇱ[ypDcZ zw%%~U[ >T?s='M9$5K)R1sx!WaU˽ŏf-gRt?(2voChyCC)9M@PIoaq}_jt{oX=W{e\fCrvW {L㙎%"""""O 1B$~r*[{(J ᅬ!JA EboޯVBUW4a7iܸi_M|OΜwImǗsr۷Zs`~SsU(fogLN-v>:IBzŐJ'O~cP(Ty嗫Ӵd2^wiT[?[&Bܚ5~>me<\?7LxɽP9?~Ub'Sr j%Ʋ:^BrkKJAANSJA/Tie΂PWl2p/v#Ձ#;mܳeh5+n8ht*}$"""*"\ʻ 0,7 cX,(T*_<> MDDDDTDob\. t8QEqkS DDDDDDDDDDDT """""""""""1@DDDDDDDDDDDL"""""""""""b*#I swA Az0ƤD6 S DDDDDDDDDDDT """""""""""1@DDDDDDDDDDDL"""""""""""b*S DDDDDDDDDDDT """""""""""1@DDDDDDDDDDDL"""""""""""b*S DDDDDDDDDDD^DcNFc Fz=04MSE:Xc &ό9@Za,L k7ޏUB/ ?=Vkq_<;kk}[]918GKYZȷ̢uNZ7a֓FFj)-z]J?eoOsVbpҼ6N{\ZhuOѯRIK:- ;H$Br?0`,f]c2w;(R1 FJc;bsؼZd"G-Ⱥ-pi05P-;fk?۷).lIkv.{7l<[,S?Uąߩck{Zۣx3moN5uϵPt0qEW `okqw=3W 8)w# Zժ D:1iZYhq+MO%]uwt\g \&H0gϞRKqԫƞpeWbX&Xka#e'H$d5IT^[켅"?\I 6Z]) 5P~cmv5s f-me-)ߛ'eՂ}=Zx7 #rھ-ٻfuڢ[p0sYν%ǽjBvG1V.v>9*Ӻ>W sɺg ut@[T5*T.U@|5[!ق@Tܚ%8T wCw|sm}skAӑ%#s\ *A˃dNlx  U[G=ljS#8z8x9]Ƀ {WBp̓-8z\\ʃ- :VV)7_rFc0 DB΃ăKW V u(aX-He2YBX9%9X`$l*S,[ӳT\ -A稂%[T7[Oe %]ύ*PCT|*TA8PxTˈ?Q1[Z$!³|x ;|^F#YЖimha7cIz*x&L <" e PpEgG|*`Ar- AUeO8Q|*l⚊³fH؂sP-xs-@yDDޤ $b|Xp*|*#*UNT"  -x*&sBgK.O ~^[fDDC$ yH$AtF;9e$G aȃe \ʃQ^lA"YIN$̃ S_->^kHfN0lVWPr[ٴh?KkB+7cl TނG*7#@fە;XSg5JY U`fb*yr@BRٗ*7/IV5PdRDQz/ArS D$A?dqe BSy|8b| b{"bA0 x%bFĂNow]hch#CcT5kQn5cQ X.igg/Zc؆Py@TD2z$SRB8oOX$% UAHĂ TH$bABIFf \ĂGb*+hl%7g/}{h {4X 햼?u*3'Wڿ/^jhle?7ZqQ4;t ȕ޷I0o۾=|)k7=OrIHfv$ H<&"3/G9,D5Vpr TWcL7*7#̭ʩ0͸Wn0KO@v;Rك/z*~owM:KS@U}`'S7LgP.C_Y462^?kƢŃk;VI%!m_Svr݊M+ƿ4H{yۮk=]yo~@̈́*G2FTB @ r y*pc U FH$ YS"`MVL4@? U{ZZl5Bᡒ4yFKT@LŠ) "~U܂e0bg4:bP"@F=sD68U_h* qg X0 [*p N"b 6\Pnl؂{T ;TAP*TRzߵ g>.-+(;b $'4\ҸG퓦.^m¹X5wiɋo:r/$^K&b*ac$wu Ǐzm7sி+Vd?‰yzvm]ϾgnںryIk١μqZB\8Yˋey,jo*Ƀd-KNY A qƛ'+xP<[87> A)`ąB52ApSA 9k~۽/lM2dQ,:g㪚ۇ~9w-cz=?]1kCuB\A6 =q땰S=\A"yV !Axf UcAU0@p{nn:v{ey P@H,NV B8>q WJu=oɑܭ{ .K-ּT|ƒse]{q-nɃ$h$i_-\wL- J΃$eʘE(qfꩣ 2Lv✏5ib @7S%>ZdMKgpݧ 7N$g9|*2}NN( ^T!o:q53QԈєMlU=n)!MvAm&GB?HݵR3: ,A\!V_ٽd2T ~b U[wKЈaF ;[X}{h]7eʞd\~tIky3T;GHf+7]d s?k]VT 4M^o̴s>d?kiTx/2&C-Nn{ǔWzЊ Z0H~b@$xQm/S[gt(YOԦ *+Ӏho1bmz~Ϻqqߗ'Nܴ]!/X4c7d#w'd耰PENvvS.ܽCl+fI9qAݭ4g(zmP+7;iWsO^x5"i/%E,x,gVXݑ>$g?sn\3Y q_'};KJ4,JֿwNܹ{۽kR|SdU5?^ҙc6zP?~{|〢 [{<맯Mkt]åvzǼVfL0YWdr~ؙӛ?TU&;?=vtnKN`40)ϩ XιSֽIUfw+ k5Un@E |*7*7M8PXxPb@Qhhzޒ۸5K@DDđ*d[ٌ*<9b>k id-&LsflP9|^ݺC HEŸ63G'&Ϻsf6j(Dž>H 91Y̢_z6EdYE!qtɒn5^2ہ!v*]ݧ¦[mHP8C8 uƈg\6^~Zr:R {cy@UB*M@EVÀQHp: +Ք}Z*.T3)(?u 4wA"@U ""Upr3kzW}d :&LJ>-&D\Xx ã$TA20=#.OOMLQKϫ sp<OX yHRy|ƂW YfeI zn[b !$1ɀDZ6E::+N*x&byɃ74Pv[~d^3)ʾiZ*ndHȾzE4y)75RuHp.]#[}t3̿f2%nTsizWeA1S.dئ LFMiIk(T-,8NV-ntkFkCuV7,UFaմ 3}/"z m\-0BײOU k'zmzs'tLvaKfɗ*7{*U@>ؒwvXiJoa_'UA,~FU_zV-W6R,"Tpeypeҍ>O穂/WnV6oI3J|ntb5i8@~|eή[ ߻#UgTPޓ{q0}{dwm4"frTg 3UCW3!2'U^3nm?bJ3ŝKV1VTjd\yo_uT QhM@_Xz& t;nɄSrJ0:ƾ/70@G0h<>@E;TzpaɪSt!̺qGd+0 *'Яlݻ"*qc@Gwk,or3&=LU ySlA$BF$BVcI$J"K[K3UxcˀA/  $ n:fCBk|`җ]OSYO}5uipvivŬ;Cؤ J.o쪀V;j7}4M3Ry0Ba]v9@.jF2>Ci) dsr(#L*ݞi,]ҧF=[+O'@ץueFHfOSARJ$!Uݝ'U0ze㋬AGD*Miu[ÿݰLbphG(eyj}3WU (C#6G\5Y6q^T31=v>||h2<<*enD(XYџV-IHfhZY# toMdк<0pyƴ:iUDZi:N?T.c'[[k*,❍۰mkjѭz{݀"f`i_}$% С]Lt2 7Tj5O'V^M_耖JVft\ umX$RǨG,`oTnƞ E,p bD +1b=՚ 'J/3iċ͔~a_FtzMeʊ cVhD?/u1G2B @kB3 rQ{y~`XvHU>x.BAEf6"~qZO ?_GoR￧a/TU "l*7{*+5̛{fYyӝ"1v BMp[l+6OVI^ZgՍW通vR C$_X@܌4wb0LDDTGj}Mk=#H$7*7#R,Bٱb*=2VM7[ y\̓zmfNE,8ح\6d^VyG,Tnv!x6i.,؊L[ DbXN$b*=ql }gB> Up-@KT{ BQX~SIfT[UhsMGs/87_~cyA3w j6tgkT.[p*XE,N̨}9³"ʆ*`  b cWڡ - {/wcA ǀ F=XF7 k/B4Q9.u3o߸UZu1iL]Ef{u 3YwL\IۦC| LXE,՚" P+7wp痃)9>>|miӷOܜtmf{xތ%hSE G^q֍ I]gآ P1͜*}g{ ڌ WBF,Srb ֙gxAw}YN߈7ʦrT"Wn> ܩkl1V ?:xHL֠DNH_1r'I_~mߏ'5DVS\ܰ2)S[RPEE n E 6A2c 7~*7"hIiEn 뛩 z—5&p2Nn yݙ;bAY3^B( Uvz؂G$F,` `_\3^Ў;}ײj#^|8CW> )ܮŇolA9ݸ _>qGXNx5'0qMގXMˑ^BR^l#B[{Th1"vQНT5|ҏY7~+VzppTI!s]Q?Հw {x֐Zښ*\cˉtE7{=.Ed٤ ߶~دe㸸6{\?0gPV[6kc'B8q&ld=ڵі;_.-:ȃB?*e'C:7kҲ]OWYo7bk4~p[6n썷L=.>hm6n78Єfoͻ+e~I%*ew;m~Swj;}灉lEov*50R2lS?w~c1y;'j䍦ou01S1@~M,TAw}w4kΜ &en͚żu}!]9Mc^nֺy]Y): Ϯsth,*Yw&~ _ui9e_WӭKK1/kӬ][cƎۥBK;~}b:@(cW߉ )d-P9_~}2WvUm-ZMK(ڭpzqk;OH WƩVN_} N8r⇡tUs#wpb08~"){#  ˃d*Z,˜F/E3,!~zeP^ S|*;CaC&뇁Ŕ|tD8N#;P4^S߅RN ZAQ͹?X!z/2Cu>02Mto̹ڔO1MG~"XhԠXu4c mԥ}(B8 P&ʬL$K6Һri "r+rA&7EWJڢKkSM%AL`s>'3T LMVʞ?-eȘ2JWTkЛ\j:cڔ9SQ.V3k8ɄsgkO523U *"i,TxrZ+c3:'VtJ^0z2!vx5˜Obta:T2cL?M&/#+J/жyjlRNff^ ~/vɝ+R)tZqJS8ckLs*yiɊXUeϛb8[KLkxnڳuWaDqj0%%/2kf< IeŹ .LK'+I U9]]^#Ϟ33I{NfY@[|֜1ou(^3iA.QQZل;b ;˧ IN yc|±J̕31&%aX2ex<u3GL,x;sуU 3f-Τ∸l5cЛ7_Ӽ4%"t٩*7,@!D*g*;H޺_}OL*S+&f HHNV R]~̞YH(͘Jظr͠F&z(*B !'0R#X;A)SghB@P$A#D ,3/槽q  UR#U}| IDATB& i2, wrGՄDT5Q\FAͯӭtlMqXg`7nrv BH./YtjOeԽzOɡW֮_ָO †B$Ar%a/ml0jʁ*VQ`d@zcc ;^Ѝf*uQ Qg`7 \G2 e :W%ޝFB}sr PPEIQ``A@PƦˡD[,FrRd4 RzsRtʵ)+䱶kA,AQNJH=p8.~hBMV]o0xep̨#ְݏ7J;rI0-L| @(>*Jjb5E9oB_Q=T$رy$l\P vPSJ@g/XeB)&e' 0tm΃'fXMbY*r Heb6"7IdB;wn*s3߹wnvg0 Dj] (V8Z&E1U֬~ͤQTvìXKfi d]TT|UI:P @d_ݷa`x ĥ.}䡺 _|^^Wߙʑib ( A=O wVw| r9ٻ!]}NFC$5  Nx$6`+'čUP)05:WF!ѪI;^(W(dȌuz[*/ L̀˥UKȜq(M"zqrM& ѰCQgp<MFcXh?_|&TJy:QA7G˘[Di&wRΔE/ˮisZV/KU^(HR'J4 "!p1;:H{M;+hVz(rl0_ig]JPj :l& i, ru_skZ=q?:aaРmhb1p T_ z5\1;كi ƥ%߿6q$gVgDoטڢ#v$~chcιL`g` 2ز_20KWO|d%ƉUPf%?cIX*ꊋ.@ڰ4Ei~Bnzv#S3MK['Y޷fZ]P):7+)31Vߧa+'a@KO:z mb5I㒐%^Dy~&A fkT5ï OV h R钃GLޯQO*Ѐ<-]C/*oF_=-acKil/oƈ/})ˋ1FsnnvvV)g0e{]2al ĺ܃n%)'[s%:'稀@o61_~Je޸d =fjUP)"5 M͌]HPWMѝ|AF3KABYkTuj{`$ss;7A$d=mYlTׄ5Oԯ&lgNϝU:HVA2ɺ6U{-g߻@|#Og]0ed~L~L7qlȱy# _~Zi5}GCOGd㷽48hCgeף}mc!.g) ^sUS^<0(Y8f#>ilMNkD9z% ޟrժ+N*غ}6qjȡ5ovnD9潏4jNo\+}:~{N֠#N޴m *XkY̚LzͶI'F?!II=`m,QCf=לm׬{  +m(5bb3"?Ĩ̡N>Aoet(]:f*Pߺc6yQcJ߶kv_&?dþ2u >4_2~{y& )#ZpTWV)G?o)U~R חSv@܃neb3!qƎ=vڵi qaP|3V&V?7!;mtOd* n]f?5;5%5ﱣL89oZҸqϚ2̕g OϞS7J-UTQ>\6}h֐9=4=) unyf:H doZ 럹V:a-XoX{46GV!u Q%7t]w x㷆`kod~ɎUt3Ҍ!ߒ5|=\tgǂ{qD;(TV~>]H@p.?g _yx`9Ĩ##K5Brm s ڕ1Dל;-qNx./! Mĕͻw?#/ hxz|?\k7*t![޶}?_HC2nܺ( A-n#JۂU {B|Q1QJ&xtn_tcs4^(JfI|Bn1'lx!J޿_fⅺ}Co,32*~PHI{S.=fI[tJvj,`:71AZ.ҹ*[X*b`)}z_KH(}X*H`:Yθz˸"e1R(d{MNn,d;W)H-ě*K?ՠX|rEŅU}hmZeں}8*9}ծ)ǭ;qu[?bnVp9 };݂ǚ~X],V0$$$ imy/@&;_%cib:HKdRd!CC; }-'$-0[@P>2oT ,) u~XpY8ceI {vRآi dݝUpp !+PH+*,dWba> O+Wn-ytőQ੃ڊ! qI)t0UZ[& UL2Dbp $$2VAA:Hz,H:Hb C՟h1eVo%7$#*:HyB*D I&@ne5DbyYB b Fp  0w:H|e~ n Rt |B@$=HV ԵD8@_L(ޗx{t!$ CB 1Еo/céd|uY:H.3Q AP Ar*pŶ (?mO\绝>'+{lcdg 4,g ! C}g2&B\ٖrdg 8,=>[^!eu{ [ BeYg{O~߰ւK_7~TaÆ?[ߘ0BAwGO6pذ1s7-RO͝:hIQ;9q#+1׫.3t蠬s_4M:fj3 T~5/:r2?fpb5F4du,wxKMFtѷ0`V_egЊLmZn!<}a눬o9S&|zh^أsMJ6#=YTC9O_>6sĦj֟|Y n8YϸboZ-Ty3sGeL>iF\j3GzD +0!`O8ó=ᔞ=g7x`e@$O>Шҳǯ8~.KW>=`p+W`K٫/:uU AANAԉt$^jHR_$LL#`KjwT~`.|b.yu_W~Ba.3 Yz;~srý9T'v뒟:nؽoXdZ1Fjka@Ȥ59"WXrҪwyƃ˷5}e峗K-ۗ^^\cо[>/V}TIsUQ?W^xt~B@wR'دk+RO}PUy*W^{fsz?׏wf9P m}ρۏ 5{~~BQ†cf.۽;i Q.݁E^r_ޝ^ 3@Z*}N94-~jw% @$ԞHzOώeV^~}| '͸#/ 6\V_:+}k,P{הӯ̀Sk6n>DƚϿ;8cEjɱ/۹Rf'cpMk> җ^({a132ժw~y{/0 c*v)_R+ֲ\2c^*:^޹'v\/ZsAo r!8R.Z^ɗ7,7_]?ܑ+Xh/P^\O3oZ_bªoZL$-yľߘՇH_/>64UkFS5 ͗+u׽X^A/0YHdmήOLEtO\>ڜaصpGucl ܕ/}b'*ۅ7gg֮+R^OV&;n}BsKv꒟\ߎo4|bG5o?CfٺTt/|TT=@غ)N]X,5"@<ɓi*17y'г tNG-  "w gW,H[$,xcٸ*$0QW)M3.;] T{g#@ʋƔy lGgae+Hzaat٩*_F2!?1p]TsBӆGQ eCnY5О8gL{dq*\3mVQ}ՕWz$CIw IDAT [0)/PL_=M#GX0-˳:?x!ٿA,% W M7OIeJ0b uL9`@ #XOLG@R@#ڲ l)X*g#9vΔ9 Gi͠.@pb^~2}`)RX[rΐȒXsFF=NBsW H@}'#+^h}ɺLJ#'ISٴ-ζ?Xf 'd?q>YyOOLR٣@_`EFa@b ftƴ))r͔GsU͞B_(׺nzIٶX}F='WE''`1[c zkhq_dG$",̘3*hAn훞FU5Qtju~if۟ymE"R2.;ܠ3gL/(I`*B%iIeCՎStFq.qƢxuL+Ss)26>ejv//KҔ$BgΦOV9cv6y撜820 Kk5VGL=Z{v3U;7a~<€lɸxgcaux;ۃLp eDAss" Y2ɺ5LT~3o6ጜI{>l|XP_~!yJC;nMq36τHZ5L]qAtqj2, 7vPg<Ƙ>e(>rJTA2R!7J04O _x!W/;@KnÊ8Xh=U &X {/<$ Hu4F&PSr E&SZzR:i4;PQe|/%a1 ^6Lb0XZTj Hj7-;Q r]#PeUU1X3O@&=#{ѣR)ҷRMs}ف}Et<ƑjG3Ib0 *5ET8Wh` D9b*6owaj͠P[|Jf7-uŒ[&|;%-=7}`D#W6Pvs"9#\\c”<3/pɧkjE @kKv*֛`i6emD(Քcm#+j2(;" :U ewF%ul4*QeQN$):6?cA*$,2_1U[N jD|wyTY ܻ+ w_r@}8ˇqx)=i8[ ۛ|K-Z21o 8 B ,;Oji3Ӿ:[oFl+5jý853߱iO$",x ;씒~Jk/c'vgY}XIIr@GM=pv|6c+2) ~—=vœ:ivI:^y0B@( |LBAtˢWHu_-?ƒG _yoUjW$jM]Twk|1]Xt@UpjLpQN}ue噳78XW֭rb<%C.SSzJYSm&plEW>Կ=uzy%0~-_D<ߜsm7cNg}b{qcr/']gXA C£DA& RI#OA;7)I2$0759d6~Ǫ֝8oHtF@AQ ![ld6~פ'3uդ'(1^c UXThCmkGF!TJ9LF , JRE#Zvv/sBlVSr U:"u$!ZL{֜;#3A_o@rhfF4hmyD]`r|;s: .5 l63 E2@WQ24^1UQ+h#S10BJRG/-:YZhPhOYvXJ] f]Y;#"-бs+#mNz YhdndJH`Nh\vVAA)ThzS8ujgv|pdUTnl09tuFPƳUV-3ɨg`rp}/7ll +yu྅;6ozx}CǮ9X}p9dgQ׌m.M 2scpG,|2͸cAcV߹.:ryu[4bA15}F2aG4 oF5pȹM[<&"0]yЁ,/5L[tt‚z>~̬?ru3NZt`0A#GoZHwo=6>k삣L)1ܓl:ŽV^ /Y>>Yik %:>>gѓi3]uRu㨪-ןgX8y,}|ts03#zMAy @`j+ǎ| RqӝwffkE>bmi=eD)Ε,w?L@dKj'lpՓ^ۍa$P4ߙBqMcdZy<;7BS{?_*r+[SU/+Al2+{OD, n\;rG8. 9"d9gf X qM &*?uxnqʚoݺҭdI {\(|=_;lMY]b^b+<=`+=RIcE´j!/UwP@O6U)0YNOV"uҼ?ʙ}a)][)-cϾyoHB1c9_ b87>!bD*JݼySE#:&/tkKK\Lb`C:c+*!rv_CyV,$ D1Kcrsma[l@4dg<<^ZۂN3իK% EEB[nKR$ l Pd8d]j%FCT'B̏~ ]p̦sBGc 3}j!T7N2a>e@;, a H-jEB(*JbGɢ04y/) I&dgyP`߷f2s$xBW,DK7=mO= k|#i$b`{w䋔 @9È$va ~_ yQ|)|8[qukssЬ/cܞ׉jbPX6{d%$r8ά A*Z)Hօv$~C]+ʭ5 [$PZ׹ك sü!Xtwbap V!@2Y8:7KdI֥ȅ!ߞ^v ]Eph*$R/ 9:,m{62uALZ* Br&ṗ,9fN {Њ8z.O Vj(, Y$FU29U,Pp A {~o/YEbo&a~` \o.7nwbJ0l CGNk W&=7U3M,$P7Z᣾nux@#T@9x5>{[Z?eqmVk[EEdѶ˂{ȝ㼤dIN[F&oolD?ŃoV{; Q4ݯ t3__L77'Qt?>8?s* A@wNx! {,r܀M瓎./_ړ{"|0(>jib6vŨkYee] yoBE͂BZZZ[ZZP uXWA ?[% `H* @Z2ğm!_ED' ׻/?ICi=7vFNx!_ n;wOr iDHA܊.ipY M^eKDlA x,X/i}Dr7Jv n0wu@XFR@Ng?NMMn]_vѤB?5n>A{> Fc mN7btuKE$3÷ñG^Kx޲Cq5@~b|QEne(Aq} z[$$d2QEEEd2.e2MOW2nCRU,BU!&u-Zr'V2RV~d,ZI|r3/O+X 0+ZD z &S1o94Z,T~j7SG] _  \NN[[˲OWRPP9X7w {Z7nܐd^2nSR[aq S`z,JAa#MY23sj=*]6tn6n:z!s _Ra"BXʣg*TaoݺADEuung@5*tlz Xb:i;B;7{ڠY9>5{V_-ZabroxʿTh >"*{ȼzWcʝ28p !#cI!&& 5l77nIy'xgƄ  du;s"ଣt F*`+Vg;''gyB|p(O>0ꀆDQ|hR.^hq a[@un $H#V͛71QQQ=zAtW b.8/d<^$B&] MV0 ;CA頍ߋ]XBҹ9>+[Y:oBnܸ߇;fDEETu`Z(P2O>seV硻/ŵ[cEwE $ :@< j\eiv!I ?x4̃!q@ Ecl#m%E a߬V%uRvL2:\ǂBc:Ck[6H@raJLeψMՋZ ](^fw>aeWDUţR(8V!jacujz%(-%VA|V!RI]n%Qx};7w{{$ !1__G/?9<4 IDAT aM^F>d*@' 8MlV"Qe,ڮ,K|']ص ,D򳩭 n$"8 H[]fX}6H@r9A) ǂx߇LOCqr sL+&;W* km}!#scx ;ɜVef0aT䞯۵ ---OKe͒I/#ssОC$$FPD[ (av}K~(fv:B'N{,B5unv\yϱH"A;-q0`])kaMVy\0.:?25h wgq =zĆ &&=dAX r V7m9I9iw+eM7n\xת `CPl. OR  [k fT_"lO ^KyyI']R&!எ;Y8xqі.U&R>L`ы^Hf?PxtocSn\1mQݟygcβڥpxXӡ<ͻªQgV,WSTDArR!BH.:wqq˱ k$hY2ɂ Ey >^Mx"S7cYWSګkf@8˄,^բM4bch{*i({ˈ.ϡWU=EOwMJ5qy7?wu߬/5sZ^$+~狦p2IX I AEܦ6ĘnB|*a@cmE\*W$L2( _M!Wư\oV@c{ >~LZZi=}┿d 3{gJsm,Fa!5S}8ܾ:>ʊmoO?~f,(tސ0W>y /.{kZHg=?Vp[@ vaL"y{} X% rdV#U?lnnh'k U,"+׃Nđx 8 1y$={we_S#]_W k]̝C3HoA%R˷j_iRlGbZSdv3+ g{ҝ]? j1୺ǟ ;>z6fr1rθ^T,Qx,PAD 'ʍM"aQ^`gCUZZZB$IJQdl t  rξH<%bM//?ĥEOq=`;rclvDt~20CDG|KO~b buP¼;7dB{WMrݹ W&į0`DQr{Frejt%V!ҬVУGI#U2Ʉ (\5Vq_rF<Ƙsf!JTԇ(uP{4V0":i{\پ3ۭIɜIDc+*aq`O]HS㐈iu~(G(1 |&"ˑLV!T{vVn (uƘ IH2ɂ-"9 wQ䆽9ʚ9:ղQW~b #: gD cR~[\+?soOFݚw&aEј:@7t{$s ɸ0/p _tH78{4^)mimm HbĈCcY FTV͛7pLLtllO=B[$BxMHħ6bj 跧$AW%zܓrׯ__Q?U-l?{g;x82~D&y|d{7&*uʝ-gcᖭ?3#_B(u+d=bFx!`ê6 <:dd2, \zM:j=M2nG&Dl9pXqKK//7éq/wWoSKAW{MHzZXD#`ן˯h?*|޸r B+?\ow= 8(rx DOw\<`q0؂JVlWT(J3]pHHK'ȻO~0--->A2ɺ" '#o<I`ݚCKy_z˱oOH>ZK5sմ}==oe0i<3fX1ިzx_'[܉%;*:ue\*rn?LdA5U|4ӼmU#e[VaVMH dw~'bi1sᆈ9פ %E? WVV_#ຄ9I/Kd`RN}zh!q"]CUq[[[ssMR#:**66;ueFa_u!5{@k  )~ ‹F=^H%Yԋ87b;moоCM.bbb$2A2ɺBW ψ;W,pKe>Yu/-{wﶊ r-j~d./31pzCqBn3# YRᡣ>, jea Bwqڡ֖!/oݺ/e2YϞ=U`~ȊONw$ ߢ պk[Tp=I@ޣw89 Npz=zn$L2ɂ;/B ޣ@6;uʖ_F}wn?5Ƌ nE9Zn xɄx}H5V E 1%dARA |'qNJ V ] M6#*|`.>uP &ggvet]]mkv/PHd;kmw}bmduZRwy7D5 Ō蕍n֋ 3w uku&B0-0JX2$G Wng0=!.81w/$W?nO:<>QSЖm$b²X߆ R_ɤt:^{%08PvsX~~wtvf{JĂnX$L L /m;;1! zn̓ONԂM2$o]*g7 =k$Y q^=۳ v|1Wa@CxÎ^ΌP{\8 {6@OyWT? 'MC5<9?P/  ҬŨGdT1IJZ&a ? {nGGPx$JY*3 6@F e_Tچ(CCC.Y H;#}r* 2=Ra#2ǬiGY2YG Tù%[&O)?d읏n9V0+}'GRa"Eq号`4sKuz~~Z%f8]ᖊV;NV@>=~z[{Sٸ% :d'UC2g;kϪ j/LtX#KUص՟UםUG#~1 Io~*M4v /i+(ϐ0SrQjЄH4 _zH,xCqTPw cB\lfVܜ)SHubKpy\.}<5pJx?0:Ueᱴ/C .F7|GHWv +"Nr$+)~з^ H092y]W jei/o uiҐt`B@`=jl# 1c-|3>Ŝ ]g5?g#d]{1OTX۩/,RO޿hrcO^;bte0jJo?_Z9w֝U-PeB&8˜*TfV0&@L/+TO@L}jkwJM)KY S͔-|`x,C bckwV dm6#"5ŒX}<,!Z;M e_xEs_ƭ{覅YSQfqva% `{ /Ig^;`QL>Kb{4d-s*(?;Rܰ,~H|{0}̆gxR6|޼FQ;+%V檉xs?ts?-8*-Ur؛{cFU$^eY?m;%m&>۽Wu7숨MQn,"'ߴm=퐯VV>_ADD)r.)#,B4\nFK,G|ߡG͚]? C+-W?s|OfeDDwBWzu"~Ώ2 #j֔Cej_󍮲PoՏ_j/?ov{Ef̝4C,wFs}]ar[8 I5ĝ(,$RF-\wRy 9Q #O3 WWy IɢW bܕDDua;3M}V}|(-YO 1ULMH?Nߪp~Ϫ]^Nsy[H6< !-\=+˶E\7 oGA BꢭCrt6?sdϗm8|gȦ|zHu{׷9ԍ5'ȘCƈژ􆲮Oɮc7n}k)ߪob]zn*WRNDDDD7.5ی=ϴOM4j,UT,?!RwFDHloxIGDO.wm]]5 : m&YX^T,75GCTEKuMSߓ?mY#=:e|l/&; n ` S +Ɍ[IDATמ!"nyU-)// fKB/뎂ժFY;z޶^M TSx{R(Fe{Fh{03ZB p.-N[lc)t`9R4 r<~8ydg#hx^͕* +-qDU%rV"c|]m)+bTCg :{9pcROjeiֿ^f8]C'@/'GWR屽ma<%? c|"b{HvkE*ƂC:(rp@)ӇΑg+ &fxFUL8 : V].~lP_{*yDĸM#; ڋrF6kT$ޒr,}c )l\n'2FG{0ڪf"h#"2`F2HUsyV"<;jlj5r\jŵ2 Xj[v)17oZu'wJGdH^uK20ҷƤS1>O$-c—[NlnNͬ˳qMQ[LD̥$yף4oiCiu#cg Z1ƽ_oO1dVc&ZqlyG۵޶Yge7Ojk2:iMKlV]˘ToX4gU۴mZW75&C+)OB$Xm;Pb*ǖhiĈ~7u NxXU%rr3&K,KDD=cUDP@* *ż}CܔSϐ ;),yJ"a {C$Q>cd3 ~#O줐`i^n6N5QqWQS"/ϱR& $Ue Է,[RXՐ}oõ g@kEە.F}fv͉ޘ7<@yt[G{ZnwǽsxU{ZrT˾\E!UWixD$tX뾯Pd9'lUD=Z+WJҰe5g53ۚSgS[ #*tH"tlb"VyjkHѨ]gcVGMvSuN̠S5u|k ]V#5 "R&.;70 RUb;`l;|0+5-'5zB5=e,X`ɹ0נg눈-6$]7)`c`ÉG~hc4gݍ~Y 3Ey<. JD$( W >ٳܱ?hc`Nskgp.UP74e;1/̉SԨHsiof G!2 z^Vѧb)7B^)&cS׮Ab>pI紳 2nz-Z6KOO =t<tP=ݷk'׮tBcx<>Rf5xTUsI#LX}oYt?-0 X]ﲫ53I ^$~=bͣӨKg'En.g\>X.:<֙J!]z0ҷ(t0'HwrF=DVM kr:4Rҽl%DMwrZ^KV$QȻU+7Mt5bTjӕ5m?gnJ̛ x}icצDZ{%#+zM-]cc#Cjk3֠8FsYsĠ;yud3[1q'u5JY x8JZRdc0'QD*g4OUI> ZyGjDRW.Xu_SSWuR8rZ>p3!p懤=YyR_wVi*ΩiuVgUAޕ|+*)z{夥ĸ 7Ņ}]>%Ď5:aO3 'Mw e^UT߲VW\ gOJ ]xTi wFUT+kJsg2,-Q(k5ey,J/j 瞺P\Tx&3))q\|ewpl]m 嵼čYuN\BWz,\٠ɌQ]YRq*ošeMARTw9Ƅ$"x٢ܽ g;̌I5$瀅n[sKh،iv?$e&Č_d MIϋMDDLT3 g4e7[ \j;rⷐJs鄓hNjEڷD4:.W p~:kwm;㿍ek!raC1үj#vX93sذifbp>⻮OrkZNj|NF <d*jwĢlp ܴ 9rj>Uit\MhNqY;Nit\@>32rRo>‹jӢTijv MٴB<|uѻٹlHf}ZlǦp&/a`EU3kW,?3gw:l}z{sD4fd֝K3T@1onH1G4;FCI% ,__S?mMd-9ɜ_;LI=n\/غ}#eV&{ؑsN>3!Qsn)SV9 C/֦ͯG}+A^jƘ}-ڥuS¯?,%=[VGyh&7lL۬%"jrj6~$^?Q!z~,lhskhEp|9 ^Z8z{v[ ް1 Xa-2$z@`(Im%FI]^z:-W&>7}6S͇m\r7߸L o~}J& 1 L'Y7IDATx U}ޙ  D Z0ddl%|ڪbV+J}[[⫯/TkZ #($LBHX'3>9w,wN9ɝyso$yv9e$np\ﶍ~5( jLJg_Tb𫄖8ns:>ݾy^4:iyo&~"'mU xK M \ǿ^.W^ps10ry+KNټ(8b뻋mZ?p4u9<&6]GS(smxӆ0;ޑϴ{=7\擱& L+E7Ϝ׹\~ ]l Mf7O_8{8 C Qg Qh\NNP9:zW I|JޱB\߁Yڷ[`ƛ4{Hv',uA̵ ύ 2t;rV DԻۗn ۣxqb$#}g=χҾ( ߸fOl:vz5H]a5k~{Ç!s7>Ŏ%3U<Ǎ-~i㪥׷! 6Z} {7"8 8~y:gʌҢa -ǫ(_CJܘ6ڨH٫ǖn׬};bKSyoD\e4펗KAu^?xPPcY7d [W)zYߙHߣO&~59(ܵθ:r_&B8Mρ :Bw2{2"o8'~+j.-Rz?CۻZϽ{R}xyt- 7`q8x $=J]2P![!QkA}%854^ZdRYNI;VǬYO۴ˤ@\* cZA| UF*ӛ6m̜⇭xSZCV XӺm,B+ :,_|ז5NN~jCh&R"Cqlt>&sP f݅ Cp@}sv(0܀^_F֢g`.g[6EzA4wz5Ùk4JeՁ`MSԡO«S*N+TM]MLl{ .NI{k_yf¥4UQRGJ1X{(n(Ij/4xHb[ASg8ɝzt[聕=BJv uw*#H}?$!ϸ`\scW\E"{g]Mk?I{%q+?cv̎eKI%Lc2bu͞?/:,y)fO pH 1 d92`/,M>;]!6`yg|<(ς%D O>w?^h*Iu~F& M>Y">a|Ƒ&/{Ri9v')$pY)Ȉ\z09 Q`8$vϥkMF 8răDaU^oz`ꔃ!B5-3qǚA;,ja~qًO=աS}+rѧ9aϽvF1')z`cgᲨem\WO8*KaZ?J[/2k_` fqSh8Xb>wQ3vf*|%4'x- ²/qXF(d}C 㴮{t5yYfy!!@ BXSZJOqy*Rzdy 'ZФ9矌*Wfف, o"VQL1o/5GO]LYf4YZ;>c{nYN5tN(DSCMֱ-@h6j1, l+} 7 eZqdޚnc߮'(D`AK|uT.spA%=>@` B&菅j?hh:&:99qH4B!i.[I8^\$EcOEt[L1cWr=/W~8^v 8_8ݪ #i _LrewoVT,ȢӰ ؂X [-j}2h"pn`q;?9F1Ul_͕Fn=5M~[7)R]?د9V3`JiCsPKӖU=Fc g5%رMðC6Po-3h_{CEؙumt>I% ӷ|T,n\GdؼCe7o}ԋa ZD)E ϊ}N 3/aajAJd{x!H20m b_e2siH2Wrp dƵ˾vr[FU C.4U89s̾8qi>t싏qliSQAs|& ˨*\KrBN񏦜=oJĽc\[Ig-\9h˛e[A/wjV0 ; %Y`Q^{A 97ሓq/t|ōcvw ޛ8UPޚfz^h;ݸz_$}ՇQ\q(k8IX=Eya~;6<}o haOoudA]ITߍ{9=*Ϥ:QF ~a-X, ], IlƓΐmy rL<{T w rmS\25@;4gf9㑏`])VЂҳ7P&̇)د6 * XL yƆۯ{-_xx6ߦlzI1kPCV3_ BY$gWz{?uXnIS~9m|؞%X%Im(PaLj"y?="G격/uTzb+M ;22, `D{a/oHYkHg`9A[C{`6kv{U^ch,ֽO}ZA:&7HS;k,55#M}?bH:tF }\:.5Q >i]3%hmk nɵ@L4iq[4g$0[6YCy63m<{A  c_%XT^_R$t]Շ'(rDZwm45.1Ҥ x/.s[^:qV~f]9]'Q2Qn6)p$0-9OH _aNgRawZ >Х|V<*4|D|wv@jz5/fMkQ{j+4C%h9g-|_U qC. !5^jFc^ a;ѵ/ةQI$5@ʘ"Ùq=\`?ϗ;0oʪOCAuѴ]t\Ε=vIgظsg%0tDkGZ)W'Zp1ޚ]Jd1h<?GQE/yU%1d4 inMrLDCMqceirI?i\^4cEOn,r-@8XѣA+gJ| 9 MXc 10ԑ0_Viu`#-^c猊mScЪ0ɰtnheWXr7F{EP_B,}:W3yѶYyݾ ٯhtwCI1 dUp´kZЦt<}58KHYiL'@eO(Nyo(A|{я6Z(%j{ĭ"!/A: &Uz<sgϝQ,xTN4)rpMҿ U DvL?{vbl=V`ƌiYs/ue`'*gۢ>S}1ˍ`0uĥIiт9Yv%TҎj$ɢ6|؜'KD;Y+6S nU~m qF&n Ɔ5AFA/*1E΂ " E"F^݁q%mA~_9B \GRORxc]rbʽ~kgN~og- ~IL=צTi&K)rS +;FEIJo){c645 ,?A)J:`V1pQZױv_wJM?rOQa&|u߂7e95aq#Ey ,A1S70X0YPyTMGzshyj<}qˌr?2IHZN*^? `N}B<jH#HՙQ̉!h7!U+,0& ~ oecP)T~jK6t1,Dv\n`%o_ Bh fHSNwƾ~WUSSiuJӛn`pߠD lUW:9n=1  PNYm&}3AÈVI]\wZqj=>Нܑ]]J? <S=VBrW#,GhZ7NR@L8?N=R>Ȼ91|Mej{{$QfӪ( %Uy](k9K@ψTJ6U# x`Ⱥ 1 n {5!`$O3Sfe*)9o!OQSDHܜO>7[l5c+%Gȧ|>c ݼ@Nm)\*dv R44ɢ@\jBZkJzRjwtu-|C:MF(00r^B|HqKM {FFMӋ͆;ҿPїц [uLЅZSèZV$K|8' y| _|h 4VIaPa/s: !P$SM AՅȏqw4jn+!p+Zf,hX[zAt侂! b> h[ f6xZnS6U l"o5Aw) UzUąI%56Ɖ]CX>zmyB|.C'rp df֍EЬ$uN7$(#ZgT%Խl9* SZy]))i~46ƣb5O>Ժ0fXRQ_cr߆T}R88W[e IZ\ˌ 2pU bYZ7yZ6HUTEbkHssqQId_u~6Om3H+{sh آ)S#CNBUUZ7I>/f d&2n/F?QHu@]uc?sfA:RMYHBQixڡ$=A<~d; LKSLE0h`N()|zcxϏ0vU1a) )xlÿ=gAWW\*mLjyI*NF}1r`^FW8O Co3D.aǺ)3OB G+BJM]g_ʏKΩBLE%D_k:1qJܙ5К T ! &sݝPWlzku?ϓ*Q_))!:bꬴ!P_xA{}^h/=4H|![֟6&"Xռ ^:@eˢvGL" Ȭ,}Yͺ2'6zagBMaʕ|MQ;Kv/z&,1 >`Ĵ{Z*Ϫǂf,ig?fʇG\Έ:PX xX+qͻvi S^-.y3IDz;'p 2Zd0T4mzW}i<;/%5vT ᐱe?~!4»8OAuy3̀?=2KWPC4B҃ Wˠq(vG(DWvi傧A2MUlAA{/z> _;L>N: :sB00K-+R$fJJ˼ߣza#8A`c= y8__c7N)ec eBVB*R O&I~W PXTca:rv{ }5}:}Η㶊 F (c*f`bNU:Zy@7B!yu IEW1,X7DIAS4O ))F>6>#|B:5)MW{37z7lvWvPC943<G9y2, Oy-1I |`zTLXO#=^xCVPIQHSgRdILGo A#J:4c^u5}k6ph憎rYW/, |u\]\C.MWzcHxG} %_;e] H $I,aҹ1 4TEOFiu9 N7k/df83Eg6(lXɢFL2cӌK"_CA} QPB⒖[xQVsV z_#"V=D åq QѬQcGPFeƎ >r^:3h1L᷂0 Y%4?ZDt-*L52.`vaG0H ){;!yfQ tEXFH X g.ϟT^ 1U.-M\ W"Q$0W0[`1H^g-0 OJDÆ*/u* aqPI״dc@vN ^VࢲvMMvv/ǠҨHg*./}vbLgF۸%2 RH=oҸNy+yϵq01u Q.qWjۇƕk壆 s'Pf]̎167Egc Zvl*2>jpsZѿni[ >BǨs~a Z' Nհ41W$u(er$60c`WGwvh'O[0asȌ]1 "F1,emÐt9AtK2h^+tT) i29~7vw~}qa>nS\x=ʷ3x>ÃV;!eƒyxx#`5Q+ mlm۬aQylV]k!M/CLs`֞y1Ğ)Zx|x"VlOG(`4fhg7A#L^U*STmr~(P?*ul.WPdC~#Anϴ* ceNHt_߽ bC4 T"Fu󞛻4m|ڰot=vLE WF6P ;  cO޺0ӫcf5m'iI+C_Mcj0grR Qļryx@!q5*A\^OKږi"? D0uΛQu)ŀ8ul>eHcBl1fKbd0U{*roaavYB՟)C8 ToU8]|E]bP_ Nl^70?iE&ػ}T7vT M@^}efWPUU؛&|M99Ͽ85%C]F8z#s/f꺸s OЏH7o! 1^q~sO/{ިm8"Xe7 QV&mnFfTY 9mA_4e1*#-'t;H|gTzQwExU-9cA6B[BPU:yT&҆"ZIGatmL΂!X$SYR遉*(LJ(%Ny5eM-;hȘg~N3CлqU]eSAP* I^ xLi ]T3j7;h(wnZ (# yR?kڊe;Mdts>}i/:SMIr8m15ڬ* c@TB̳']:Jq4A!^㎀ 4Z}X8 ҚRߴz{MzV 0x#C^r?HnϤmG?^1P]݃`,u`De)}bAR)7+~}.5k ]>I_P(λ  d3b‘ +YYp7K6_Z J+sSWx$, {K0?Maax,"nXiQ98.c"CJur>u+H\̅4K妒#)ę5w̥֜L/ٜM+,c70Gb Dm3K/+8WS70,=WuS4Փc TOO{\mU2Lbr "45xĩ :|q;$|i:M#}g.?qu6xTxBL-BJ 3PQFa%e)Te4[7ou4v\F3G=)1c%aDPq0ʟin0K]Z^&r8;8 ]/(ioWL[v<`BR%jMW>>)S0}f :FMPR8SJ<#q!]VFzT&-k} (I_W 2X 7g}CRG7{y%+ 5 ƱF:r7b̨`Ęٯ{a1ڜ'#KxEOgy=Wf6{v\ȪTEw|yWM,Q;mx$Hj|it5ja).jQE)ASAM`QX/-/#9ex˓GVQIq cM2! ›Lô!-  ښ` l8 VMh0}T[kk[$[IEj_ w3n֮i/iߒ'|