Shopping List: my way to learn stuff
Tags: showdev, java, ionic, angular
Published: 2020-11-14 21:20:25

# Shopping list #

Hi devs, I want to show you a simple app I have create for playing with Ionic 5 and Java Spring.

## Let me introduce you to **Shopping List** ##

Shopping List is a simple app for manage my stuff to buy from grocery to clothes and other stuff when I go out.

But why create your own app when there are plenty of them? Just for learn some stuff, get my hands dirty and stress a bit my Raspberry :D

Let's take a look to a few key elements for the app and the server.


## APP ##

The app is created with Ionic 5 and Angular 10.

The idea, behind this project, was to write some clean code and play a bit with RxJS operators, interceptors and guards.

<br>
The HTTP interceptor is necessary because I have to inject the OAUTH2 Token for all the request to the server.

```typescript
    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {

        const currentUser = this.authService.currentUserValue;

        if (currentUser != null) {
            const token: string = currentUser.access_token;
            if (token) {
                request = request.clone({ headers: request.headers.set('Authorization', 'Bearer ' + token) });
            }
        }

        if (!request.headers.has('Content-Type')) {
            request = request.clone({ headers: request.headers.set('Content-Type', 'application/json') });
        }

        request = request.clone({ headers: request.headers.set('Accept', 'application/json') });

        return next.handle(request).pipe(
            map((event: HttpEvent<any>) => {
                return event;
            }),
            catchError((error: HttpErrorResponse) => {
                // TODO: Add 401 Handler
                return throwError(error);
            })
        );
    }
```

<br>
I used the RxJS operators for manipulate all the Observables used for calling the server.

I really used and loved a lot the *catchError* for handling server error response and throw them were I have subscribed to this observables.

Here is an example about an Observable I have created for retrieve the full list of shops.

```typescript
    getShops(): Observable<IShopsResponse> {

    return this.http.get<IShopsResponse>(this.url + 'getAll').pipe((
      catchError((error: HttpErrorResponse) => {
        let err: IShopsResponse = null;
        err.status = error.status;
        err.message = error.error;
        return throwError(err);
      })
    ))
  }
```

Here is where I subscribe to the Observable and how I manage the error, and what to show to the user.

```typescript
getAllShops() {
    this.shopService.getShops().subscribe(
      (response: IShopsResponse) => {
        this.list = response.data;
      },
      (error) => {
        let title = this.langService.getString('ALERT.error');
        let msg = this.langService.getString('ERROR.retrieve_list');
        this.alertService.showAlert(title, msg).then(
          (alertBox) => { alertBox.present(); }
        );
      }
    );
  }
```

Remember to take a look, if you haven't at [RxJS documentation](https://www.learnrxjs.io/learn-rxjs/operators)


## SERVER ##

The server is created using Spring Boot framework.

The basic idea was to create a simple server with a basic authentication for the REST using OAuth2 following this [tutorial](https://www.devglan.com/spring-security/spring-boot-security-oauth2-example).

After following this tutorial I decided to take a look at Spring Boot potentials and I find out the possibility of retrieve user informations without doing a ping-pong with ids and other private informations using *SecurityContextHolder*.

Here is a snipping of the function for the current user:

```Java
public User getCurrentUser(){
  Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
  if ((authentication instanceof AnonymousAuthenticationToken)) {
    return null;
  }

  String email = authentication.getName();
  Optional<User> optUser = userRepo.findByEmail(email);
  if(optUser.isEmpty()){
      return null;
  }
  
  return optUser.get();
}
```

Another element that I decide to add was a logger for request and response, a very usefull way to analyze future server errors.
For achieve this I had to create a specific class that extends *HandlerInterceptorAdapter* and with the some extra manipulation I was able to get my logs.

Here are the methods I created for the logs:

```java
public void writeRequestPayloadAudit(ResettableStreamHttpServletRequest wrappedRequest) {
  try {
    String requestHeaders = getRawHeaders(wrappedRequest);
    String requestBody = org.apache.commons.io.IOUtils.toString(wrappedRequest.getReader());

    LOGGER.info("=================================== Request Start ===================================");
    LOGGER.info("Request Method: " + wrappedRequest.getMethod());
    LOGGER.info("Request URL: " + wrappedRequest.getRequestURI());
    LOGGER.info("Request Headers:" + requestHeaders.replace("\n", ","));
    LOGGER.info("Request Body:" + requestBody.replace("\n", ""));
    LOGGER.info("==================================== Request End ====================================");

  } catch (Exception e) {
    LOGGER.error("Exception Request" + e.getMessage());
  }
}

public void writeResponsePayloadAudit(ResettableStreamHttpServletResponse wrappedResponse) {
  String rawHeaders = getRawHeaders(wrappedResponse);
  
  LOGGER.info("=================================== Response Start ===================================");
  LOGGER.info("Response Status: " + wrappedResponse.getStatus());
  LOGGER.info("Response Headers:" + rawHeaders.replace("\n", ","));

  byte[] data = new byte[wrappedResponse.rawData.size()];
  for (int i = 0; i < data.length; i++) {
          data[i] = (byte) wrappedResponse.rawData.get(i);
  }

  String responseBody = new String(data);
  LOGGER.info("Response body:" + responseBody);
  LOGGER.info("==================================== Response End ====================================");
}
```

And here is how my logs looks like:
```
2020-10-30 18:32:35,583 INFO com.bebetos.shoppinglist.interceptors.LoggerInterceptor [http-nio-8080-exec-3] =================================== Request Start ===================================
2020-10-30 18:32:35,584 INFO com.bebetos.shoppinglist.interceptors.LoggerInterceptor [http-nio-8080-exec-3] Request Method: GET
2020-10-30 18:32:35,585 INFO com.bebetos.shoppinglist.interceptors.LoggerInterceptor [http-nio-8080-exec-3] Request Headers:content-type:application/json,authorization:Bearer 6de79b7b-03bd-4e05-a8f8-af7f618d2fbc,user-agent:PostmanRuntime/7.26.5,accept:*/*,postman-token:18287157-4a9d-483f-9031-62cc2b3aa5dd,host:localhost:8080,accept-encoding:gzip, deflate, br,connection:keep-alive,
2020-10-30 18:32:35,585 INFO com.bebetos.shoppinglist.interceptors.LoggerInterceptor [http-nio-8080-exec-3] Request body:
2020-10-30 18:32:35,586 INFO com.bebetos.shoppinglist.interceptors.LoggerInterceptor [http-nio-8080-exec-3] ==================================== Request End ====================================
2020-10-30 18:32:35,647 INFO com.bebetos.shoppinglist.interceptors.LoggerInterceptor [http-nio-8080-exec-3] =================================== Response Start ===================================
2020-10-30 18:32:35,648 INFO com.bebetos.shoppinglist.interceptors.LoggerInterceptor [http-nio-8080-exec-3] Response Status: 200
2020-10-30 18:32:35,648 INFO com.bebetos.shoppinglist.interceptors.LoggerInterceptor [http-nio-8080-exec-3] Response Headers:Vary:Origin,Vary:Origin,Vary:Origin,
2020-10-30 18:32:35,649 INFO com.bebetos.shoppinglist.interceptors.LoggerInterceptor [http-nio-8080-exec-3] Response body:{"status":200,"message":"","data":[[{"id":1,"name":"Supermercato 1","createdAt":"2020-10-29T22:44:33","updatedAt":"2020-10-29T22:44:33","deleted":0,"total":0,"bought":0,"items":[]}],[{"id":2,"name":"Supermercato 2","createdAt":"2020-10-29T22:44:41","updatedAt":"2020-10-29T22:44:41","deleted":0,"total":0,"bought":0,"items":[]}],[{"id":13,"name":"Supermercato 3","createdAt":"2020-10-29T22:49:06","updatedAt":"2020-10-29T22:49:06","deleted":0,"total":0,"bought":0,"items":[]}]]}
2020-10-30 18:32:35,649 INFO com.bebetos.shoppinglist.interceptors.LoggerInterceptor [http-nio-8080-exec-3] ==================================== Response End ====================================
```

Special credits to my friend Alessandro Valenti for Angular advices.

For extra code informations here is the link to the [repository](https://github.com/BeBeToS92/my-shopping-list).


What are your opinions/advices?