diff --git a/examples/hello_flama.py b/examples/api/01-hello_flama.py similarity index 100% rename from examples/hello_flama.py rename to examples/api/01-hello_flama.py diff --git a/examples/api/02-data_schema.py b/examples/api/02-data_schema.py new file mode 100755 index 00000000..faa910e2 --- /dev/null +++ b/examples/api/02-data_schema.py @@ -0,0 +1,130 @@ +import http +from pydantic import BaseModel, validator + +import flama +from flama.http import APIResponse +from flama.types import Schema + + +class Puppy(BaseModel): + id: int + name: str + age: int + + @validator("age") + def minimum_age_validation(cls, v): + """Validates that the age is not negative.""" + if v < 0: + raise ValueError("Age must be positive") + + return v + + +PUPPIES = [ + {"id": 1, "name": "Bobby", "age": 3}, + {"id": 2, "name": "Rex", "age": 5}, + {"id": 3, "name": "Toby", "age": 2}, +] + + +app = flama.Flama( + title="My 🔥 API", + version="1.0", + description="My API with schema validation", + docs="/docs/", + schema="/schema/", +) + + +@app.route("/puppy/", methods=["GET"]) +def list_puppies(name: str = None): + """ + tags: + - puppy + summary: + List puppies. + description: + List the collection of puppies. There is an optional query parameter that + specifies a name for filtering the collection based on it. + responses: + 200: + description: List puppies. + """ + if name is None: + return PUPPIES + + puppies = [puppy for puppy in PUPPIES if puppy["name"] == name] + + if not puppies: + return APIResponse(status_code=http.HTTPStatus.NOT_FOUND) # type: ignore + + return puppies + + +@app.route("/puppy/", methods=["POST"]) +def create_puppy(puppy: Schema[Puppy]): + """ + tags: + - puppy + summary: + Create a new puppy. + description: + Create a new puppy using data validated from request body and add it + to the collection. + responses: + 200: + description: Puppy created successfully. + """ + if puppy["id"] in [p["id"] for p in PUPPIES]: + return APIResponse(status_code=http.HTTPStatus.CONFLICT) # type: ignore + + PUPPIES.append(puppy) + return puppy + + +@app.route("/puppy/{id:int}/", methods=["GET"]) +def get_puppy(id: int) -> Puppy: + """ + tags: + - puppy + summary: + Get a puppy. + description: + Get a puppy from the collection. + responses: + 200: + description: Puppy retrieved successfully. + """ + puppies = [puppy for puppy in PUPPIES if puppy["id"] == id] + if not puppies: + return APIResponse(status_code=http.HTTPStatus.NOT_FOUND) # type: ignore + + return puppies[0] + + +@app.route("/puppy/{id:int}/", methods=["DELETE"]) +def delete_puppy(id: int): + """ + tags: + - puppy + summary: + Delete a puppy. + description: + Delete a puppy from the collection. + responses: + 200: + description: Puppy deleted successfully. + """ + puppies = [puppy for puppy in PUPPIES if puppy["id"] == id] + if not puppies: + return APIResponse(status_code=http.HTTPStatus.NOT_FOUND) + + for puppy in puppies: + PUPPIES.remove(puppy) + return APIResponse(status_code=http.HTTPStatus.OK) + + +app.schema.register_schema("Puppy", Puppy) + +if __name__ == "__main__": + flama.run(flama_app=app, server_host="0.0.0.0", server_port=8000) diff --git a/examples/api/03-pagination.py b/examples/api/03-pagination.py new file mode 100755 index 00000000..b28af6e8 --- /dev/null +++ b/examples/api/03-pagination.py @@ -0,0 +1,88 @@ +import string +import typing + +from pydantic import BaseModel, validator + +import flama + + +class Puppy(BaseModel): + id: int + name: str + age: int + + @validator("age") + def minimum_age_validation(cls, v): + """Validates that the age is not negative.""" + if v < 0: + raise ValueError("Age must be positive") + + return v + + +PUPPIES = [ + {"id": 1, "name": "Canna", "age": 7}, + {"id": 2, "name": "Sandy", "age": 12}, + {"id": 3, "name": "Bobby", "age": 3}, + {"id": 4, "name": "Rex", "age": 5}, + {"id": 5, "name": "Toby", "age": 2}, +] + + +app = flama.Flama( + title="My 🔥 API", + version="1.0", + description="My API with pagination", + docs="/docs/", + schema="/schema/", +) + + +@app.route("/puppy/", methods=["GET"]) +@app.paginator.page_number(schema_name="Puppy") +def puppies(name: str = None, **kwargs) -> typing.List[Puppy]: + """ + tags: + - puppy + summary: + List puppies. + description: + List the puppies collection. There is an optional query parameter that + specifies a name for filtering the collection based on it. + responses: + 200: + description: List puppies. + """ + result = PUPPIES + if name: + result = filter(lambda x: x["name"] == name, result) + + return result + + +@app.route("/puppy-offset/", methods=["GET"]) +@app.paginator.limit_offset(schema_name="Puppy") +def puppies_offset(name: str, **kwargs) -> typing.List[Puppy]: + """ + tags: + - puppy + summary: + List puppies with offset pagination. + description: + List the puppies collection using offset pagination, so that + the client can specify the number of items to skip and the + number of items to return. + + responses: + 200: + description: List puppies. + """ + result = PUPPIES + if name: + return filter(lambda x: x["name"] == name, result) + + return result + + +if __name__ == "__main__": + flama.run(flama_app=app, server_host="0.0.0.0", server_port=8000) diff --git a/examples/data_schema.py b/examples/data_schema.py deleted file mode 100755 index 3ec87e1e..00000000 --- a/examples/data_schema.py +++ /dev/null @@ -1,73 +0,0 @@ -import typing - -from pydantic import BaseModel, validator - -import flama -from flama import Flama - -app = Flama( - title="Puppy Register", # API title - version="0.1", # API version - description="A register of puppies", # API description - schema="/schema/", # Path to expose OpenAPI schema - docs="/docs/", # Path to expose Docs application -) - - -class Puppy(BaseModel): - id: int - name: str - age: int - - @validator("age") - def minimum_age_validation(cls, v): - if v < 0: - raise ValueError("Age must be positive") - - return v - - -app.schema.register_schema("Puppy", Puppy) - - -def home(): - return {"hello": "world"} - - -def list_puppies(name: str = None) -> typing.List[Puppy]: - """ - tags: - - puppy - summary: - List puppies. - description: - List the puppies collection. There is an optional query parameter that - specifies a name for filtering the collection based on it. - responses: - 200: - description: List puppies. - """ - ... - - -def create_puppy(puppy: Puppy) -> Puppy: - """ - tags: - - puppy - summary: - Create a new puppy. - description: - Create a new puppy using data validated from request body and add it - to the collection. - responses: - 200: - description: Puppy created successfully. - """ - ... - - -app.add_route("/puppy/", list_puppies, methods=["GET"]) -app.add_route("/puppy/", create_puppy, methods=["POST"]) - -if __name__ == "__main__": - flama.run(app, host="0.0.0.0", port=8000) diff --git a/examples/pagination.py b/examples/pagination.py deleted file mode 100755 index fec2973a..00000000 --- a/examples/pagination.py +++ /dev/null @@ -1,90 +0,0 @@ -import string -import typing - -from pydantic import BaseModel, validator - -import flama -from flama import Flama - - -class Puppy(BaseModel): - id: int - name: str - age: int - - @validator("age") - def minimum_age_validation(cls, v): - if v < 0: - raise ValueError("Age must be positive") - - return v - - -PUPPIES = [{"id": 1, "name": "Canna", "age": 7}, {"id": 2, "name": "Sandy", "age": 12}] - - -app = Flama( - title="Puppy Register", # API title - version="0.1", # API version - description="A register of puppies", # API description -) - - -@app.route("/number/", methods=["GET"]) -@app.paginator.page_number -def numbers(**kwargs): - """ - tags: - - numbers. - summary: - A sequence of numbers. - description: - A sequence of numbers that uses page-number pagination. - responses: - 200: - description: Sequence of numbers. - """ - return list(range(100)) - - -@app.route("/alphabet/", methods=["GET"]) -@app.paginator.limit_offset -def alphabet(**kwargs): - """ - tags: - - alphabet. - summary: - A sequence of alphabet letters. - description: - A sequence of alphabet letters that uses limit-offset pagination. - responses: - 200: - description: Sequence of alphabet letters. - """ - return list(string.ascii_lowercase) - - -@app.route("/puppy/", methods=["GET"]) -@app.paginator.page_number -def puppies(name: str = None, **kwargs) -> typing.List[Puppy]: - """ - tags: - - puppy - summary: - List puppies. - description: - List the puppies collection. There is an optional query parameter that - specifies a name for filtering the collection based on it. - responses: - 200: - description: List puppies. - """ - result = PUPPIES - if name: - result = filter(lambda x: x["name"] == name, result) - - return result - - -if __name__ == "__main__": - flama.run(app, host="0.0.0.0", port=8000)