From afece502c6538beeeec76beae075c11b95e2b1ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20Oliva?= Date: Mon, 5 Jun 2023 22:07:57 -0600 Subject: New post! Not so weekly essay. --- _posts/2023-05-29-weekly-essay-5.md | 350 ++++++++++++++++++++++++++++++++++++ 1 file changed, 350 insertions(+) create mode 100644 _posts/2023-05-29-weekly-essay-5.md (limited to '_posts') diff --git a/_posts/2023-05-29-weekly-essay-5.md b/_posts/2023-05-29-weekly-essay-5.md new file mode 100644 index 0000000..1a8aee3 --- /dev/null +++ b/_posts/2023-05-29-weekly-essay-5.md @@ -0,0 +1,350 @@ +--- +title: To-Do App +categories: [Weekly Essay] +tags: [] +# math: true +--- + +After a long time since the last post, we're back! Let's try to explain our +latest project: _A To-Do App_. + +You can see the complete code on my personal Git page or in my GitHub account. + +| Personal Git Page | GitHub | +| :------------------------------------------------: | :--------------------------------------------------------: | +| [Front End](https://cgit.hisiste.com/ToDo-App-FE/) | [Front End](https://github.com/Hisiste/ToDo-App-Front-End) | +| [Back End](https://cgit.hisiste.com/ToDo-App-BE/) | [Back End](https://github.com/Hisiste/ToDo-App-Back-End) | + +## Goals and Requirements + +As the name implies, this project is about creating and managing to-dos. We have +the following functional requirements for this app: + +- [x] Create a "to-do" specifying the name, a priority, and possibly a due + date. +- [x] Ability to edit name, priority and due date for existing "to-do" tasks. + - Being able to specify a due date or clear the due date (because you may + not be interested in when to finish that "to-do"). +- [x] Being able to filter "to-dos" specifying the name (or part of the name), + the priority and if they are done/undone. +- [x] Being able to sort the "to-do’s" by priority and/or due date. + - For example, be able to sort items where their due date is soon and sort + them also by priority to see what tasks are more urgent or less urgent. +- [x] Mark "to-do’s" as done (clicking in a checkbox) or to undo a "to-do". + - The undone functionality is just there if there is a mistake. :D +- [x] Since it is possible that you can have a lot of "to-dos" there's the + need to paginate the list of "to-dos". +- [x] Ability to know, in average, the time between creation and done for all + "to-dos". This should be shown in general for all done "to-dos" and + also grouped by priority. + - This could be a metric to measure performance. + +The project was separated into two projects: The _Front End_ part and the _Back +End_ part. Each part has its own details and technologies. Let's discuss all of +them. + +### The Front End + +We'll follow this markup to design the app: + +![](/assets/posts_assets/2023-05-29-weekly-essay-5/UX-UI-markup.png) + +1. Search/Filtering Controls. +2. New To Do Button. This should open a modal to type the "to-do" data. +3. Priority column should show in the header the classic up and down arrows to + allow the user to sort. +4. Due date column should show in the header the classic up and own arrows to + allow the user to sort. +5. Action column to show actions (links/buttons) to allow the user to delete or + edit a "to-do". + - To Edit is ok to show a modal similar to the one to create a "to-do". +6. Pagination control. Showing the pages, its number and the next + and previous page is enough. +7. Area to show the metrics. + +We're restricted to use the following technologies for this project: + +- JavaScript. +- ReactJS. +- Redux. + +Neither of which I've used before. So this will be an interesting project. + +### The Back End + +This is further broken into two parts: the _model_ and the _API_. The _model_ is +the information we're storing for every to-do. The _API_ will be our connection +between the Front End and the Back End. Let's detail the requirements for each +part. + +#### Model + +Every to-do should have: + +- ID. This could be a number or string or a combination. Must be unique. +- Text (required). Max length is 120 chars. +- A due date (optional). +- Done/undone flag. +- A done date. When the "to-do" is marked as done this date is set. +- Priority (required). Options: High, Medium and Low. +- Creation date. + +#### API + +- A GET endpoint (/todos) to list "to-dos". + - **Include pagination. Pages should be of 10 elements.** +- Sort by priority and/or due date. +- Filter by: + - The name or part of the name. + - Done/Undone. + - Priority. +- A POST endpoint (/todos) to create "to-dos". + - Validations included. +- A PUT endpoint (/todos/{id}) to update the "to-do" name, due date + and/or priority. + - Validations included. +- A POST endpoint (/todos/{id}/done) to mark "to-do" as done. + - This should update the "done date" property. + - If "to-do" is already done nothing should happen (no error returned). +- A PUT endpoint (/todos/{id}/undone) to mark "to-do" as undone. + - If "to-do" is already undone nothing should happen. + - If "to-do" is done, this should clear the done date. + +Finally, we're restricted to using the following technologies: + +- Java. +- Maven. +- Spring Boot. + +And again, none of which I have experience. Oh, boy. + +## What I have learned + +A LOT. Not only have I learned enough of each technology to (barely) make a +functional To-Do App from almost scratch in less than 3 weeks, but we're +restricted to **NOT** use a database. At first, I thought "well, I don't know +anything about databases either, so this is something one technology less to +learn." Later I finally understood that this requirement: + +> No need to use a database by now, storing data could be in memory using Java +> Collections, and it is ok if data is lost when the application is +> shutdown. But they are asking you to design the persistent layer such that it +> will be somehow easy to switch from in-memory implementation to a database +> implementation. + +will cause me a lot of trouble. Well, not that much trouble, but things would +have been smoother if we're allowed to use databases. (Or at least that was my +conclusion.) + +So, without further ado, let's summarize everything I've learned during this +whole new project: + +### Front End + +To use [_Redux_](https://redux.js.org/), we have to understand key-concepts +like: + +Functional Programming : Decomposing a problem into a bunch of small and +reusable pieces. + +Higher Order Functions : A function that takes a function as an argument and/or +returns a function. + +Pure Functions : Where a function with the same arguments yield the same +outputs. No external behavior should alter the output. + +And little more. + +Redux was heavily used at first. Creating the store and developing the reducers +to store all to-dos was my first move. Not the best, but my first. I learned +about [Bootstrap](https://getbootstrap.com/) and used it to create the UX/UI. +Buttons, a table for the to-dos, containers with borders, and very messy **but +functional** forms for adding a new to-do, editing a to-do and filtering through +to-dos. Every time a reducer worked, I celebrated it. It wasn't very difficult +to make them work, but it was an achievement for me, a math student who +specialized solely on Python. + +I realized that JavaScript wasn't that much different from C++, but where +everything is a class and objects are mutable. ~~And when something failed, +`undefined` was returned instead of an error... Why?~~ + +[React](https://react.dev/) was a breeze of fresh air and something easy to +pick-up. It has its funny quirks like using `className` instead of `class`, the +style needs to be defined as a JSON object and not a string, or that inside a +loop, every new item needs to have a key, but they weren't serious problems. + +I always had Google opened (well, it was Bing, but you get the point). Every +time I was stuck or lost, I tried to Google it. StackOverflow was my savior most +of the time, but not always. Even _Googling_ can be seen as a skill. Let me tell +you a funny story on why I said this: + +Because I was too into Bootstrap, I searched for a date-picker input using +Bootstrap. After a long search, I stumbled upon a +[**Bootstrap date-picker**](https://github.com/uxsolutions/bootstrap-datepicker) +plugin developed by [**uxsolutions**](https://github.com/uxsolutions). I really +thought it was the only (actively developed) solution and I forced it into my +project. (See commit +[163398](https://cgit.hisiste.com/ToDo-App-FE/commit/?id=163398f68444d278eb83543ba9798a6441c8af9f) +([same but on GitHub](https://github.com/Hisiste/ToDo-App-Front-End/commit/163398f68444d278eb83543ba9798a6441c8af9f)).) +**TWO** days later and after having LOTS of troubles with `onChange`, I just +found out that I could have used HTML5's date input. By just doing + +```html + +``` + +ALL of my problems with date picking suddenly vanished. You can even see part of +my frustration on commit +[3d4a58](https://cgit.hisiste.com/ToDo-App-FE/commit/?id=3d4a589e5dc8f97a5fe94f5f8f42b97f2b6c7b52) +([GitHub link](https://github.com/Hisiste/ToDo-App-Front-End/commit/3d4a589e5dc8f97a5fe94f5f8f42b97f2b6c7b52)). +I attribute this problem to me trying to solve and forcing myself to solve +everything with Bootstrap. I thought (and still think) that it is a very cool +library, but it cannot do everything. + +### Back End + +Working on the Back End was something else. I felt a little more restricted +using Java than using JavaScript. But at the end, Spring Boot helped me in +simplifying the API creation. Like, a lot. I felt really happy and relieved when +my first API call worked instantly. It was just a "Hello World!", but after +seeing how simple it was to write it, I was calmer than when I started this +project. + +{: file="Main.java"} + +```java +@SpringBootApplication +@RestController +public class Main { + public static void main(String[] args) { + SpringApplication.run(Main.class, args); + } + + // GET request on page `http://localhost:9090/greet`. + // Returns the string "Hello world!". + @GetMapping("/greet") + public String greet() { + return "Hello world!"; + } +} +``` + +As simple as that! I was happy... Until the "database" came along. + +Apparently, after setting up a database and linking it with the back end, all I +had to do was: + +{: file="ToDosRepository.java"} + +```java +public interface ToDosRepository extends JpaRepository{} +``` + +`ToDosRepository` would have been our database variable, `ToDos` is our class +[model](#model), and `Integer` is the data type of our ID. The database would +have functions like `findAll`, `save`, `deleteById` and many more. But because +we're restricted to not use a database, WE HAD to implement those functions by +ourselves. So we had to do something like this: + +{: file="ToDosRepository.java"} + +```java +public class ToDosRepository implements JpaRepository { + List todos; + + // Constructor + public ToDosRepository() { + this.todos = new ArrayList<>(); + } + + // Return all to-dos. + @Override // <- We HAVE to write our own implementations. + public List findAll() { + return this.todos; + } + + // Sort and return all to-dos. + @Override + public List findAll(Sort sort) { + // Figure out how to use the Sort class. >:3 + } + + // We HAVE to override EVERY function that JpaRepository offers. Even if + // we won't need it. (You can always return `null` as a solution.) +} +``` + +Notice how instead of an _interface_, we have created a _class_, and instead of +_extending_ the **JpaRepository**, we're _implementing_ it. + +The "database" took me way longer than I've expected. But at the end I made it +work. Somehow. There were hiccups here and there, like when I tried to pass a +request body to a GET request, just to then find out that this is a bad +practice. At the end, there were no major "blockers" in this project. + +### Connecting both projects + +I HAD to run and implement this as fast as possible. Time was not on my side and +the connection wasn't as smooth as I wanted to. For this part, I used +[Axios](https://axios-http.com/) on the Front End. This library allowed me to +make HTTP requests to the Back End. These were my major takeaways from this: + +- Discovered about a `'Access-Control-Allow-Origin' header` problem and found + about CORS Policy.[^CORS] At the end the problem was fixed by making the + Back End (port 9090) allow requests from the Front End (port 8080). + + ```java + @SpringBootApplication + @RestController + @RequestMapping() + public class Main { + // Edit this origin and set where the Front End is allocated. + private static final String allowed_origin = "http://localhost:8080/"; + + // ... + + // Get all to dos. + @CrossOrigin(origins=allowed_origin) // <- Allow HTTP request. + @GetMapping("/todos") + public List getToDos() { + return toDosRepository.findAll(); + } + } + ``` + + Every request had to have the `@CrossOrigin(origins=allowed_origin)` line in + order for the Front End to successfully make the HTTP request. + +- Apparently the GET requests should not have a body.[^GET] Although `curl` + CAN send a body on GET requests, Axios doesn't support it. + +- Filters, sorting, pagination and more can be done pretty quickly on the Back + End. It is bad practice to do this stuff in the Front End. ~~There goes work + to waste. D:~~ + +- APIs are a GREAT way to connect projects, each written in their own + languages, having their own frameworks and their own databases. + +## Conclusion + +This project was a fun ride. Full of new technologies, new ways of creating +applications and turning theory into practice. I celebrated each personal +milestone, from deploying the whole app to writing my first API call. It's not +hard to write code in a new programming language, but it's hard to write clean +code. My app works (I hope), but the code is most likely a complete mess with +lots of bad practices that I don't know about. The only way to get better at it +is to **practice**. Keep practicing, learning and trying new and challenging +things. + +--- + +## Footnotes + +[^CORS]: + To know more about this issue, see commit + [5fccbc](https://cgit.hisiste.com/ToDo-App-BE/commit/src/main/java/com/encora?id=5fccbca45f7ad6837f46b4f5967c06c85e588e8c) + ([GitHub](https://github.com/Hisiste/ToDo-App-Back-End/commit/5fccbca45f7ad6837f46b4f5967c06c85e588e8c)) + +[^GET]: + See the following link for more information: + [[StackOverflow] HTTP GET with request body](https://stackoverflow.com/a/983458). -- cgit v1.2.3