DEV Community

Cover image for Shopping List: my way to learn stuff
Alberto Talone
Alberto Talone

Posted on

Shopping List: my way to learn stuff

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.



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

    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);
            })
        );
    }
Enter fullscreen mode Exit fullscreen mode



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.

    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);
      })
    ))
  }
Enter fullscreen mode Exit fullscreen mode

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

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(); }
        );
      }
    );
  }
Enter fullscreen mode Exit fullscreen mode

Remember to take a look, if you haven't at RxJS documentation

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.

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:

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();
}
Enter fullscreen mode Exit fullscreen mode

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:

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 ====================================");
}
Enter fullscreen mode Exit fullscreen mode

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 ====================================
Enter fullscreen mode Exit fullscreen mode

Special credits to my friend Alessandro Valenti for Angular advices.

For extra code informations here is the link to the repository.

What are your opinions/advices?

Top comments (0)