diff --git a/MycroForge.CLI/Commands/MycroForge.Generate.Service.cs b/MycroForge.CLI/Commands/MycroForge.Generate.Service.cs
index 0a75bf2..898ddcc 100644
--- a/MycroForge.CLI/Commands/MycroForge.Generate.Service.cs
+++ b/MycroForge.CLI/Commands/MycroForge.Generate.Service.cs
@@ -36,7 +36,7 @@ public partial class MycroForge
"",
"\tasync def list(self, value: str) -> List[Entity]:",
"\t\tasync with async_session() as session:",
- "\t\t\t# stmt = select(User).where(Entity.value == value)",
+ "\t\t\t# stmt = select(Entity).where(Entity.value == value)",
"\t\t\t# results = (await session.scalars(stmt)).all()",
"\t\t\t# return results",
"\t\t\tpass",
diff --git a/MycroForge.Core/MycroForge.Core.csproj b/MycroForge.Core/MycroForge.Core.csproj
new file mode 100644
index 0000000..3a63532
--- /dev/null
+++ b/MycroForge.Core/MycroForge.Core.csproj
@@ -0,0 +1,9 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+
+
diff --git a/MycroForge.sln b/MycroForge.sln
index 8c590de..be1bffa 100644
--- a/MycroForge.sln
+++ b/MycroForge.sln
@@ -2,6 +2,8 @@
Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MycroForge.CLI", "MycroForge.CLI\MycroForge.CLI.csproj", "{27EFB015-AFC3-4046-8D9A-DD5C5D3B35E0}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MycroForge.Core", "MycroForge.Core\MycroForge.Core.csproj", "{CFF8BD4E-520D-4319-BA80-3F49B5F493BA}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -12,5 +14,9 @@ Global
{27EFB015-AFC3-4046-8D9A-DD5C5D3B35E0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{27EFB015-AFC3-4046-8D9A-DD5C5D3B35E0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{27EFB015-AFC3-4046-8D9A-DD5C5D3B35E0}.Release|Any CPU.Build.0 = Release|Any CPU
+ {CFF8BD4E-520D-4319-BA80-3F49B5F493BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CFF8BD4E-520D-4319-BA80-3F49B5F493BA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CFF8BD4E-520D-4319-BA80-3F49B5F493BA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CFF8BD4E-520D-4319-BA80-3F49B5F493BA}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
diff --git a/README.md b/README.md
index 3bbe090..529bb77 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,6 @@
### Dependencies
+- git
- Docker
- bash (/bin/bash)
- Python 3.10.2 (/usr/bin/python3)
diff --git a/docs/README.md b/docs/README.md
index df60f81..fffe18d 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -61,29 +61,32 @@ Let's go through these one by one.
### .git
-The project is automatically initialised with git by the `m4g init` command.
-If you don't want to use git you can run `m4g init --without git`.
+The `m4g init` command will initialize new projects with git by default.
+If you don't want to use git you can pass the option `--without git` to `m4g init`.
### .venv
-To promote isolation of Python dependencies, the project is initialized with a virtual environment by default.
+To promote isolation of Python dependencies, new projects are initialized with a virtual environment by default.
+TODO: This is a good section to introduce the `m4g hydrate` command.
### api/routers/hello.py
-This file defines a basic example router, which is imported and mapped in `main.py`.
+This file defines a basic example router, which is imported and mapped in `main.py`. This router is just an example and
+can be removed or modified at you discretion.
### db/engine/async_session.py
-This file defines the `async_session` function, which can be used to asynchronously connect to a database.
+This file defines the `async_session` function, which can be used to open an asynchronous session to a database.
### db/entities/entity_base.py
-This is the automatically generated base class that all database entities will inherit from.
-SQLAlchemy requires a base entity class to properly function.
+This file contains an automatically generated entity base class that derives from the DeclarativeBase.
+All entities must inherit from this class, so that SQLAlchemy & alembic can track them. The entities directory is also
+where all newly generated entities will be stored.
-### db/entities/versions
+### db/versions
-This is where the generated database migrations will be kept.
+This is where the generated database migrations will be stored.
### db/README
@@ -92,10 +95,10 @@ This README file is automatically generated by the alembic init command.
### db/env.py
This is the database environment file that is used by alembic to interact with the database.
-If you take a close look at the imports, you'll see that the file has been modified to include the entity metadata,
-i.e. `EntityBase.metadata` and you should also notice that the `DbSettings` class is used to get the connectionstring.
-Any time you generate a new database entity or create a many-to-many relation between two entities, this file will also
-be modified.
+If you take a closer look at the imports, you'll see that the file has been modified to assign `EntityBase.metadata` to
+a variable called `target_metadata`, this will allow alembic to track changes in your entities. You'll also find that
+the `DbSettings` class is used to get the connectionstring. Any time you generate a new database entity, or create a
+many-to-many relation between two entities, this file will also be modified to include the generated classes.
### db/script.py.mako
@@ -103,15 +106,16 @@ This file is automatically generated by the alembic init command.
### db/settings.py
-This file defines a class that is responsible for retrieving the connectionstring.
+This file defines the `DbSettings` class, that is responsible for retrieving the database connectionstring.
+You will probably want to modify this class to retrieve the connectionstring from a secret manager at some point.
### .gitignore
-The default .gitignore file/
+The default .gitignore file that is generated by the `m4g init` command. Modify this file at your discretion.
### alembic.ini
-The alembic ini file
+This file is automatically generated by the alembic init command.
### db.docker-compose.yml
@@ -119,13 +123,19 @@ A docker compose file for running a database locally.
### m4g.json
-The m4g config file, this file contains some configs that are used by the CLI,
-for example the ports to map to the API and database.
+This file contains some configs that are used by the CLI, for example the ports to map to the API and database.
### main.py
-The entrypoint for the application.
+The entrypoint for the application. When generating entities, many-to-many relations or routers, this file will be
+modified to include the generated files.
### requirements.txt
The requirements file containing the Python dependencies.
+TODO: introduce the `m4g install` & `m4g uninstall` commands.
+
+
+## Scripting
+
+TODO: Dedicate a section to scripting
\ No newline at end of file
diff --git a/docs/todo-example/README.md b/docs/todo-example/README.md
index 432b390..f94b80b 100644
--- a/docs/todo-example/README.md
+++ b/docs/todo-example/README.md
@@ -1,54 +1,123 @@
# Quick tutorial
-Summary: We're gonna build a todo app to demonstrate how the `m4g` command line tool works.
-TODO: Supplement this section.
+We're going to build a simple todo app, to demonstrate the capabilities of the MycroForge CLI.
+After this tutorial, you should have a solid foundation to start exploring and using MycroForge to develop your
+projects.
+
+## General notes
+
+The commands in this tutorial assume that you're running them from a MycroForge root directory.
## Initialize the project
-Run `m4g init todo-example` to initialize a new project.
-
-Open the newly created project by running `code todo-example`, make sure you have `vscode` on you machine before running
-this command. If you prefer using another editor, then manually open the generated folder.
+Open a terminal and `cd` into the directory where your project should be created.
+Run `m4g init todo-app` to initialize a new project and open the newly created project by running `code todo-app`.
+Make sure you have `vscode` on you machine before running this command. If you prefer using another editor, then
+manually open the generated `todo-app` folder.
## Setup the database
-### Start the database
+Our todo app needs to keep track of todos, so it needs a storage mechanism of some sorts. A database should be one
+of the first things, if not THE first thing, that comes to mind. Luckily, MycroForge provides you with a locally hosted
+database for you project out of the box. This setup, is powered by docker compose and can be examined by opening the
+`db.docker-compose.yml` file in the project. Follow along to learn how to use the docker based database when developing
+locally.
+
+### Run the database
+
+The first step is to start the database, you can do this by running the following command in a terminal.
`m4g db run`
+This command starts the services defined in the `db.docker-compose.yml` file.
+You can verify that the services are up by running `docker container ls`. If everything went well, then the previous
+command should output the service names defined in `db.docker-compose.yml`.
+
+If you go to [PhpMyAdmin (i.e. http://localhost:5051)](http://localhost:5051), you should now be able to login with the
+following credentials.
+- user: root
+- pass: password
+
+When you're done developing, you can shut down the local database by running `m4g db stop`
+
+
### Create the entities
-`m4g db generate entity Tag -c "description:str:String(255)"`
+Now that the database is running, we can start to create our entities. Run the commands below to create the `Todo` &
+`Tag` entities.
-`m4g db generate entity Todo -c "description:str:String(255)" -c "is_done:bool:Boolean()"`
+`m4g db generate entity Tag --column "description:str:String(255)"`
-### Define a many to many relation between Todo & Tag
+`m4g db generate entity Todo --column "description:str:String(255)" -c "is_done:bool:Boolean()"`
+
+After running these commands, you should find the generated entities in the `db/entities` folder of your project.
+You should also see that the `main.py` & `db/env.py` files have been modified to include the newly generated entity.
+
+For more information about the `m4g db generate entity` command, you can run `m4g db generate entity -?`.
+
+### Define a many-to-many relation between Todo & Tag
+
+To allow for relations between `Todo` & `Tag`, we'll define a many-to-many relation between the two entities.
+This relation makes sense, because a `Todo` can have many `Tags` and a `Tag` could belong to many `Todos`.
+You can generate this relation by running the following command from you terminal.
+Creating a one-to-many relation would also make sense, but for the purpose of demonstration we're going to demonstrate
+the many-to-many relation, because this one is the most complex, since it requires an additional mapping to be included
+in the database schema.
`m4g db link many Todo --to-many Tag`
+After running this command you should see that both the `Todo` and `Tag` entities now have a new field referencing the
+a `List` containing instances of the other entity.
+
+For more information about the `m4g db link` command try running `m4g db link -?`. Note that you can do the same thing
+for all sub commands, so if you want to know more about `m4g db link many` you can simply run `m4g db link many -?` to
+examine the command. The same is true for all the other commands as well.
+
### Generate the migration
+Now that we've generated our entities, it's time to generate a migration that will apply these changes in the database.
+Generate the initial migration by running the following command.
+
`m4g db generate migration initial_migration`
+After running this command, you should see the new migration in the `db/version` directory.
+
### Apply the migration
+The last step for the database setup is to actually apply the new migration to the database. This can be done by running
+the following command.
+
`m4g db migrate`
-If you inspect the database in [PhpMyAdmin](http://localhost:5051), you should now see a populated schema.
-
-To stop the database, you can run `m4g db stop`
+After running this command, you should now see a populated schema when visiting [PhpMyAdmin](http://localhost:5051).
+If for whatever reason you want to undo the last migration, you can simply run `m4g db rollback`.
## Setup the API
### Generate CRUD for Tag & Todo
+Our API should provide use with basic endpoint to manage the `Todo` & `Tag` entities, i.e. CRUD functionality.
+Writing this code can be boring, since it's pretty much boilerplate with some custom additions sprinkled here and there.
+Fortunately, MycroForge can generate a good chunk of this boring code on your behalf. Run the following commands to
+generate CRUD functionality for the `Todo` & `Tag` classes.
+
`m4g api generate crud Tag`
`m4g api generate crud Todo`
+After running this command you should see that the `api/requests`,`api/routers` & `api/services` now contain the
+relevant classes need to support the generated CRUD functionality. This could should be relatively straightforward, so
+we won't dive into it, but feel free to take a break and explore what the generated code actually does. Another thing to
+note, is that the generated routers are also automatically included in `main.py`.
+
### Modify the generated Todo request classes
-Modify `CreateTodoRequest` in `api/requests/create_todo_request.py`, you might need to import `List` from `typing`
+Since we have a many-to-many relationship between `Todo` & `Tag`, the generated CRUD functionality isn't quite ready
+yet. We need to be able to specify which `Tags` to add to a `Todo` when creating or updating it.
+To do this, we will allow for a `tag_ids` field in both the `CreateTodoRequest` & the `UpdateTodoRequest`.
+This field will contain the ids of the `Tags` that are associated with a `Todo`.
+
+Modify `CreateTodoRequest` in `api/requests/create_todo_request.py`, you might need to import `List` from `typing`.
```python
# Before
@@ -63,7 +132,7 @@ class CreateTodoRequest(BaseModel):
tag_ids: Optional[List[int]] = []
```
-Modify `UpdateTodoRequest` in `api/requests/update_todo_request.py`, you might need to import `List` from `typing`
+Modify `UpdateTodoRequest` in `api/requests/update_todo_request.py`, you might need to import `List` from `typing`.
```python
# Before
@@ -81,6 +150,8 @@ class UpdateTodoRequest(BaseModel):
### Modify generated TodoService
+The `TodoService` will also need to be updated to accomodate the management of `tag_ids`.
+
Add the following imports in `api/services/todo_service.py`.
```python
@@ -203,3 +274,6 @@ Modify `TodoService.update`
await session.commit()
return True
```
+
+At this point, the app should be ready to test.
+TODO: Elaborate!
\ No newline at end of file