Skip to content

Commit

Permalink
tests for terminal exceptions
Browse files Browse the repository at this point in the history
exception for when no endpoints are found
  • Loading branch information
sh-rp committed May 23, 2024
1 parent f83d22b commit 54e0653
Show file tree
Hide file tree
Showing 10 changed files with 277 additions and 7 deletions.
2 changes: 0 additions & 2 deletions dlt_init_openapi/cli/cli_endpoint_selection.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ def questionary_endpoint_selection(endpoints: EndpointCollection) -> Set[str]:
("italic", f" {endpoint.path}"),
]
choices.append(questionary.Choice(text, endpoint))
if not choices:
raise ValueError("No endpoints found")
selected_endpoints: List[Endpoint] = questionary.checkbox(
"Which resources would you like to generate? Press enter to continue, "
+ "if you do not select any resources, all of them will be rendered.",
Expand Down
11 changes: 11 additions & 0 deletions dlt_init_openapi/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
from typing import List


class DltOpenAPIException(Exception):
pass

Expand Down Expand Up @@ -36,3 +39,11 @@ class DltUnparseableSpecException(DltOpenAPITerminalException):
def __init__(self) -> None:

super().__init__("Could not parse selected spec, please provide a valid YAML or JSON document.")


class DltNoEndpointsDiscovered(DltOpenAPITerminalException):
def __init__(self, enabled_methods: List[str]):
super().__init__(
f"Did not find any endpoint with http methods {enabled_methods} in provided OpenAPI spec. "
+ "Please check your spec if endpoints with these methods exist or add additional methods in your config."
)
12 changes: 10 additions & 2 deletions dlt_init_openapi/parser/openapi_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@
from loguru import logger
from yaml import BaseLoader

from dlt_init_openapi.exceptions import DltInvalidSpecException, DltOpenAPINot30Exception, DltUnparseableSpecException
from dlt_init_openapi.exceptions import (
DltInvalidSpecException,
DltNoEndpointsDiscovered,
DltOpenAPINot30Exception,
DltUnparseableSpecException,
)
from dlt_init_openapi.parser.config import Config
from dlt_init_openapi.parser.context import OpenapiContext
from dlt_init_openapi.parser.endpoints import EndpointCollection
Expand All @@ -34,7 +39,7 @@ def parse(self, data: bytes) -> None:

self.spec_raw = self._load_yaml_or_json(data)
self.security_schemes = {}

print(self.spec_raw)
logger.info("Validating spec structure")
try:
spec = osp.OpenAPI.parse_obj(self.spec_raw)
Expand Down Expand Up @@ -73,6 +78,9 @@ def parse(self, data: bytes) -> None:
self.endpoints = EndpointCollection.from_context(self.context)
logger.success(f"Completed parsing endpoints. {len(self.endpoints.endpoints)} endpoints found.")

if len(self.endpoints.endpoints) == 0:
raise DltNoEndpointsDiscovered(self.config.include_methods)

def _load_yaml_or_json(self, data: bytes) -> Dict[str, Any]:
logger.info("Trying to parse spec as JSON")
try:
Expand Down
7 changes: 6 additions & 1 deletion tests/cases/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@

PREFIX = "./tests/cases"

CASE_FOLDERS = {"original": "original_specs", "artificial": "artificial_specs", "extracted": "extracted_specs"}
CASE_FOLDERS = {
"original": "original_specs",
"artificial": "artificial_specs",
"extracted": "extracted_specs",
"error": "error",
}


def case_path(type: str, case: str) -> str:
Expand Down
224 changes: 224 additions & 0 deletions tests/cases/error/coinpaprika.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
openapi: "3.0.0"

info:
version: 1.7.8
title: Coinpaprika API
x-logo:
url: "https://coinpaprika.com/static/files/df51e301.png#greywizard/rock-coin-web/assets/img/cp_logo-transparent.png"
backgroundColor: "#FAFAFA"
description: |
Coinpaprika API delivers precise & frequently updated market data from the world of crypto: coin prices, volumes, market caps, ATHs, return rates and more.
# Introduction
If you want to use the Coinpaprika API, you have two main options: you can choose the API Free plan, which has sufficient limits for hobby and non-commercial use, or get one of the paid plans, ideal for commercial or professional use. To decide which plan is the best for you, check the [Plans and Pricing comparison](https://coinpaprika.com/api).
Depending on the selected plan, you should send requests to the appropriate base URL:
| Plan | Base URL |
|------------|-------------------------------------|
| Free | https://api.coinpaprika.com/v1/ |
| Starter | https://api-pro.coinpaprika.com/v1/ |
| Pro | https://api-pro.coinpaprika.com/v1/ |
| Business | https://api-pro.coinpaprika.com/v1/ |
| Enterprise | https://api-pro.coinpaprika.com/v1/ |
# Authentication
If you use the Free plan, you don't need to set up an API key for each request. For other plans it is required. You can generate the API key in the Developer Portal after signing in.
To provide the API key in REST API calls, set the `Authorization` header:
```
Authorization: <api-key>
```
# Standards and conventions
## General
* All endpoints return either a JSON object or array
* All timestamp related fields are in seconds
## Errors
* API errors are formatted as JSON:
```{"error": "<error message>"}```
* The API uses standard HTTP status codes to indicate a request failure:
* HTTP 4XX return codes are used for invalid requests - the issue is on the sender's side
* HTTP 5XX return codes are used for internal errors - the issue is on the server's side
| HTTP Status | Description |
|---|---|
| 400 Bad Request | The server could not process the request due to invalid request parameters or invalid format of the parameters. |
| 402 Payment Required | The request could not be processed because of the user has an insufficient plan. If you want to be able to process this request, get a [higher plan](https://coinpaprika.com/api). |
| 403 Forbidden | The request could not be processed due to invalid API key. |
| 404 Not Found | The server could not process the request due to invalid URL or invalid path parameter. |
| 429 Too Many Requests | The rate limit has been exceeded. Reduce the frequency of requests to avoid this error. |
| 500 Internal Server Error | An unexpected server error has occured. |
# Rate limit
* The monthly number of requests is limited depending on the plan:
| Plan | Calls/month |
|------------|-------------------------------------|
| Free | 20 000 |
| Starter | 200 000 |
| Pro | 500 000 |
| Business | 3 000 000 |
| Enterprise | No limits |
# API Clients
We provide the API clients in several popular programming languages:
* [PHP](https://github.com/coinpaprika/coinpaprika-api-php-client)
* [NodeJS](https://github.com/coinpaprika/coinpaprika-api-nodejs-client)
* [GO](https://github.com/coinpaprika/coinpaprika-api-go-client)
* [Swift](https://github.com/coinpaprika/coinpaprika-api-swift-client)
* [Kotlin](https://github.com/coinpaprika/coinpaprika-api-kotlin-client)
* [Python](https://github.com/coinpaprika/coinpaprika-api-python-client)
* [Google Sheets](https://github.com/coinpaprika/coinpaprika-google-sheet)
* Community Contributed Clients:
* [Rust](https://github.com/tokenomia-pro/coinpaprika-api-rust-client) built by <a href="https://tokenomia.pro/" target="_blank">tokenomia.pro</a>
* [C#](https://github.com/MSiccDev/CoinpaprikaAPI)
* [JS](https://github.com/jaggedsoft/coinpaprika-js)
**Note**: some of them may not be updated yet. We are working on improving them and they will be updated soon. If you'd like to contribute, please report issues and send PRs on Github.
# Terms of use
* [Download terms of use](https://coinpaprika.github.io/files/terms_of_use_v1.pdf)
# Archival documentations
* [API v1.2](https://api.coinpaprika.com/docs/1.2)
* [API v1.3](https://api.coinpaprika.com/docs/1.3)
* [API v1.4](https://api.coinpaprika.com/docs/1.4)
* [API v1.5](https://api.coinpaprika.com/docs/1.5)
* [API v1.6](https://api.coinpaprika.com/docs/1.6)
# Version history
## 1.7.8 - 2024.01.24
* Plan limits update
## 1.7.7 - 2023.06.07
* Added official Python client link
## v1.7.6 - 2023.04.12
* New intervals for OHLCV endpoint
## v1.7.5 - 2022.12.07
* Removed documentation for /beta/ endpoints
## v1.7.4 - 2022.09.19
* Key info endpoint
* Coin logo image URL
## v1.7.3 - 2022.09.08
* Plans update
## v1.7.2 - 2022.07.22
* Changelog endpoint documentation
## v1.7.1 - 2022.07.14
* Beta endpoints documentation
## v1.7.0 - 2022.05.06
* API-Pro documentation
## v1.6.1 - 2020.12.09
* Added information about first date with price data for currency ticker [/tickers](#operation/getTickers) and [/tickers/{coin_id}](#operation/getTickersById)
* Added redirect for historical tickers by contract address [/contracts/{platform_id}/{contract_address}/historical](#operation/getHistoricalTicker)
## v1.6.0 - 2020.10.27
* Added contracts section [/contracts](#operation/getPlatforms), [/contracts/{platform_id}](#operation/getContracts),
[/contracts/{platform_id}/{contract_address}](#operation/getTicker)
servers:
- url: https://api.coinpaprika.com/v1

tags:
- name: "Key"
- name: "Global"
- name: "Coins"
- name: "People"
- name: "Tags"
- name: "Tickers"
- name: "Exchanges"
- name: "Tools"
- name: "Contracts"
- name: "Changelog"
- name: "Deprecated"

paths:
# Global
/key/info:
$ref: "paths/key.yml#/info"

# Global
/global:
$ref: "paths/global.yml#/global"

# Coins
/coins:
$ref: "paths/coins.yml#/coins"

/coins/{coin_id}:
$ref: "paths/coins.yml#/coin_by_id"

/coins/{coin_id}/twitter:
$ref: "paths/coins.yml#/twitter"

/coins/{coin_id}/events:
$ref: "paths/coins.yml#/events"

/coins/{coin_id}/exchanges:
$ref: "paths/coins.yml#/exchanges_by_coin_id"

/coins/{coin_id}/markets:
$ref: "paths/coins.yml#/markets_by_coin_id"

/coins/{coin_id}/ohlcv/latest/:
$ref: "paths/coins.yml#/coins_ohlcv_latest"

/coins/{coin_id}/ohlcv/historical:
$ref: "paths/coins.yml#/coins_ohlcv_historical"

/coins/{coin_id}/ohlcv/today/:
$ref: "paths/coins.yml#/coins_ohlcv_today"

# People
/people/{person_id}:
$ref: "paths/people.yml#/person_by_id"

# Tags
/tags:
$ref: "paths/tags.yml#/tags"
/tags/{tag_id}:
$ref: "paths/tags.yml#/tag_by_id"

# Tickers
/tickers:
$ref: "paths/tickers.yml#/tickers"

/tickers/{coin_id}:
$ref: "paths/tickers.yml#/tickers_by_id"

/tickers/{coin_id}/historical:
$ref: "paths/tickers.yml#/tickers_historical"

# Exchanges
/exchanges:
$ref: "paths/exchanges.yml#/exchanges"
/exchanges/{exchange_id}:
$ref: "paths/exchanges.yml#/exchange_by_id"
/exchanges/{exchange_id}/markets:
$ref: "paths/exchanges.yml#/markets_by_exchange_id"

# Contracts
/contracts:
$ref: "paths/contracts.yml#/platforms"
/contracts/{platform_id}:
$ref: "paths/contracts.yml#/contracts_by_platform"
/contracts/{platform_id}/{contract_address}:
$ref: "paths/contracts.yml#/ticker_redirect"
/contracts/{platform_id}/{contract_address}/historical:
$ref: "paths/contracts.yml#/ticker_historical_redirect"

# Changelog
/changelog/ids:
$ref: "paths/changelog.yml#/ids"

# Tools
/search:
$ref: "paths/tools.yml#/search"
/price-converter:
$ref: "paths/tools.yml#/price_converter"

# Deprecated
/ticker:
$ref: "paths/deprecated.yml#/ticker_deprecated"

/ticker/{coin_id}:
$ref: "paths/deprecated.yml#/ticker_by_coin_id_deprecated"
4 changes: 4 additions & 0 deletions tests/cases/error/malformed_yaml.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
def test_all_specs(case: str) -> None:
for skipped in SKIP_CASES:
if skipped in case:
return
3 changes: 2 additions & 1 deletion tests/integration/test_all_specs.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@

from tests.integration.utils import get_all_spec_paths, get_source

SKIP_CASES = ["zoom_with_pagination", "art_institute_chicago_api"] # broken spec # swagger 2.0 spec
# do not test specs in error folder
SKIP_CASES = ["zoom_with_pagination", "/error/"]


@pytest.mark.slow
Expand Down
19 changes: 19 additions & 0 deletions tests/integration/test_error_specs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import pytest

from dlt_init_openapi.exceptions import DltNoEndpointsDiscovered, DltOpenAPINot30Exception, DltUnparseableSpecException
from tests.integration.utils import get_dict_by_case


def test_no_endoint_discovered() -> None:
with pytest.raises(DltNoEndpointsDiscovered):
get_dict_by_case("error", "coinpaprika.yaml")


def test_not_openapi_30() -> None:
with pytest.raises(DltOpenAPINot30Exception):
get_dict_by_case("error", "art_institute_chicago_api.yaml")


def test_not_parseable() -> None:
with pytest.raises(DltUnparseableSpecException):
get_dict_by_case("error", "malformed_yaml.yaml")
2 changes: 1 addition & 1 deletion tests/integration/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

LOCAL_DIR = "tests/_local/"

TType = Literal["artificial", "original", "extracted"]
TType = Literal["artificial", "original", "extracted", "error"]


def get_detected_project_from_open_api(case: str, config: Config) -> Project:
Expand Down

0 comments on commit 54e0653

Please sign in to comment.