diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..57e2cae --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,47 @@ +name: Build and Test + +on: [push] + +jobs: + format-code: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Install dependencies and format code + run: | + sudo apt-get install -y make + pip3 install -r lint_requirements.txt + make format + + if git diff --quiet; then + echo "No code formatting changes detected." + else + echo "Code formatting changes detected. Please run 'make format' locally and commit the changes." + git diff --exit-code + exit 1 + fi + + run-tests: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Set up Python environment + uses: actions/setup-python@v3 + with: + python-version: 3.9 + + - name: Install dependencies and run tests + run: | + python -m venv venv + source venv/bin/activate + pip3 install --upgrade pip + pip3 install -r requirements.txt + pip3 install -r test_requirements.txt + + python -m unittest discover -v \ No newline at end of file diff --git a/.github/workflows/issues.yml b/.github/workflows/issues.yml new file mode 100644 index 0000000..fe5f4e8 --- /dev/null +++ b/.github/workflows/issues.yml @@ -0,0 +1,17 @@ +on: + issues: + types: [opened] + +jobs: + comment: + runs-on: ubuntu-latest + steps: + - uses: actions/github-script@v7 + with: + script: | + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: 'Thank you for reporting! If this is an SDK specific issue, we will look into it and get back to you soon. If this is an API related request, report it in our [Advanced API forum](https://forums.coinbasecloud.dev/c/advanced-trade-api/20) instead.' + }) \ No newline at end of file diff --git a/.github/workflows/notification.yml b/.github/workflows/notification.yml new file mode 100644 index 0000000..f3ac552 --- /dev/null +++ b/.github/workflows/notification.yml @@ -0,0 +1,61 @@ +name: Slack notifications for PRs and Issues + +on: + issues: + types: [opened, reopened] + +env: + CHANNEL_API_FEEDBACK: ${{ vars.CHANNEL_API_FEEDBACK }} + ON_CALL_API: ${{ vars.ON_CALL_API }} + +jobs: + checks: + runs-on: ubuntu-latest + outputs: + skip: ${{ env.skip }} + steps: + - name: Check spam labels + if: ${{ contains(github.event.*.labels.*.name, 'spam') }} + run: | + echo "skip=true" >> $GITHUB_ENV + echo "::error:: Spam label found." + + notify: + runs-on: ubuntu-latest + needs: checks + if: ${{ needs.checks.outputs.skip != 'true' }} + steps: + - name: Set channel and mention + run: | + echo "channel=${{ env.CHANNEL_API_FEEDBACK }}" >> $GITHUB_ENV + echo "mention=${{ env.ON_CALL_API }}" >> $GITHUB_ENV + + - name: Set text + run: | + text=$(echo "${{ vars.SLACK_TEMPLATE }}") + text=${text//'{{event}}'/Issue ${{ env.action }}} + text=${text//'{{author}}'/${{ env.author }}} + text=${text//'{{url}}'/${{ env.url }}} + text=${text//'{{mention}}'/${{ env.mention }}} + text=${text//'{{repo}}'/${{ github.repository }}} + text="${text//$'\r\n'/'\n'}" + text="${text//$'\n'/'\n'}" + echo "text=$text" >> $GITHUB_ENV + env: + action: ${{ github.event.action }} + author: ${{ github.event.issue.user.login }} + url: ${{ github.event.issue.html_url }} + + - name: Notify Slack + uses: slackapi/slack-github-action@v1.24.0 + with: + payload: | + { + "channel": "#${{ env.channel }}", + "username": "${{ vars.WEBHOOK_USERNAME }}", + "text": "*${{ env.title }}*\n${{ env.text }}", + "icon_emoji": ":${{ vars.ICON_EMOJI }}:" + } + env: + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} + title: ${{ github.event.issue.title }} \ No newline at end of file diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml new file mode 100644 index 0000000..fa3a432 --- /dev/null +++ b/.github/workflows/pages.yml @@ -0,0 +1,50 @@ +# Simple workflow for deploying static content to GitHub Pages +name: Github Pages + +on: + # Runs on pushes targeting the default branch + push: + branches: ["master"] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + build: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Install Dependencies + run: | + pip3 install -r pinned_requirements.txt + pip3 install -r docs_requirements.txt + - name: Build HTML + run: | + cd docs + make html + - name: Setup Pages + uses: actions/configure-pages@v3 + - name: Upload artifact + uses: actions/upload-pages-artifact@v1 + with: + # Upload the build output directory + path: 'docs/_build/html' + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v2 diff --git a/coinbase/rest/common.py b/coinbase/rest/common.py new file mode 100644 index 0000000..ed21a1b --- /dev/null +++ b/coinbase/rest/common.py @@ -0,0 +1,25 @@ +from typing import Any, Dict + +from coinbase.constants import API_PREFIX + + +def get_unix_time(self, **kwargs) -> Dict[str, Any]: + """ + **Get Server Time** + _________________ + [GET] https://api.coinbase.com/api/v3/brokerage/time + + __________ + + **Description:** + + Get the current time from the Coinbase Advanced API. This is a public endpoint. + + __________ + + **Read more on the official documentation:** `Get Server Time `_ + """ + + endpoint = f"{API_PREFIX}/time" + + return self.get(endpoint, public=True, **kwargs) diff --git a/tests/rest/test_common.py b/tests/rest/test_common.py new file mode 100644 index 0000000..f33b6c9 --- /dev/null +++ b/tests/rest/test_common.py @@ -0,0 +1,29 @@ +import unittest + +from requests_mock import Mocker + +from coinbase.rest import RESTClient + +from ..constants import TEST_API_KEY, TEST_API_SECRET + + +class TimeTest(unittest.TestCase): + def test_get_time(self): + client = RESTClient(TEST_API_KEY, TEST_API_SECRET) + + expected_response = {"iso": "2022-01-01T00:00:00Z", "epoch": 1640995200} + + with Mocker() as m: + m.request( + "GET", + "https://api.coinbase.com/api/v3/brokerage/time", + json=expected_response, + ) + time = client.get_unix_time() + + captured_request = m.request_history[0] + captured_headers = captured_request.headers + + self.assertEqual(captured_request.query, "") + self.assertEqual(time, expected_response) + self.assertNotIn("Authorization", captured_headers)