# Ideas for improvement:
## ToDo
- [ ] Clarity in terms of API IDs
- [ ] Clean up unnecessary APIs
- [ ] Documentation of the code itself (helpful comments before functions)
- [ ] Enforce with Super Linter.
- [ ] Pydantic/Serialization
## API:
Basic idea is to try to follow a good practice when creating an api:
- use `nouns` to represent resources (not verbs)
- use `plural` name for collections
- use `forward slash (/)` to indicate hierarchical relationships
### PRs
- https://github.com/oakestra/oakestra/issues/333
[Important Discussion](https://github.com/oakestra/oakestra/issues/333#issuecomment-2129165662) and following
### Issues found:
**Common issues**:
1. A lot of blueprints that are bit confusing.
2. Mutliple endpoints for single and multiple resources, for example their is an endpoint for 'application' & 'applications'.
3. uri not making sense, for instance /applications/<userid>
**Applications Blueprint:**
- response of several endpoints have the `_id` as an object.
- Response is double json. First the result is dumbed as json then the @response decorator is json encoding it again.
- has a uri in the following form: `/api/applications/<userid>`. This does not make sense for couple of reasons:
1. userid is redundant, this information can be retrieved from auth token.
2. its natural to think that the id coming next after the applications uri is an applicationId and not something else.
**Deployment & Service blueprint:**
- deployment blueprint uses an endpoint called `/serivce` - singular. Should be plural and why is it called a service and not job.
- Then there is a `service_blueprint` which is conlicting in naming with `deployment_blueprint`, has endpoints for `/serivce` & `/serivces`. Also appid is part of the uri `/services/<appid>`.
_The uri could be transformed into: `/applications/<appid>/services/<serviceid>`_
**Scheduling Blueprint:**
- endpoint: `/deploy` is a verb and not a resource.
## Types++
Secure & proper type handing improve comprehension, extendability, fault-tollerance, serializations, and more.
A few ideas based on this [discussion thread](https://github.com/oakestra/oakestra/issues/333#issuecomment-2136805097).
- Use & enforce Python [Type-hints](https://docs.python.org/3/library/typing.html)
- Use dedicated libraries and their objects to encapsulate and handle things in a more structured and controlled way. E.g.:
- For [HTTP Statuses](https://docs.python.org/3/library/http.html#http-status-codes):
- `http.HTTPStatus.OK` instead of `"200"`
- [Pathlib](https://docs.python.org/3/library/pathlib.html) for paths
- `pathlib.Path("my/path/smth")` instead of `"my/path/smth"`
- Enums instead of strings
- There are huge benefits of using enums: e.g. you can look up the enum and see immediately what other options exists, no accidental typos, etc.
- Use custom types and aliases (place in shared libraries to avoid code duplication)
## Unification & duplication elimination via custom shared libraries
We have a lot of duplicated code which can and should be unified and placed into libraries that can then be installed and called anywhere. (E.g. See the upcoming [oakestra-utils library](https://github.com/oakestra/oakestra/pull/337/files#diff-7d51d48167f7bd1f39a9422cf72ffa19f7bb2cb15a553f51df4e43945f2c5022), or the public [FLOps library flops-utils](https://github.com/Malyuk-A/tmp_flops_utils))
## Simplification
If something seems to be too verbose and to complex there usually tends to be way to refactor it in a much simpler way. One big part places how large/mighty files/classes/functions are. I think that we have several huge files/classes (especially in the NodeEngine) that if we manage to break them up into more digestable parts, we can reuse many similarities and make development easier.
## Pydantic / better "Serialization"
Context: When developing the isolated FLOps Plugin I took the liberty to take a detailed look at what libraries the main Oakestra repo uses and what alternatives are out there. I created this [comparison](https://www.notion.so/oakestra-team/Flask-Extensions-related-python-packages-8da79f12a57740ceb94f0c3241429e80?pvs=4) during that time. One major change is the swap to [pydantic](https://docs.pydantic.dev/latest/). So far I am having a blast using it and can only recommend it. Feel free to checkout the [FLOps Plugin repo](https://github.com/oakestra/plugin-FLOps?tab=readme-ov-file) once it is public as reference. A few noteworthy things that pydantic enabled me to do:
- When receiving an SLA I do not need to parse it via complex custom logic (see the Oakestra SLA handling) but I simply take the received json and pipe it into the corresponding pydantic object. If the SLA should be faulty pydantic will through a serialization error with concrete hints on what went wrong, i.e. pydantic handles the entire parsing/serialization part, no custom logic necessary.
- IMO how Oakestra uses complex and lengthy custom logic for (de)serializing data from the received API data towards usable python objects down to the DB and back up again can and should be able to be simplified massively via pydantic. E.g. The DB tables and actual python objects/classes have diverging/multiple names and a flat hierarchy:

#### Compare this to a FLOps project
This is how the user SLA looks like that is send to the FLOps API:

Here is the corresponding Pydantic class for FLOps projects: (Note: This is not the entire class)

All that is necessary to convert the json SLA to the python object is this: `flops_project = FLOpsProject.model_validate(request_data)`
If you combine it with a few nice automatic calls to pymongo you get this DB table & nested entry automatically.

I repeat this all is possible without lengthy and complex custom (de)serialization/DB logic. Special serialization behaviour/needs can easily be added to the pydantic class directly.