diff --git a/.gitignore b/.gitignore index 68d69529..353da69f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,13 @@ -config.ini -.idea/ -.vscode/ -__pycache__ -*.egg-info/ -.pytest_cache/ +# Royalnet ignores +config*.toml +downloads/ + + +# Python ignores +**/__pycache__/ dist/ -build/ -venv/ +*.egg-info/ +**/*.pyc + +# PyCharm ignores +.idea/ diff --git a/README.md b/README.md index aef4366f..a988ebbf 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -# `{packname}` [![PyPI](https://img.shields.io/pypi/v/{packname}.svg)](https://pypi.org/project/{packname}/) +# `examplepack` -{Replace everything surrounded by braces with your own data, including this description!} +This is an example Pack for [Royalnet](https://github.com/Steffo99/royalnet)! -{Files to be changed are the package folder, setup.py and this README!} +> To be updated with more info on how to create your own pack. \ No newline at end of file diff --git a/examplepack/commands/__init__.py b/examplepack/commands/__init__.py new file mode 100644 index 00000000..c9ffb60c --- /dev/null +++ b/examplepack/commands/__init__.py @@ -0,0 +1,12 @@ +# Imports go here! +# TODO: If you create a new command, remember to import it here... +# from .example import ExampleCommand + +# Enter the commands of your Pack here! +# TODO: and add it to the list here! +available_commands = [ + # ExampleCommand, +] + +# Don't change this, it should automatically generate __all__ +__all__ = [command.__name__ for command in available_commands] diff --git a/examplepack/commands/example.py b/examplepack/commands/example.py new file mode 100644 index 00000000..f98652ba --- /dev/null +++ b/examplepack/commands/example.py @@ -0,0 +1,12 @@ +from typing import * +from royalnet.commands import * +from royalnet.utils import * + + +class ExampleCommand(Command): + name: str = "example" + + description: str = "Say Hello to the world!" + + async def run(self, args: CommandArgs, data: CommandData) -> None: + await data.reply("Hello world!") diff --git a/examplepack/events/__init__.py b/examplepack/events/__init__.py new file mode 100644 index 00000000..8bafb696 --- /dev/null +++ b/examplepack/events/__init__.py @@ -0,0 +1,12 @@ +# Imports go here! +# TODO: If you create a new event, remember to import it here... +# from .example import ExampleEvent + +# Enter the commands of your Pack here! +# TODO: and add it to the list here! +available_events = [ + # ExampleEvent, +] + +# Don't change this, it should automatically generate __all__ +__all__ = [command.__name__ for command in available_events] diff --git a/examplepack/events/example.py b/examplepack/events/example.py new file mode 100644 index 00000000..866abc7a --- /dev/null +++ b/examplepack/events/example.py @@ -0,0 +1,10 @@ +from typing import * +from royalnet.commands import * +from royalnet.utils import * + + +class ExampleEvent(Event): + name = "example" + + async def run(self, **kwargs) -> dict: + return {"hello": "world"} diff --git a/examplepack/stars/__init__.py b/examplepack/stars/__init__.py new file mode 100644 index 00000000..8193b6d8 --- /dev/null +++ b/examplepack/stars/__init__.py @@ -0,0 +1,14 @@ +# Imports go here! +# TODO: If you create a new star, remember to import it here... +# from .example import ExampleStar +# from .api_example import ApiExampleStar + +# Enter the PageStars of your Pack here! +# TODO: and to add it either to the list here if it is a PageStar... +available_page_stars = [ + # ExampleStar, + # ApiExampleStar, +] + +# Don't change this, it should automatically generate __all__ +__all__ = [command.__name__ for command in available_page_stars] diff --git a/examplepack/stars/api_example.py b/examplepack/stars/api_example.py new file mode 100644 index 00000000..80b03165 --- /dev/null +++ b/examplepack/stars/api_example.py @@ -0,0 +1,70 @@ +import royalnet.utils as ru +import royalnet.constellation.api as rca +import royalnet.constellation.api.apierrors as rcae + + +# View autogenerated docs for all ApiStars at the HTTP path `/docs` + + +class EchoStar(rca.ApiStar): + # What does this ApiStar do? + summary = "Returns the same string entered as input." + + # Optional: A longer description of what the ApiStar should do. + description = """ + NOTE: This method can only be used by Royalnet Admins. + """ + + # The HTTP methods that can be used with this ApiStar. + methods = ["GET", "POST"] + # You can disambiguate between methods using the `data.method` variable. + + # The HTTP path this ApiStar should bind to. + path = "/api/example/echo/v1" + + # Does this method require any auth? + # Only for documentation purposes, it doesn't do any check on it's own. + requires_auth = True + # To authenticate an user through their token, use the `await data.user()` method. + # If the user isn't logged in, the method authomatically returns 403 Forbidden, unless `rcae.ForbiddenError` + # is caught. + + # A dict of paramenters accepted by this method, with a description of their purpose. + parameters = { + "echo": "What should the method return? " + "(Optional: if nothing is passed, the ApiStar will return the username of the caller.)", + "error": "Should the method return a sample error?" + } + # You can access parameters by using `data` as a dict with the parameter name as key. + # If a missing parameter is accessed, a `rcae.MissingParameterError` will be raised, which will lead to a + # 400 Bad Request error if not caught. + + # The autodoc categories this ApiStar should fall in. + tags = ["example"] + + # The actual method called when the ApiStar received a HTTP request. + # It must return a JSON-compatible object, such as a str, a int, a float, a list, a dict or None. + async def api(self, data: rca.ApiData) -> ru.JSON: + + # If "true" is passed as the "error" parameter in the query string... + if data["error"] == "true": + # ...return an example error + raise Exception("Example error! Everything works as intended.") + # If the "error" parameter is missing, the ApiStar will respond with 400 Bad Request + + # Ensure the user is logged in + user = await data.user() + # Check if the user has the role "admin" + if "admin" not in user.roles: + raise Exception("Only admins can call this method!") + + # Get the value of the "echo" parameter, without raising an exception if it doesn't exist + echo = data.get("echo") + + if echo is None: + # Find the username of the logged in user + # user is a SQLAlchemy ORM object generated from the Users table defined in `royalnet.backpack.tables.users` + echo = user.username + + # Return a 200 OK successful response containing the value of the echo variable and the HTTP method used + return {"echo": echo, "method_used": data.method} diff --git a/examplepack/stars/example.py b/examplepack/stars/example.py new file mode 100644 index 00000000..6b044cea --- /dev/null +++ b/examplepack/stars/example.py @@ -0,0 +1,11 @@ +from starlette.requests import Request +from starlette.responses import * +from royalnet.constellation import * +from royalnet.utils import * + + +class ExampleStar(PageStar): + path = "/example" + + async def page(self, request: Request) -> JSONResponse: + return HTMLResponse("""