Commits: 5
Introduce SQLite to store jokes
For now only /jokes endpoint is implemented. The others are commented out.
index 18b86e4..a818d1a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
+jokes.db
=.devenv
=.direnv
=__pycache__index 75792ae..2f6297e 100644
--- a/flake.nix
+++ b/flake.nix
@@ -27,7 +27,9 @@
= modules = [
= {
= # https://devenv.sh/reference/options/
- packages = [];
+ packages = [
+ pkgs.litecli
+ ];
=
= languages.python = {
= enable = true;new file mode 100644
index 0000000..83fd1e1
--- /dev/null
+++ b/init.sql
@@ -0,0 +1,26 @@
+Create table if not exists "joker" (
+ id integer primary key autoincrement,
+ name text not null unique,
+ password text not null
+);
+
+
+Create table if not exists "joke" (
+ id integer primary key autoincrement,
+ text text not null,
+ author integer not null references "joker.id"
+);
+
+
+Insert into joker ("name", "password") values
+("Quipster", "1234"),
+("Giggle Guru", "1234"),
+("Wit‑Wizard", "1234"),
+("Chuckle Champion", "1234");
+
+
+Insert into joke ("author", "text") values
+(1, "Why did the scarecrow get promoted? He was outstanding in his field."),
+(2, "I used to be a baker, but I couldn’t make enough dough."),
+(2, "What’s a snake’s favorite subject in school? Hiss‑tory."),
+(3, "What's big? Your nose.");index be932e4..df65610 100644
--- a/main.py
+++ b/main.py
@@ -6,6 +6,8 @@ You can use it to teach concepts of REST.
=
=from fastapi import FastAPI
=from pydantic import BaseModel
+from typing import Optional
+import sqlite3
=import logging
=
=app = FastAPI()
@@ -18,30 +20,39 @@ def init():
=
=class Joke(BaseModel):
= """A joke."""
-
+ id: Optional[int]
= text: str
+ author: str
=
=
-jokes = [
- Joke(text="How do you find a velociraptor?\n\n" +
- "By taking the integral of the acceleraptor!")
-]
-
=
-@app.get("/")
-def get_random_joke() -> Joke:
- """Get a single random joke."""
- return jokes[0]
+# @app.get("/")
+# def get_random_joke() -> Joke:
+# """Get a single random joke."""
+# return jokes[0]
=
=
=@app.get("/jokes")
=def list_jokes() -> list[Joke]:
= """List all jokes."""
- return jokes
-
-
-@app.post("/jokes")
-def new_joke(joke: Joke) -> Joke:
- """Write a new joke."""
- jokes.append(joke)
- return joke
+ with sqlite3.connect("jokes.db") as db:
+ db.row_factory = sqlite3.Row
+ cursor = db.cursor()
+ cursor.execute("""
+ Select
+ joke.id,
+ joke.text,
+ joker.name as author
+ from
+ joke
+ join joker on joke.author = joker.id
+ ;
+ """)
+ return [Joke(**row) for row in cursor.fetchall()]
+
+
+# @app.post("/jokes")
+# def new_joke(joke: Joke) -> Joke:
+# """Write a new joke."""
+# jokes.append(joke)
+# return jokeImplement random joke endpoint
index df65610..365ecbf 100644
--- a/main.py
+++ b/main.py
@@ -26,10 +26,26 @@ class Joke(BaseModel):
=
=
=
-# @app.get("/")
-# def get_random_joke() -> Joke:
-# """Get a single random joke."""
-# return jokes[0]
+@app.get("/")
+def get_random_joke() -> Joke:
+ """Get a single random joke."""
+ with sqlite3.connect("jokes.db") as db:
+ db.row_factory = sqlite3.Row
+ cursor = db.cursor()
+ cursor.execute("""
+ Select
+ joke.id,
+ joke.text,
+ joker.name as author
+ from
+ joke
+ join joker on joke.author = joker.id
+ order by
+ random()
+ limit 1
+ ;
+ """)
+ return Joke(**cursor.fetchone())
=
=
=@app.get("/jokes")Implement the POST /jokes/ endpoint
index 365ecbf..d697b50 100644
--- a/main.py
+++ b/main.py
@@ -4,7 +4,7 @@ A sample REST API service.
=You can use it to teach concepts of REST.
="""
=
-from fastapi import FastAPI
+from fastapi import FastAPI, HTTPException
=from pydantic import BaseModel
=from typing import Optional
=import sqlite3
@@ -67,8 +67,37 @@ def list_jokes() -> list[Joke]:
= return [Joke(**row) for row in cursor.fetchall()]
=
=
-# @app.post("/jokes")
-# def new_joke(joke: Joke) -> Joke:
-# """Write a new joke."""
-# jokes.append(joke)
-# return joke
+@app.post("/jokes", status_code=201)
+def new_joke(joke: Joke) -> Joke:
+ """Write a new joke."""
+ with sqlite3.connect("jokes.db") as db:
+ db.row_factory = sqlite3.Row
+ cursor = db.cursor()
+ author = cursor.execute("""
+ Select
+ id
+ from
+ joker
+ where
+ name = ?
+ ;
+ """, (joke.author,)).fetchone()
+
+ if author is None:
+ logger.debug(f"Specified author not found {joke.author}")
+ raise HTTPException(
+ status_code=422,
+ detail=f"The specified joker is not known: {joke.author}"
+ )
+
+ row = cursor.execute("""
+ Insert into joke (
+ text,
+ author
+ )
+ values (?, ?)
+ returning id, text
+ ;
+ """, (joke.text, author["id"])).fetchone()
+
+ return Joke(**row, author=joke.author)Give the app a title and summary
index d697b50..d15e5f6 100644
--- a/main.py
+++ b/main.py
@@ -10,7 +10,11 @@ from typing import Optional
=import sqlite3
=import logging
=
-app = FastAPI()
+app = FastAPI(
+ title="JaaS",
+ summary="Joke as a service"
+)
+
=logger = logging.getLogger("uvicorn.error")
=
=@app.on_event("startup")Implement the GET /jokes/
Also POST /jokes/ will only give a location of a new joke to allow for demonstration of response headers.
Also, remove the em-dash from initial authors set.
index 83fd1e1..3f5e347 100644
--- a/init.sql
+++ b/init.sql
@@ -15,7 +15,7 @@ Create table if not exists "joke" (
=Insert into joker ("name", "password") values
=("Quipster", "1234"),
=("Giggle Guru", "1234"),
-("Wit‑Wizard", "1234"),
+("Wit-Wizard", "1234"),
=("Chuckle Champion", "1234");
=
=index d15e5f6..58c1ec2 100644
--- a/main.py
+++ b/main.py
@@ -4,7 +4,7 @@ A sample REST API service.
=You can use it to teach concepts of REST.
="""
=
-from fastapi import FastAPI, HTTPException
+from fastapi import FastAPI, HTTPException, Response
=from pydantic import BaseModel
=from typing import Optional
=import sqlite3
@@ -71,6 +71,31 @@ def list_jokes() -> list[Joke]:
= return [Joke(**row) for row in cursor.fetchall()]
=
=
+@app.get("/jokes/{id}")
+def get_joke(id: int) -> Joke:
+ """Get a single joke."""
+ with sqlite3.connect("jokes.db") as db:
+ db.row_factory = sqlite3.Row
+ cursor = db.cursor()
+ joke = cursor.execute("""
+ Select
+ joke.id,
+ joke.text,
+ joker.name as author
+ from
+ joke
+ join joker on joke.author = joker.id
+ where
+ joke.id = :id
+ ;
+ """, {"id": id}).fetchone()
+
+ if joke is None:
+ raise HTTPException(status_code=404)
+
+ return Joke(**joke)
+
+
=@app.post("/jokes", status_code=201)
=def new_joke(joke: Joke) -> Joke:
= """Write a new joke."""
@@ -99,9 +124,14 @@ def new_joke(joke: Joke) -> Joke:
= text,
= author
= )
- values (?, ?)
- returning id, text
+ values (:text, :author)
+ returning id
= ;
- """, (joke.text, author["id"])).fetchone()
+ """, {"text": joke.text, "author": author["id"]}).fetchone()
=
- return Joke(**row, author=joke.author)
+ return Response(
+ headers={
+ "location": f"/jokes/{row["id"]}"
+ },
+ status_code=201
+ )