def test_validates(): api1 = NinjaAPI() try: os.environ["NINJA_SKIP_REGISTRY"] = "" with pytest.raises(ConfigError): api2 = NinjaAPI() finally: os.environ["NINJA_SKIP_REGISTRY"] = "yes"
def test_examples(): api = NinjaAPI() with patch("builtins.api", api, create=True): import docs.src.tutorial.path.code01 # noqa: F401 client = TestClient(api) response = client.get("/items/123") assert response.json() == {"item_id": "123"} api = NinjaAPI() with patch("builtins.api", api, create=True): import docs.src.tutorial.path.code02 # noqa: F401 import docs.src.tutorial.path.code010 # noqa: F401 client = TestClient(api) response = client.get("/items/123") assert response.json() == {"item_id": 123} response = client.get("/events/2020/1/1") assert response.json() == {"date": "2020-01-01"} schema = api.get_openapi_schema("") events_params = schema["paths"]["/events/{year}/{month}/{day}"]["get"][ "parameters"] # print(events_params, "!!") assert events_params == [ { "in": "path", "name": "year", "required": True, "schema": { "title": "Year", "type": "integer" }, }, { "in": "path", "name": "month", "required": True, "schema": { "title": "Month", "type": "integer" }, }, { "in": "path", "name": "day", "required": True, "schema": { "title": "Day", "type": "integer" }, }, ]
def test_get_openapi_urls(): api = NinjaAPI(openapi_url=None) paths = get_openapi_urls(api) assert len(paths) == 0 api = NinjaAPI(docs_url=None) paths = get_openapi_urls(api) assert len(paths) == 1 api = NinjaAPI(openapi_url="/path", docs_url="/path") with pytest.raises( AssertionError, match="Please use different urls for openapi_url and docs_url"): get_openapi_urls(api)
def __init__(self, router_or_app): if isinstance(router_or_app, NinjaAPI): self.urls = router_or_app.urls[0] else: api = NinjaAPI() router_or_app.set_api_instance(api) self.urls = list(router_or_app.urls_paths(""))
def test_manytomany(): class SomeRelated(models.Model): f = models.CharField() class Meta: app_label = "tests" class ModelWithM2M(models.Model): m2m = models.ManyToManyField(SomeRelated, blank=True) class Meta: app_label = "tests" WithM2MSchema = create_schema(ModelWithM2M, exclude=["id"]) api = NinjaAPI() @api.post("/bar") def post_with_m2m(request, payload: WithM2MSchema): return payload.dict() client = TestClient(api) response = client.post("/bar", json={"m2m": [1, 2]}) assert response.status_code == 200, str(response.json()) assert response.json() == {"m2m": [1, 2]} response = client.post("/bar", json={"m2m": []}) assert response.status_code == 200, str(response.json()) assert response.json() == {"m2m": []}
def test_examples(): api = NinjaAPI() with patch("builtins.api", api, create=True): import docs.src.tutorial.path.code010 client = NinjaClient(api) response = client.get("/events/2020/1/1") assert response.json() == {"date": "2020-01-01"} schema = api.get_openapi_schema("") events_params = schema["paths"]["/events/{year}/{month}/{day}"]["get"][ "parameters"] assert events_params == [ { "in": "path", "name": "year", "required": True }, { "in": "path", "name": "month", "required": True }, { "in": "path", "name": "day", "required": True }, ]
def test_duplicate_schema_names(): from django.db import models from ninja import NinjaAPI, Schema from ninja.orm import create_schema class TestModelDuplicate(models.Model): field1 = models.CharField() field2 = models.CharField() class Meta: app_label = "tests" class TestSchema(Schema): data1: create_schema(TestModelDuplicate, fields=["field1"]) # noqa: F821 data2: create_schema(TestModelDuplicate, fields=["field2"]) # noqa: F821 api = NinjaAPI() @api.get("/test", response=TestSchema) def a_test_method(request): return [] match = r"Looks like you may have created multiple orm schemas with the same name:" with pytest.raises(ConfigError, match=match): assert api.get_openapi_schema()
def test_examples(): api = NinjaAPI() with patch("builtins.api", api, create=True): import docs.src.tutorial.form.code01 # noqa: F401 import docs.src.tutorial.form.code02 # noqa: F401 client = TestClient(api) assert client.post("/items", data={ "name": "Katana", "price": 299.00, "quantity": 10 }).json() == { "name": "Katana", "description": None, "price": 299.0, "quantity": 10, } assert client.post("/items/1?q=test", data={ "name": "Katana", "price": 299.00, "quantity": 10 }).json() == { "item_id": 1, "q": "test", "item": { "name": "Katana", "description": None, "price": 299.0, "quantity": 10, }, } with patch("builtins.api", api, create=True): import docs.src.tutorial.form.code03 # noqa: F401 client = TestClient(api) assert client.post( "/items-blank-default", data={ "name": "Katana", "price": "", "quantity": "", "in_stock": "" }, ).json() == { "name": "Katana", "description": None, "in_stock": True, "price": 0.0, "quantity": 0, }
async def test_asyncio_exceptions(): api = NinjaAPI() @api.get("/error") async def thrower(request): raise Http404("test") client = NinjaAsyncClient(api) response = await client.get("/error") assert response.status_code == 404
def urls(self) -> List: if not hasattr(self, "_urls_cache"): self._urls_cache: List if isinstance(self.router_or_app, NinjaAPI): self._urls_cache = self.router_or_app.urls[0] else: api = NinjaAPI() self.router_or_app.set_api_instance(api) self._urls_cache = list(self.router_or_app.urls_paths("")) return self._urls_cache
def test_no_handlers(): api = NinjaAPI() api._exception_handlers = {} @api.get("/error") def thrower(request): raise RuntimeError("test") client = NinjaClient(api) with pytest.raises(RuntimeError): client.get("/error")
def test_examples(): api = NinjaAPI() with patch("builtins.api", api, create=True): import docs.src.tutorial.body.code01 import docs.src.tutorial.body.code02 import docs.src.tutorial.body.code03 client = NinjaClient(api) assert client.post("/items", json={ "name": "Katana", "price": 299.00, "quantity": 10 }).json() == { "name": "Katana", "description": None, "price": 299.0, "quantity": 10, } assert client.put("/items/1", json={ "name": "Katana", "price": 299.00, "quantity": 10 }).json() == { "item_id": 1, "item": { "name": "Katana", "description": None, "price": 299.0, "quantity": 10, }, } assert client.post("/items/1?q=test", json={ "name": "Katana", "price": 299.00, "quantity": 10 }).json() == { "item_id": 1, "q": "test", "item": { "name": "Katana", "description": None, "price": 299.0, "quantity": 10, }, }
def test_docs_decorator(): api = NinjaAPI(docs_decorator=staff_member_required) paths = get_openapi_urls(api) assert len(paths) == 2 for ptrn in paths: request = Mock(user=Mock(is_staff=True)) result = ptrn.callback(request) assert result.status_code == 200 request = Mock(user=Mock(is_staff=False)) request.build_absolute_uri = lambda: "http://example.com" result = ptrn.callback(request) assert result.status_code == 302
def test_unique_operation_ids(): api = NinjaAPI() @api.get("/1") def same_name(request): pass @api.get("/2") def same_name(request): pass with pytest.warns(UserWarning): schema = api.get_openapi_schema()
def test_unique_operation_ids(): api = NinjaAPI() @api.get("/1") def same_name(request): pass @api.get("/2") # noqa: F811 def same_name(request): # noqa: F811 pass match = 'operation_id "test_openapi_schema_same_name" is already used' with pytest.warns(UserWarning, match=match): api.get_openapi_schema()
def test_raises_on_cookie_auth(): "It should raise if user picked Cookie based auth and csrf=False" class Auth(APIKeyCookie): def authenticate(self, request, key): return request.COOKIES[key] == "foo" api = NinjaAPI(auth=Auth(), csrf=False) @api.get("/some") def some_method(request): pass with pytest.raises(ConfigError): api._validate()
def test_reuse_router_error(): test_api = NinjaAPI() test_router = Router() test_api.add_router("/", test_router) # django debug server can attempt to import the urls twice when errors exist # verify we get the correct error reported match = "Router@'/another-path' has already been attached to API NinjaAPI:1.0.0" with pytest.raises(ConfigError, match=match): with mock.patch("ninja.main._imported_while_running_in_debug_server", False): test_api.add_router("/another-path", test_router) # The error should be ignored under debug server to allow other errors to be reported with mock.patch("ninja.main._imported_while_running_in_debug_server", True): test_api.add_router("/another-path", test_router)
def test_kwargs(): api = NinjaAPI() @api.get("/") def operation(request, a: str, *args, **kwargs): pass schema = api.get_openapi_schema() params = schema["paths"]["/api/"]["get"]["parameters"] print(params) assert params == [ # Only `a` should be here, not kwargs { "in": "query", "name": "a", "schema": { "title": "A", "type": "string" }, "required": True, } ]
async def test_asyncio_operations(): api = NinjaAPI() class KeyQuery(APIKeyQuery): def authenticate(self, request, key): if key == "secret": return key @api.get("/async", auth=KeyQuery()) async def async_view(request, payload: int): await asyncio.sleep(0) return {"async": True} @api.post("/async") def sync_post_to_async_view(request): return {"sync": True} client = NinjaAsyncClient(api) # Actual tests -------------------------------------------------- # without auth: res = await client.get("/async?payload=1") assert res.status_code == 401 # async successful res = await client.get("/async?payload=1&key=secret") assert res.json() == {"async": True} # async innvalid input res = await client.get("/async?payload=str&key=secret") assert res.status_code == 422 # async call to sync method for path that hahve async operations res = await client.post("/async") assert res.json() == {"sync": True} # invalid method res = await client.put("/async") assert res.status_code == 405
def test_raises_on_cookie_auth(): "It should raise if user picked Cookie based auth and csrf=False" class Auth(APIKeyCookie): def authenticate(self, request, key): return request.COOKIES[key] == "foo" api = NinjaAPI(auth=Auth(), csrf=False) @api.get("/some") def some_method(request): pass with pytest.raises(ConfigError): api._validate() try: import os os.environ["NINJA_SKIP_REGISTRY"] = "" # Check for wrong error reported match = "Looks like you created multiple NinjaAPIs" with pytest.raises(ConfigError, match=match): api.urls # django debug server can attempt to import the urls twice when errors exist # verify we get the correct error reported match = "Cookie Authentication must be used with CSRF" with pytest.raises(ConfigError, match=match): with mock.patch( "ninja.main._imported_while_running_in_debug_server", True): api.urls finally: os.environ["NINJA_SKIP_REGISTRY"] = "yes"
from client import NinjaClient class RoomEnum(str, Enum): double = "double" twin = "twin" single = "single" class Booking(BaseModel): start: date end: date room: RoomEnum = RoomEnum.double api = NinjaAPI() @api.post("/book") def create_booking(request, booking: Booking): return booking @api.get("/search") def booking_search(request, room: RoomEnum): return {"room": room} client = NinjaClient(api)
from ninja import NinjaAPI from django.conf import settings from django.views.decorators.csrf import csrf_exempt from client import NinjaClient csrf_OFF = NinjaAPI(urls_namespace="csrf_OFF") csrf_ON = NinjaAPI(urls_namespace="csrf_ON", csrf=True) @csrf_OFF.post("/post") def post_off(request): return {"success": True} @csrf_ON.post("/post") def post_on(request): return {"success": True} @csrf_ON.post("/post/csrf_exempt") @csrf_exempt def post_on_with_exempt(request): return {"success": True} TOKEN = "1bcdefghij2bcdefghij3bcdefghij4bcdefghij5bcdefghij6bcdefghijABCD" COOKIES = {settings.CSRF_COOKIE_NAME: TOKEN} def test_csrf_off(): client = NinjaClient(csrf_OFF)
from emp_main.models import ValueMessage as ValueHistoryDb from emp_main.models import LastValueMessage as ValueLatestDb from emp_main.models import ScheduleMessage as ScheduleHistoryDb from emp_main.models import LastScheduleMessage as ScheduleLatestDb from emp_main.models import SetpointMessage as SetpointHistoryDb from emp_main.models import LastSetpointMessage as SetpointLatestDb from emp_main.models import ForecastMessage as ForecastMessageDb from emp_main.models import Product as ProductDb from emp_main.models import ProductRun as ProductRunDb from emp_main.models import Plant as PlantDb logger = logging.getLogger(__name__) api = NinjaAPI( title="EMP API", version="v1", docs_url="/", ) class GenericAPIView: """ Some generic stuff that should be relevant for all API endpoints. Attributes: ----------- PydanticModel: esg.models._BaseModel instance The Model that should be used to parse the input of `update_*` and serialize the output of `list_*` operations. DBModel: esg.django_models.DjangoBaseModel instance. The django model to interact with the DB.
def test_examples(): from someapp.models import Client api = NinjaAPI(csrf=True) Client.objects.create(key="12345") with patch("builtins.api", api, create=True): import docs.src.tutorial.authentication.code002 import docs.src.tutorial.authentication.apikey01 import docs.src.tutorial.authentication.apikey02 import docs.src.tutorial.authentication.apikey03 import docs.src.tutorial.authentication.basic01 import docs.src.tutorial.authentication.bearer01 import docs.src.tutorial.authentication.code001 import docs.src.tutorial.authentication.schema01 import docs.src.tutorial.authentication.multiple01 client = NinjaClient(api) response = client.get("/ipwhiltelist", META={"REMOTE_ADDR": "127.0.0.1"}) assert response.status_code == 401 response = client.get("/ipwhiltelist", META={"REMOTE_ADDR": "8.8.8.8"}) assert response.status_code == 200 # Api key -------------------------------- response = client.get("/apikey") assert response.status_code == 401 response = client.get("/apikey?api_key=12345") assert response.status_code == 200 response = client.get("/headerkey") assert response.status_code == 401 response = client.get("/headerkey", headers={"X-API-Key": "supersecret"}) assert response.status_code == 200 response = client.get("/cookiekey") assert response.status_code == 401 response = client.get("/cookiekey", COOKIES={"key": "supersecret"}) assert response.status_code == 200 # Basic http -------------------------------- response = client.get("/basic") assert response.status_code == 401 response = client.get( "/basic", headers={"Authorization": "Basic YWRtaW46c2VjcmV0"}) assert response.status_code == 200 assert response.json() == {"httpuser": "******"} # Bearer http -------------------------------- response = client.get("/bearer") assert response.status_code == 401 response = client.get("/bearer", headers={"Authorization": "Bearer supersecret"}) assert response.status_code == 200 # Multiple ------------------------------------ assert client.get("/multiple").status_code == 401 assert client.get("/multiple?key=supersecret").status_code == 200 assert (client.get("/multiple", headers={ "key": "supersecret" }).status_code == 200)
from ninja import NinjaAPI from users.router import router as users_router from properties.router import router as properties_router from appointments.router import router as appointments_router from communications.router import router as communications_router from locations.router import router as locations_router from .security import UserAuth api = NinjaAPI(auth=UserAuth()) api.add_router("/users", users_router, tags=["users"]) # funciona igual con o sin slash api.add_router("/properties", properties_router, tags=["properties"]) api.add_router("/appointments", appointments_router, tags=["appointments"]) api.add_router("/communications", communications_router, tags=["communications"]) api.add_router("/locations", locations_router, tags=["locations"])
from ninja import NinjaAPI from server.middlewares.auth import JWTBearer import server.routes.example v1_route = NinjaAPI(auth=JWTBearer(), version="1.0.0") v1_route.add_router('example', example.router)
from django.http import HttpResponseServerError from django.shortcuts import render, get_object_or_404 # Create your views here. from typing import List, Dict from ninja import NinjaAPI, Schema from ninja.security import django_auth from users.models import User from .models import Entry from ninja.orm import create_schema # ninja_api = NinjaAPI(auth=django_auth, csrf=True) ninja_api = NinjaAPI(title="Entry API") EntryIn = create_schema(Entry, name='EntryIn', fields=['text', 'number']) EntryOut = create_schema(Entry, name='EntryOut') class Entries(Schema): # name: 'Entries' entries: List[EntryOut] columns: List[Dict] # Dict entry_form_fields: Dict UserOut = create_schema(User, name='UserOut',
from django.contrib import admin from django.urls import path from ninja import NinjaAPI from someapp.api import router api_v1 = NinjaAPI() api_v1.add_router("events", router) # TODO: check ^ for possible mistakes like `/events` `evetns/`` api_v2 = NinjaAPI(version="2.0.0") @api_v2.get("events") def newevents2(request): return "events are gone" api_v3 = NinjaAPI(version="3.0.0") @api_v3.get("events") def newevents3(request): return "events are gone 3" @api_v3.get("foobar") def foobar(request): return "foobar" urlpatterns = [
def authenticate(self, request, username, password): if username == "admin" and password == "secret": return username class BearerAuth(HttpBearer): def authenticate(self, request, token): if token == "bearertoken": return token def demo_operation(request): return {"auth": request.auth} api = NinjaAPI(csrf=True) @api.exception_handler(CustomException) def on_custom_error(request, exc): return api.create_response(request, {"custom": True}, status=401) for path, auth in [ ("django_auth", django_auth), ("callable", callable_auth), ("apikeyquery", KeyQuery()), ("apikeyheader", KeyHeader()), ("apikeycookie", KeyCookie()), ("basic", BasicAuth()), ("bearer", BearerAuth()),
def test_examples(): api = NinjaAPI() with patch("builtins.api", api, create=True): import docs.src.tutorial.query.code01 # noqa: F401 import docs.src.tutorial.query.code02 # noqa: F401 import docs.src.tutorial.query.code03 # noqa: F401 import docs.src.tutorial.query.code010 # noqa: F401 client = TestClient(api) # Defaults assert client.get("/weapons").json() == [ "Ninjato", "Shuriken", "Katana", "Kama", "Kunai", "Naginata", "Yari", ] assert client.get("/weapons?offset=0&limit=3").json() == [ "Ninjato", "Shuriken", "Katana", ] assert client.get("/weapons?offset=2&limit=2").json() == [ "Katana", "Kama", ] # Required/Optional assert client.get("/weapons/search?offset=1&q=k").json() == [ "Katana", "Kama", "Kunai", ] # Coversion # fmt: off assert client.get("/example?b=1").json() == [None, True, None, None] assert client.get("/example?b=True").json() == [None, True, None, None] assert client.get("/example?b=true").json() == [None, True, None, None] assert client.get("/example?b=on").json() == [None, True, None, None] assert client.get("/example?b=yes").json() == [None, True, None, None] assert client.get("/example?b=0").json() == [None, False, None, None] assert client.get("/example?b=no").json() == [None, False, None, None] assert client.get("/example?b=false").json() == [None, False, None, None] assert client.get("/example?d=1577836800").json() == [None, None, "2020-01-01", None] assert client.get("/example?d=2020-01-01").json() == [None, None, "2020-01-01", None] # fmt: on # Schema assert client.get("/filter").json() == { "filters": { "limit": 100, "offset": None, "query": None, "category__in": None, } } assert client.get("/filter?limit=10").json() == { "filters": { "limit": 10, "offset": None, "query": None, "category__in": None, } } assert client.get("/filter?offset=10").json() == { "filters": {"limit": 100, "offset": 10, "query": None, "category__in": None} } assert client.get("/filter?query=10").json() == { "filters": { "limit": 100, "offset": None, "query": "10", "category__in": None, } } assert client.get("/filter?categories=a&categories=b").json() == { "filters": { "limit": 100, "offset": None, "query": None, "category__in": ["a", "b"], } } schema = api.get_openapi_schema("") params = schema["paths"]["/filter"]["get"]["parameters"] assert params == [ { "in": "query", "name": "limit", "required": False, "schema": {"title": "Limit", "default": 100, "type": "integer"}, }, { "in": "query", "name": "offset", "required": False, "schema": {"title": "Offset", "type": "integer"}, }, { "in": "query", "name": "query", "required": False, "schema": {"title": "Query", "type": "string"}, }, { "in": "query", "name": "categories", "required": False, "schema": { "title": "Categories", "type": "array", "items": {"type": "string"}, }, }, ]