test em
This commit is contained in:
parent
daf81343bf
commit
a2b33b25f8
11 changed files with 479 additions and 55 deletions
|
|
@ -4,15 +4,15 @@ from fastapi import APIRouter, Depends
|
||||||
from sqlmodel import SQLModel
|
from sqlmodel import SQLModel
|
||||||
|
|
||||||
from learn_sql_model.api.user import oauth2_scheme
|
from learn_sql_model.api.user import oauth2_scheme
|
||||||
from learn_sql_model.config import get_config
|
from learn_sql_model.config import Config, get_config
|
||||||
from learn_sql_model.models.hero import Hero
|
from learn_sql_model.models.hero import Hero
|
||||||
|
|
||||||
hero_router = APIRouter()
|
hero_router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
@hero_router.on_event("startup")
|
@hero_router.on_event("startup")
|
||||||
def on_startup() -> None:
|
def on_startup(config: Config = Depends(get_config)) -> None:
|
||||||
SQLModel.metadata.create_all(get_config().database.engine)
|
SQLModel.metadata.create_all(config.database.engine)
|
||||||
|
|
||||||
|
|
||||||
@hero_router.get("/items/")
|
@hero_router.get("/items/")
|
||||||
|
|
@ -21,21 +21,22 @@ async def read_items(token: Annotated[str, Depends(oauth2_scheme)]):
|
||||||
|
|
||||||
|
|
||||||
@hero_router.get("/hero/{id}")
|
@hero_router.get("/hero/{id}")
|
||||||
def get_hero(id: int) -> Hero:
|
def get_hero(id: int, config: Config = Depends(get_config)) -> Hero:
|
||||||
"get one hero"
|
"get one hero"
|
||||||
return Hero().get(id=id)
|
return Hero().get(id=id, config=config)
|
||||||
|
|
||||||
|
|
||||||
@hero_router.post("/hero/")
|
@hero_router.post("/hero/")
|
||||||
def post_hero(hero: Hero) -> Hero:
|
def post_hero(hero: Hero, config: Config = Depends(get_config)) -> Hero:
|
||||||
"read all the heros"
|
"read all the heros"
|
||||||
return hero.post()
|
hero.post(config=config)
|
||||||
|
return hero
|
||||||
|
|
||||||
|
|
||||||
@hero_router.get("/heros/")
|
@hero_router.get("/heros/")
|
||||||
def get_heros() -> list[Hero]:
|
def get_heros(config: Config = Depends(get_config)) -> list[Hero]:
|
||||||
"get all heros"
|
"get all heros"
|
||||||
return Hero().get()
|
return Hero().get(config=config)
|
||||||
# Alternatively
|
# Alternatively
|
||||||
# with get_config().database.session as session:
|
# with get_config().database.session as session:
|
||||||
# statement = select(Hero)
|
# statement = select(Hero)
|
||||||
|
|
|
||||||
|
|
@ -10,12 +10,12 @@ from learn_sql_model.cli.tui import tui_app
|
||||||
|
|
||||||
app = typer.Typer(
|
app = typer.Typer(
|
||||||
name="learn_sql_model",
|
name="learn_sql_model",
|
||||||
help="A rich terminal report for coveragepy.",
|
help="learn-sql-model cli for managing the project",
|
||||||
)
|
)
|
||||||
app.add_typer(config_app)
|
app.add_typer(config_app, name="config")
|
||||||
app.add_typer(tui_app)
|
app.add_typer(tui_app, name="tui")
|
||||||
app.add_typer(model_app)
|
app.add_typer(model_app, name="model")
|
||||||
app.add_typer(api_app)
|
app.add_typer(api_app, name="api")
|
||||||
app.add_typer(hero_app, name="hero")
|
app.add_typer(hero_app, name="hero")
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -38,6 +38,17 @@ def version_callback(value: bool) -> None:
|
||||||
raise typer.Exit()
|
raise typer.Exit()
|
||||||
|
|
||||||
|
|
||||||
|
@app.callback()
|
||||||
|
def main(
|
||||||
|
version: bool = typer.Option(
|
||||||
|
False,
|
||||||
|
callback=version_callback,
|
||||||
|
help="show the version of the learn-sql-model package.",
|
||||||
|
),
|
||||||
|
):
|
||||||
|
"configuration cli"
|
||||||
|
|
||||||
|
|
||||||
@app.command()
|
@app.command()
|
||||||
def tui(ctx: typer.Context) -> None:
|
def tui(ctx: typer.Context) -> None:
|
||||||
Trogon(get_group(app), click_context=ctx).run()
|
Trogon(get_group(app), click_context=ctx).run()
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
from typing import List, Union
|
from typing import List, Optional, Union
|
||||||
|
|
||||||
from pydantic_typer import expand_pydantic_args
|
from pydantic_typer import expand_pydantic_args
|
||||||
from rich.console import Console
|
from rich.console import Console
|
||||||
|
|
@ -9,6 +9,7 @@ from learn_sql_model.factories.hero import HeroFactory
|
||||||
from learn_sql_model.factories.pet import PetFactory
|
from learn_sql_model.factories.pet import PetFactory
|
||||||
from learn_sql_model.models.hero import Hero
|
from learn_sql_model.models.hero import Hero
|
||||||
from learn_sql_model.models.pet import Pet
|
from learn_sql_model.models.pet import Pet
|
||||||
|
import sys
|
||||||
|
|
||||||
hero_app = typer.Typer()
|
hero_app = typer.Typer()
|
||||||
|
|
||||||
|
|
@ -21,7 +22,7 @@ def hero():
|
||||||
@hero_app.command()
|
@hero_app.command()
|
||||||
@expand_pydantic_args(typer=True)
|
@expand_pydantic_args(typer=True)
|
||||||
def get(
|
def get(
|
||||||
id: int = None,
|
id: Optional[int] = None,
|
||||||
config: Config = None,
|
config: Config = None,
|
||||||
) -> Union[Hero, List[Hero]]:
|
) -> Union[Hero, List[Hero]]:
|
||||||
"get one hero"
|
"get one hero"
|
||||||
|
|
@ -52,12 +53,11 @@ def populate(
|
||||||
config: Config = None,
|
config: Config = None,
|
||||||
) -> Hero:
|
) -> Hero:
|
||||||
"read all the heros"
|
"read all the heros"
|
||||||
config.init()
|
|
||||||
if config is None:
|
if config is None:
|
||||||
config = Config()
|
config = Config()
|
||||||
if config.env == "prod":
|
if config.env == "prod":
|
||||||
Console().print("populate is not supported in production")
|
Console().print("populate is not supported in production")
|
||||||
return
|
sys.exit(1)
|
||||||
|
|
||||||
for hero in HeroFactory().batch(n):
|
for hero in HeroFactory().batch(n):
|
||||||
pet = PetFactory().build()
|
pet = PetFactory().build()
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
|
from contextvars import ContextVar
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
from fastapi import Depends
|
||||||
from pydantic import BaseModel, BaseSettings
|
from pydantic import BaseModel, BaseSettings
|
||||||
from sqlalchemy import create_engine
|
from sqlalchemy import create_engine
|
||||||
from sqlmodel import SQLModel, Session
|
from sqlmodel import SQLModel, Session
|
||||||
|
|
@ -24,6 +26,13 @@ class Database:
|
||||||
self.config = get_config()
|
self.config = get_config()
|
||||||
else:
|
else:
|
||||||
self.config = config
|
self.config = config
|
||||||
|
self.db_state_default = {
|
||||||
|
"closed": None,
|
||||||
|
"conn": None,
|
||||||
|
"ctx": None,
|
||||||
|
"transactions": None,
|
||||||
|
}
|
||||||
|
self.db_state = ContextVar("db_state", default=self.db_state_default.copy())
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def engine(self) -> "Engine":
|
def engine(self) -> "Engine":
|
||||||
|
|
@ -60,6 +69,24 @@ def get_database(config: Config = None) -> Database:
|
||||||
return Database(config)
|
return Database(config)
|
||||||
|
|
||||||
|
|
||||||
|
async def reset_db_state(config: Config = None) -> None:
|
||||||
|
if config is None:
|
||||||
|
config = get_config()
|
||||||
|
config.database.db._state._state.set(db_state_default.copy())
|
||||||
|
config.database.db._state.reset()
|
||||||
|
|
||||||
|
|
||||||
|
def get_db(config: Config = None, reset_db_state=Depends(reset_db_state)):
|
||||||
|
if config is None:
|
||||||
|
config = get_config()
|
||||||
|
try:
|
||||||
|
config.database.db.connect()
|
||||||
|
yield
|
||||||
|
finally:
|
||||||
|
if not config.database.db.is_closed():
|
||||||
|
config.database.db.close()
|
||||||
|
|
||||||
|
|
||||||
def get_config(overrides: dict = {}) -> Config:
|
def get_config(overrides: dict = {}) -> Config:
|
||||||
raw_config = load("learn_sql_model")
|
raw_config = load("learn_sql_model")
|
||||||
config = Config(**raw_config, **overrides)
|
config = Config(**raw_config, **overrides)
|
||||||
|
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from typing import Optional
|
|
||||||
|
|
||||||
from learn_sql_model.models.fast_model import FastModel
|
|
||||||
from sqlmodel import Field
|
|
||||||
|
|
||||||
|
|
||||||
class Hero(FastModel, table=True):
|
|
||||||
id: Optional[int] = Field(default=None, primary_key=True)
|
|
||||||
name: str
|
|
||||||
secret_name: str
|
|
||||||
age: Optional[int] = None
|
|
||||||
# new_attribute: Optional[str] = None
|
|
||||||
# pets: List["Pet"] = Relationship(back_populates="hero")
|
|
||||||
|
|
@ -29,6 +29,8 @@ class FastModel(SQLModel):
|
||||||
with config.database.session as session:
|
with config.database.session as session:
|
||||||
session.add(self)
|
session.add(self)
|
||||||
session.commit()
|
session.commit()
|
||||||
|
session.refresh(self)
|
||||||
|
return
|
||||||
|
|
||||||
def get(
|
def get(
|
||||||
self, id: int = None, config: "Config" = None, where=None
|
self, id: int = None, config: "Config" = None, where=None
|
||||||
|
|
@ -52,6 +54,16 @@ class FastModel(SQLModel):
|
||||||
results = session.exec(statement).one()
|
results = session.exec(statement).one()
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
def flags(self, config: "Config" = None) -> None:
|
||||||
|
if config is None:
|
||||||
|
config = get_config()
|
||||||
|
flags = []
|
||||||
|
for k, v in self.dict().items():
|
||||||
|
if v:
|
||||||
|
flags.append(f"--{k.replace('_', '-').lower()}")
|
||||||
|
flags.append(v)
|
||||||
|
return flags
|
||||||
|
|
||||||
# TODO
|
# TODO
|
||||||
# update
|
# update
|
||||||
# delete
|
# delete
|
||||||
|
|
|
||||||
275
markata.toml
Normal file
275
markata.toml
Normal file
|
|
@ -0,0 +1,275 @@
|
||||||
|
#
|
||||||
|
# __ __ _ _ _ _
|
||||||
|
# | \/ | __ _ _ __| | ____ _| |_ __ _ | |_ ___ _ __ ___ | |
|
||||||
|
# | |\/| |/ _` | '__| |/ / _` | __/ _` || __/ _ \| '_ ` _ \| |
|
||||||
|
# | | | | (_| | | | < (_| | || (_| || || (_) | | | | | | |
|
||||||
|
# |_| |_|\__,_|_| |_|\_\__,_|\__\__,_(_)__\___/|_| |_| |_|_|
|
||||||
|
#
|
||||||
|
# learn-sql-model.dev
|
||||||
|
|
||||||
|
[markata.nav]
|
||||||
|
'learn-sql-model'='https://learn-sql-model.dev/'
|
||||||
|
'GitHub'='https://github.com/WaylonWalker/learn-sql-model'
|
||||||
|
|
||||||
|
[markata]
|
||||||
|
# bump site version to bust GitHub actions cache
|
||||||
|
site_version = 13
|
||||||
|
|
||||||
|
## choose your markdown backend
|
||||||
|
# markdown_backend='markdown'
|
||||||
|
# markdown_backend='markdown2'
|
||||||
|
markdown_backend='markdown-it-py'
|
||||||
|
|
||||||
|
# 2 weeks in seconds
|
||||||
|
default_cache_expire = 1209600
|
||||||
|
# subroute = "docs"
|
||||||
|
|
||||||
|
## Markata Setup
|
||||||
|
output_dir = "markout"
|
||||||
|
assets_dir = "static"
|
||||||
|
hooks = [
|
||||||
|
"markata.plugins.publish_source",
|
||||||
|
"markata.plugins.docs",
|
||||||
|
"default",
|
||||||
|
]
|
||||||
|
disabled_hooks = [
|
||||||
|
'markata.plugins.heading_link',
|
||||||
|
'markata.plugins.manifest',
|
||||||
|
'markata.plugins.rss'
|
||||||
|
]
|
||||||
|
|
||||||
|
## Site Config
|
||||||
|
url = "https://learn-sql-model.dev"
|
||||||
|
title = "Learn SQLModel's Docs"
|
||||||
|
description = "Documentation for using the Learn SQLModel"
|
||||||
|
rss_description = "Learn SQLModel docs"
|
||||||
|
author_name = "Waylon Walker"
|
||||||
|
author_email = "waylon@waylonwalaker.com"
|
||||||
|
icon = "favicon.ico"
|
||||||
|
lang = "en"
|
||||||
|
# post_template = "pages/templates/post_template.html"
|
||||||
|
repo_url = "https://github.com/waylonwalker/learn-sql-model"
|
||||||
|
repo_branch = "main"
|
||||||
|
theme_color = "#322D39"
|
||||||
|
background_color = "#B73CF6"
|
||||||
|
start_url = "/"
|
||||||
|
site_name = "Learn SQLModel's Docs"
|
||||||
|
short_name = "ww"
|
||||||
|
display = "minimal-ui"
|
||||||
|
twitter_card = "summary_large_image"
|
||||||
|
twitter_creator = "@_waylonwalker"
|
||||||
|
twitter_site = "@_waylonwalker"
|
||||||
|
|
||||||
|
# markdown_it flavor
|
||||||
|
# [markata.markdown_it_py]
|
||||||
|
# config='gfm-like'
|
||||||
|
# # markdown_it built-in plugins
|
||||||
|
# enable = [ "table" ]
|
||||||
|
# disable = [ "image" ]
|
||||||
|
|
||||||
|
# # markdown_it built-in plugin options
|
||||||
|
# [markata.markdown_it_py.options_update]
|
||||||
|
# linkify = true
|
||||||
|
# html = true
|
||||||
|
# typographer = true
|
||||||
|
# highlight = 'markata.plugins.md_it_highlight_code:highlight_code'
|
||||||
|
|
||||||
|
# # add custom markdown_it plugins
|
||||||
|
# [[markata.markdown_it_py.plugins]]
|
||||||
|
# plugin = "mdit_py_plugins.admon:admon_plugin"
|
||||||
|
|
||||||
|
# [[markata.markdown_it_py.plugins]]
|
||||||
|
# plugin = "mdit_py_plugins.admon:admon_plugin"
|
||||||
|
|
||||||
|
# [[markata.markdown_it_py.plugins]]
|
||||||
|
# plugin = "mdit_py_plugins.attrs:attrs_plugin"
|
||||||
|
# config = {spans = true}
|
||||||
|
|
||||||
|
# [[markata.markdown_it_py.plugins]]
|
||||||
|
# plugin = "mdit_py_plugins.attrs:attrs_block_plugin"
|
||||||
|
|
||||||
|
# [[markata.markdown_it_py.plugins]]
|
||||||
|
# plugin = "markata.plugins.mdit_details:details_plugin"
|
||||||
|
|
||||||
|
# [[markata.markdown_it_py.plugins]]
|
||||||
|
# plugin = "mdit_py_plugins.anchors:anchors_plugin"
|
||||||
|
|
||||||
|
# [markata.markdown_it_py.plugins.config]
|
||||||
|
# permalink = true
|
||||||
|
# permalinkSymbol = '<svg class="heading-permalink" aria-hidden="true" fill="currentColor" focusable="false" height="1em" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M9.199 13.599a5.99 5.99 0 0 0 3.949 2.345 5.987 5.987 0 0 0 5.105-1.702l2.995-2.994a5.992 5.992 0 0 0 1.695-4.285 5.976 5.976 0 0 0-1.831-4.211 5.99 5.99 0 0 0-6.431-1.242 6.003 6.003 0 0 0-1.905 1.24l-1.731 1.721a.999.999 0 1 0 1.41 1.418l1.709-1.699a3.985 3.985 0 0 1 2.761-1.123 3.975 3.975 0 0 1 2.799 1.122 3.997 3.997 0 0 1 .111 5.644l-3.005 3.006a3.982 3.982 0 0 1-3.395 1.126 3.987 3.987 0 0 1-2.632-1.563A1 1 0 0 0 9.201 13.6zm5.602-3.198a5.99 5.99 0 0 0-3.949-2.345 5.987 5.987 0 0 0-5.105 1.702l-2.995 2.994a5.992 5.992 0 0 0-1.695 4.285 5.976 5.976 0 0 0 1.831 4.211 5.99 5.99 0 0 0 6.431 1.242 6.003 6.003 0 0 0 1.905-1.24l1.723-1.723a.999.999 0 1 0-1.414-1.414L9.836 19.81a3.985 3.985 0 0 1-2.761 1.123 3.975 3.975 0 0 1-2.799-1.122 3.997 3.997 0 0 1-.111-5.644l3.005-3.006a3.982 3.982 0 0 1 3.395-1.126 3.987 3.987 0 0 1 2.632 1.563 1 1 0 0 0 1.602-1.198z"></path></svg>'
|
||||||
|
|
||||||
|
# [[markata.markdown_it_py.plugins]]
|
||||||
|
# plugin = "markata.plugins.md_it_wikilinks:wikilinks_plugin"
|
||||||
|
# config = {markata = "markata"}
|
||||||
|
|
||||||
|
# markata feeds
|
||||||
|
# creating pages of posts
|
||||||
|
# [markata.feeds_config]
|
||||||
|
|
||||||
|
## feed template
|
||||||
|
# [markata.feeds.<slug>]
|
||||||
|
# title="Project Gallery"
|
||||||
|
## python eval to True adds post to the feed
|
||||||
|
# filter="'project-gallery' in path"
|
||||||
|
## the key to sort on
|
||||||
|
# sort='title'
|
||||||
|
## the template for each post to use when added to the page
|
||||||
|
# card_template="""
|
||||||
|
# """
|
||||||
|
|
||||||
|
[[markata.feeds]]
|
||||||
|
slug='project-gallery'
|
||||||
|
title="Project Gallery"
|
||||||
|
filter="'project-gallery' in str(path)"
|
||||||
|
sort='title'
|
||||||
|
card_template="""
|
||||||
|
<li class='post' style='background:rgba(255, 255, 255, .05); border:1px solid rgba(255, 255, 255, .2); padding:1rem; margin: 2rem auto;' >
|
||||||
|
<a href='/{{ slug }}/'><h2>{{ title }}</h2></a>
|
||||||
|
<ul style='display: flex; list-style-type: None;'>
|
||||||
|
<li><a href='{{ codeUrl }}'>Source Code</a></li>
|
||||||
|
<li><a href='{{ url }}'>Public Site</a></li>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
{{ article_html }}
|
||||||
|
</li>
|
||||||
|
"""
|
||||||
|
|
||||||
|
[[markata.feeds]]
|
||||||
|
slug='docs'
|
||||||
|
title="Documentation"
|
||||||
|
filter='"markata" not in slug and "tests" not in slug and "404" not in slug'
|
||||||
|
sort='slug'
|
||||||
|
card_template="<li class='post'><a href='/{{ slug }}/'>{{ title }}<p style='color: white; text-decoration: none;'>{{ description }}</p></a> </li>"
|
||||||
|
|
||||||
|
[[markata.feeds]]
|
||||||
|
slug='all'
|
||||||
|
title="All Learn SQLModel Modules"
|
||||||
|
filter="True"
|
||||||
|
card_template="""
|
||||||
|
<li class='post' style='background:rgba(255, 255, 255, .05); border:1px solid rgba(255, 255, 255, .2); padding:1rem; margin: 2rem auto;' >
|
||||||
|
<a href='/{{ slug }}/'>
|
||||||
|
<a href='/{{ slug }}/'>{{ title }}</a>
|
||||||
|
<p>
|
||||||
|
{{ article_html[:article_html.find('</p>')] }}
|
||||||
|
</p>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
"""
|
||||||
|
|
||||||
|
[[markata.feeds]]
|
||||||
|
slug='core-modules'
|
||||||
|
title="Learn SQLModel Core Modules"
|
||||||
|
filter="'plugin' not in slug and 'test' not in slug and title.endswith('.py')"
|
||||||
|
card_template="""
|
||||||
|
<li class='post' style='background:rgba(255, 255, 255, .05); border:1px solid rgba(255, 255, 255, .2); padding:1rem; margin: 2rem auto;' >
|
||||||
|
<a href='/{{ slug }}/'>
|
||||||
|
<a href='/{{ slug }}/'>{{ title }}</a>
|
||||||
|
<p>
|
||||||
|
{{ article_html[:article_html.find('</p>')] }}
|
||||||
|
</p>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
[markata.jinja_md]
|
||||||
|
ignore=[
|
||||||
|
'jinja_md.md',
|
||||||
|
'post_template.md',
|
||||||
|
'publish_html.md',
|
||||||
|
]
|
||||||
|
|
||||||
|
[[markata.head.meta]]
|
||||||
|
name = "og:author_email"
|
||||||
|
content = "waylon@waylonwalker.com"
|
||||||
|
|
||||||
|
[markata.tui]
|
||||||
|
new_cmd=['tmux', 'popup', 'markata', 'new', 'post']
|
||||||
|
|
||||||
|
[[markata.tui.keymap]]
|
||||||
|
name='new'
|
||||||
|
key='n'
|
||||||
|
|
||||||
|
[markata.summary]
|
||||||
|
grid_attr = ['tags', 'series']
|
||||||
|
|
||||||
|
[[markata.summary.filter_count]]
|
||||||
|
name='drafts'
|
||||||
|
filter="not published"
|
||||||
|
color='red'
|
||||||
|
|
||||||
|
[[markata.summary.filter_count]]
|
||||||
|
name='articles'
|
||||||
|
color='dark_orange'
|
||||||
|
|
||||||
|
[[markata.summary.filter_count]]
|
||||||
|
name='py_modules'
|
||||||
|
filter='"plugin" not in slug and "docs" not in str(path)'
|
||||||
|
color="yellow1"
|
||||||
|
|
||||||
|
[[markata.summary.filter_count]]
|
||||||
|
name='published'
|
||||||
|
filter="published"
|
||||||
|
color='green1'
|
||||||
|
|
||||||
|
[[markata.summary.filter_count]]
|
||||||
|
name='plugins'
|
||||||
|
filter='"plugin" in slug and "docs" not in str(path)'
|
||||||
|
color="blue"
|
||||||
|
|
||||||
|
[[markata.summary.filter_count]]
|
||||||
|
name='docs'
|
||||||
|
filter="'docs' in str(path)"
|
||||||
|
color='purple'
|
||||||
|
|
||||||
|
[markata.post_model]
|
||||||
|
include = ['date', 'description', 'published', 'slug', 'title', 'content', 'html']
|
||||||
|
repr_include = ['date', 'description', 'published', 'slug', 'title', 'output_html']
|
||||||
|
|
||||||
|
[markata.render_markdown]
|
||||||
|
backend='markdown-it-py'
|
||||||
|
|
||||||
|
# [markata.markdown_it_py]
|
||||||
|
# config='gfm-like'
|
||||||
|
# # markdown_it built-in plugins
|
||||||
|
# enable = [ "table" ]
|
||||||
|
# disable = [ "image" ]
|
||||||
|
|
||||||
|
# # markdown_it built-in plugin options
|
||||||
|
# [markata.markdown_it_py.options_update]
|
||||||
|
# linkify = true
|
||||||
|
# html = true
|
||||||
|
# typographer = true
|
||||||
|
# highlight = 'markata.plugins.md_it_highlight_code:highlight_code'
|
||||||
|
|
||||||
|
# add custom markdown_it plugins
|
||||||
|
[[markata.render_markdown.md_it_extensions]]
|
||||||
|
plugin = "mdit_py_plugins.admon:admon_plugin"
|
||||||
|
|
||||||
|
[[markata.render_markdown.md_it_extensions]]
|
||||||
|
plugin = "mdit_py_plugins.admon:admon_plugin"
|
||||||
|
|
||||||
|
[[markata.render_markdown.md_it_extensions]]
|
||||||
|
plugin = "mdit_py_plugins.attrs:attrs_plugin"
|
||||||
|
config = {spans = true}
|
||||||
|
|
||||||
|
[[markata.render_markdown.md_it_extensions]]
|
||||||
|
plugin = "mdit_py_plugins.attrs:attrs_block_plugin"
|
||||||
|
|
||||||
|
[[markata.render_markdown.md_it_extensions]]
|
||||||
|
plugin = "markata.plugins.mdit_details:details_plugin"
|
||||||
|
|
||||||
|
[[markata.render_markdown.md_it_extensions]]
|
||||||
|
plugin = "mdit_py_plugins.anchors:anchors_plugin"
|
||||||
|
|
||||||
|
[markata.render_markdown.md_it_extensions.config]
|
||||||
|
permalink = true
|
||||||
|
permalinkSymbol = '<svg class="heading-permalink" aria-hidden="true" fill="currentColor" focusable="false" height="1em" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M9.199 13.599a5.99 5.99 0 0 0 3.949 2.345 5.987 5.987 0 0 0 5.105-1.702l2.995-2.994a5.992 5.992 0 0 0 1.695-4.285 5.976 5.976 0 0 0-1.831-4.211 5.99 5.99 0 0 0-6.431-1.242 6.003 6.003 0 0 0-1.905 1.24l-1.731 1.721a.999.999 0 1 0 1.41 1.418l1.709-1.699a3.985 3.985 0 0 1 2.761-1.123 3.975 3.975 0 0 1 2.799 1.122 3.997 3.997 0 0 1 .111 5.644l-3.005 3.006a3.982 3.982 0 0 1-3.395 1.126 3.987 3.987 0 0 1-2.632-1.563A1 1 0 0 0 9.201 13.6zm5.602-3.198a5.99 5.99 0 0 0-3.949-2.345 5.987 5.987 0 0 0-5.105 1.702l-2.995 2.994a5.992 5.992 0 0 0-1.695 4.285 5.976 5.976 0 0 0 1.831 4.211 5.99 5.99 0 0 0 6.431 1.242 6.003 6.003 0 0 0 1.905-1.24l1.723-1.723a.999.999 0 1 0-1.414-1.414L9.836 19.81a3.985 3.985 0 0 1-2.761 1.123 3.975 3.975 0 0 1-2.799-1.122 3.997 3.997 0 0 1-.111-5.644l3.005-3.006a3.982 3.982 0 0 1 3.395-1.126 3.987 3.987 0 0 1 2.632 1.563 1 1 0 0 0 1.602-1.198z"></path></svg>'
|
||||||
|
|
||||||
|
[[markata.render_markdown.md_it_extensions]]
|
||||||
|
plugin = "markata.plugins.md_it_wikilinks:wikilinks_plugin"
|
||||||
|
config = {markata = "markata"}
|
||||||
|
|
||||||
|
[markata.glob]
|
||||||
|
glob_patterns = "docs/**/*.md,CHANGELOG.md"
|
||||||
|
use_gitignore = true
|
||||||
|
|
@ -57,17 +57,20 @@ path = "learn_sql_model/__about__.py"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"black",
|
"black",
|
||||||
"ipython",
|
"ipython",
|
||||||
|
"coverage[toml]",
|
||||||
|
"coverage-rich",
|
||||||
|
"markata",
|
||||||
"mypy",
|
"mypy",
|
||||||
"pyflyby",
|
"pyflyby",
|
||||||
"pytest",
|
"pytest",
|
||||||
"pytest-cov",
|
|
||||||
"pytest-mock",
|
"pytest-mock",
|
||||||
"ruff",
|
"ruff",
|
||||||
"alembic",
|
"alembic",
|
||||||
]
|
]
|
||||||
[tool.hatch.envs.default.scripts]
|
[tool.hatch.envs.default.scripts]
|
||||||
test = "coverage run -m pytest"
|
test = "coverage run -m pytest"
|
||||||
cov = "coverage-rich"
|
cov = "coverage-rich report"
|
||||||
|
test-cov = ['test', 'cov']
|
||||||
lint = "ruff learn_sql_model"
|
lint = "ruff learn_sql_model"
|
||||||
format = "black learn_sql_model"
|
format = "black learn_sql_model"
|
||||||
format-check = "black --check learn_sql_model"
|
format-check = "black --check learn_sql_model"
|
||||||
|
|
@ -84,6 +87,7 @@ test-lint = "lint-test"
|
||||||
python = ["37", "38", "39", "310", "311"]
|
python = ["37", "38", "39", "310", "311"]
|
||||||
|
|
||||||
[tool.coverage.run]
|
[tool.coverage.run]
|
||||||
|
source=["learn_sql_model"]
|
||||||
branch = true
|
branch = true
|
||||||
parallel = true
|
parallel = true
|
||||||
omit = [
|
omit = [
|
||||||
|
|
|
||||||
15
tests/test_cli_app.py
Normal file
15
tests/test_cli_app.py
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
from typer.testing import CliRunner
|
||||||
|
|
||||||
|
from learn_sql_model.cli.app import app
|
||||||
|
|
||||||
|
runner = CliRunner()
|
||||||
|
|
||||||
|
|
||||||
|
def test_cli_app_version():
|
||||||
|
result = runner.invoke(app, ["--version"])
|
||||||
|
assert result.exit_code == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_cli_help():
|
||||||
|
result = runner.invoke(app, ["--help"])
|
||||||
|
assert result.exit_code == 0
|
||||||
14
tests/test_console.py
Normal file
14
tests/test_console.py
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
from learn_sql_model.console import console
|
||||||
|
|
||||||
|
|
||||||
|
def test_default_console_not_quiet(capsys):
|
||||||
|
console.print("hello")
|
||||||
|
captured = capsys.readouterr()
|
||||||
|
assert captured.out == "hello\n"
|
||||||
|
|
||||||
|
|
||||||
|
def test_default_console_is_quiet(capsys):
|
||||||
|
console.quiet = True
|
||||||
|
console.print("hello")
|
||||||
|
captured = capsys.readouterr()
|
||||||
|
assert captured.out == ""
|
||||||
|
|
@ -2,6 +2,9 @@ import tempfile
|
||||||
|
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
import pytest
|
import pytest
|
||||||
|
from sqlalchemy import create_engine
|
||||||
|
from sqlalchemy.orm import sessionmaker
|
||||||
|
from sqlmodel import SQLModel
|
||||||
from typer.testing import CliRunner
|
from typer.testing import CliRunner
|
||||||
|
|
||||||
from learn_sql_model.api.app import app
|
from learn_sql_model.api.app import app
|
||||||
|
|
@ -18,11 +21,31 @@ client = TestClient(app)
|
||||||
def config() -> Config:
|
def config() -> Config:
|
||||||
tmp_db = tempfile.NamedTemporaryFile(suffix=".db")
|
tmp_db = tempfile.NamedTemporaryFile(suffix=".db")
|
||||||
config = get_config({"database_url": f"sqlite:///{tmp_db.name}"})
|
config = get_config({"database_url": f"sqlite:///{tmp_db.name}"})
|
||||||
return config
|
|
||||||
|
engine = create_engine(
|
||||||
|
config.database_url, connect_args={"check_same_thread": False}
|
||||||
|
)
|
||||||
|
TestingSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||||
|
|
||||||
|
# breakpoint()
|
||||||
|
SQLModel.metadata.create_all(config.database.engine)
|
||||||
|
|
||||||
|
# def override_get_db():
|
||||||
|
# try:
|
||||||
|
# db = TestingSessionLocal()
|
||||||
|
# yield db
|
||||||
|
# finally:
|
||||||
|
# db.close()
|
||||||
|
def override_get_config():
|
||||||
|
return config
|
||||||
|
|
||||||
|
app.dependency_overrides[get_config] = override_get_config
|
||||||
|
|
||||||
|
yield config
|
||||||
|
# tmp_db automatically deletes here
|
||||||
|
|
||||||
|
|
||||||
def test_post_hero(config: Config) -> None:
|
def test_post_hero(config: Config) -> None:
|
||||||
config.init() # required for python api, and no existing db
|
|
||||||
hero = HeroFactory().build(name="Batman", age=50, id=1)
|
hero = HeroFactory().build(name="Batman", age=50, id=1)
|
||||||
hero = hero.post(config=config)
|
hero = hero.post(config=config)
|
||||||
db_hero = Hero().get(id=1, config=config)
|
db_hero = Hero().get(id=1, config=config)
|
||||||
|
|
@ -31,48 +54,105 @@ def test_post_hero(config: Config) -> None:
|
||||||
|
|
||||||
|
|
||||||
def test_update_hero(config: Config) -> None:
|
def test_update_hero(config: Config) -> None:
|
||||||
config.init() # required for python api, and no existing db
|
|
||||||
hero = HeroFactory().build(name="Batman", age=50, id=1)
|
hero = HeroFactory().build(name="Batman", age=50, id=1)
|
||||||
hero = hero.post(config=config)
|
hero = hero.post(config=config)
|
||||||
db_hero = Hero().get(id=1, config=config)
|
db_hero = Hero().get(id=1, config=config)
|
||||||
db_hero.name = "Superman"
|
db_hero.name = "Superbman"
|
||||||
hero = db_hero.post(config=config)
|
hero = db_hero.post(config=config)
|
||||||
db_hero = Hero().get(id=1, config=config)
|
db_hero = Hero().get(id=1, config=config)
|
||||||
assert db_hero.age == 50
|
assert db_hero.age == 50
|
||||||
assert db_hero.name == "Superman"
|
assert db_hero.name == "Superbman"
|
||||||
|
|
||||||
|
|
||||||
|
def test_cli_get(config):
|
||||||
|
hero = HeroFactory().build(name="Steelman", age=25, id=99)
|
||||||
|
hero.post(config=config)
|
||||||
|
result = runner.invoke(
|
||||||
|
hero_app,
|
||||||
|
["get", "--id", 99, "--database-url", config.database_url],
|
||||||
|
)
|
||||||
|
assert result.exit_code == 0
|
||||||
|
db_hero = Hero().get(id=99, config=config)
|
||||||
|
assert db_hero.age == 25
|
||||||
|
assert db_hero.name == "Steelman"
|
||||||
|
|
||||||
|
|
||||||
def test_cli_create(config):
|
def test_cli_create(config):
|
||||||
|
hero = HeroFactory().build(name="Steelman", age=25, id=99)
|
||||||
result = runner.invoke(
|
result = runner.invoke(
|
||||||
hero_app,
|
hero_app,
|
||||||
[
|
[
|
||||||
"create",
|
"create",
|
||||||
"--name",
|
*hero.flags(config=config),
|
||||||
"Darth Vader",
|
|
||||||
"--secret-name",
|
|
||||||
"Anakin",
|
|
||||||
"--id",
|
|
||||||
"2",
|
|
||||||
"--age",
|
|
||||||
"100",
|
|
||||||
"--database-url",
|
"--database-url",
|
||||||
config.database_url,
|
config.database_url,
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
assert result.exit_code == 0
|
assert result.exit_code == 0
|
||||||
db_hero = Hero().get(id=2, config=config)
|
db_hero = Hero().get(id=99, config=config)
|
||||||
assert db_hero.age == 100
|
assert db_hero.age == 25
|
||||||
assert db_hero.name == "Darth Vader"
|
assert db_hero.name == "Steelman"
|
||||||
|
|
||||||
|
|
||||||
def test_read_main(config):
|
def test_cli_populate(config):
|
||||||
config.init()
|
result = runner.invoke(
|
||||||
hero = HeroFactory().build(name="Ironman", age=25, id=99)
|
hero_app,
|
||||||
|
[
|
||||||
|
"populate",
|
||||||
|
"--n",
|
||||||
|
10,
|
||||||
|
"--database-url",
|
||||||
|
config.database_url,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
assert result.exit_code == 0
|
||||||
|
db_hero = Hero().get(config=config)
|
||||||
|
assert len(db_hero) == 10
|
||||||
|
|
||||||
|
|
||||||
|
def test_cli_populate_fails_prod(config):
|
||||||
|
result = runner.invoke(
|
||||||
|
hero_app,
|
||||||
|
["populate", "--n", 10, "--database-url", config.database_url, "--env", "prod"],
|
||||||
|
)
|
||||||
|
assert result.exit_code == 1
|
||||||
|
assert result.output.strip() == "populate is not supported in production"
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_read(config):
|
||||||
|
hero = HeroFactory().build(name="Steelman", age=25, id=99)
|
||||||
hero_id = hero.id
|
hero_id = hero.id
|
||||||
hero = hero.post(config=config)
|
hero = hero.post(config=config)
|
||||||
response = client.get(f"/hero/{hero_id}")
|
response = client.get(f"/hero/{hero_id}")
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
reponse_hero = Hero.parse_obj(response.json())
|
reponse_hero = Hero.parse_obj(response.json())
|
||||||
assert reponse_hero.id == hero_id
|
assert reponse_hero.id == hero_id
|
||||||
assert reponse_hero.name == "Ironman"
|
assert reponse_hero.name == "Steelman"
|
||||||
assert reponse_hero.age == 25
|
assert reponse_hero.age == 25
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_post(config):
|
||||||
|
hero = HeroFactory().build(name="Steelman", age=25)
|
||||||
|
hero_dict = hero.dict()
|
||||||
|
response = client.post("/hero/", json={"hero": hero_dict})
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
response_hero = Hero.parse_obj(response.json())
|
||||||
|
db_hero = Hero().get(id=response_hero.id, config=config)
|
||||||
|
|
||||||
|
assert db_hero.name == "Steelman"
|
||||||
|
assert db_hero.age == 25
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_read_all(config):
|
||||||
|
hero = HeroFactory().build(name="Mothman", age=25, id=99)
|
||||||
|
hero_id = hero.id
|
||||||
|
hero = hero.post(config=config)
|
||||||
|
response = client.get("/heros/")
|
||||||
|
assert response.status_code == 200
|
||||||
|
heros = response.json()
|
||||||
|
response_hero_json = [hero for hero in heros if hero["id"] == hero_id][0]
|
||||||
|
response_hero = Hero.parse_obj(response_hero_json)
|
||||||
|
assert response_hero.id == hero_id
|
||||||
|
assert response_hero.name == "Mothman"
|
||||||
|
assert response_hero.age == 25
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue