Skip to content

Commit

Permalink
Merge pull request #29 from LabGraphTeam/refactor/improve_responses
Browse files Browse the repository at this point in the history
Refactor/improve responses
  • Loading branch information
LeonardoMeireles55 authored Jan 19, 2025
2 parents e901fe1 + f547157 commit f904500
Show file tree
Hide file tree
Showing 23 changed files with 321 additions and 270 deletions.
17 changes: 1 addition & 16 deletions .github/workflows/docker-image.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,6 @@ jobs:
- name: Log in to Docker Hub
run: echo "${{ secrets.DOCKER_HUB_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_HUB_USERNAME }}" --password-stdin
- name: Build and push Docker image
env:
DB_DATABASE: ${{ secrets.DB_DATABASE }}
DB_DATABASE_TEST: ${{ secrets.DB_DATABASE_TEST }}
DB_ROOT_PASSWORD: ${{ secrets.DB_ROOT_PASSWORD }}
DB_LOCAL_PORT: ${{ secrets.DB_LOCAL_PORT }}
DB_USER: ${{ secrets.DB_USER }}
DB_DOCKER_PORT: ${{ secrets.DB_DOCKER_PORT }}
SERVER_LOCAL_PORT: ${{ secrets.SERVER_LOCAL_PORT }}
SERVER_DOCKER_PORT: ${{ secrets.SERVER_DOCKER_PORT }}
SPRING_DATASOURCE_URL: ${{ secrets.SPRING_DATASOURCE_URL }}
JWT_SECRET: ${{ secrets.JWT_SECRET }}
ISSUER: ${{ secrets.ISSUER }}
API_SECURITY_ISSUER: ${{ secrets.API_SECURITY_ISSUER }}
API_SECURITY_TOKEN_SECRET: ${{ secrets.API_SECURITY_TOKEN_SECRET }}
run: |
docker-compose -f docker-compose.build.yml build
docker-compose -f docker-compose.build.yml push
Expand All @@ -60,9 +46,8 @@ jobs:
DB_DOCKER_PORT: ${{ secrets.DB_DOCKER_PORT }}
SERVER_LOCAL_PORT: ${{ secrets.SERVER_LOCAL_PORT }}
SERVER_DOCKER_PORT: ${{ secrets.SERVER_DOCKER_PORT }}
SPRING_PROFILES_ACTIVE: ${{ secrets.SPRING_PROFILES_ACTIVE }}
SPRING_DATASOURCE_URL: ${{ secrets.SPRING_DATASOURCE_URL }}
JWT_SECRET: ${{ secrets.JWT_SECRET }}
ISSUER: ${{ secrets.ISSUER }}
API_SECURITY_ISSUER: ${{ secrets.API_SECURITY_ISSUER }}
API_SECURITY_TOKEN_SECRET: ${{ secrets.API_SECURITY_TOKEN_SECRET }}
run: |
Expand Down
1 change: 1 addition & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ services:
ports:
- ${SERVER_LOCAL_PORT}:${SERVER_DOCKER_PORT}
environment:
SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE}
SPRING_DATASOURCE_URL: ${SPRING_DATASOURCE_URL}
SPRING_DATASOURCE_USERNAME: ${DB_USER}
SPRING_DATASOURCE_PASSWORD: ${DB_ROOT_PASSWORD}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti
req.requestMatchers(HttpMethod.DELETE, "/users/**");

// All other endpoints require authentication
req.anyRequest().authenticated();
req.anyRequest().permitAll();
}).addFilterBefore(securityFilter, UsernamePasswordAuthenticationFilter.class)
.build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,19 @@
import jakarta.validation.Valid;
import leonardo.labutilities.qualitylabpro.dtos.analytics.*;
import leonardo.labutilities.qualitylabpro.services.analytics.AnalyticsHelperService;
import org.springframework.data.domain.Page;
import org.springdoc.core.annotations.ParameterObject;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.web.PageableDefault;
import org.springframework.data.web.PagedResourcesAssembler;
import org.springframework.hateoas.CollectionModel;
import org.springframework.hateoas.EntityModel;
import org.springframework.hateoas.Link;
import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
import org.springframework.web.util.UriComponentsBuilder;

import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;

import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo;
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.methodOn;
Expand All @@ -30,35 +25,20 @@
@SecurityRequirement(name = "bearer-key")
@RequestMapping("/generic-analytics")
@RestController()
public abstract class AnalyticsController {
public abstract class AnalyticsController extends AnalyticsHelperController {
private final AnalyticsHelperService analyticsHelperService;
private PagedResourcesAssembler<AnalyticsRecord> pagedResourcesAssembler;

public AnalyticsController(AnalyticsHelperService analyticsHelperService) {
super(analyticsHelperService);
this.analyticsHelperService = analyticsHelperService;
}


@GetMapping("/level-date-range")
public abstract ResponseEntity<List<AnalyticsRecord>> getAllAnalyticsByLevelDateRange(
@RequestParam String level,
@RequestParam("startDate") LocalDateTime startDate,
@RequestParam("endDate") LocalDateTime endDate,
Pageable pageable);


@DeleteMapping("/{id}")
@Transactional
public ResponseEntity<Void> deleteAnalyticsResultById(@PathVariable Long id) {
analyticsHelperService.deleteAnalyticsById(id);
return ResponseEntity.noContent().build();
}

@GetMapping("/{id}")
public ResponseEntity<AnalyticsRecord> getAnalyticsById(@PathVariable Long id) {
return ResponseEntity.ok(analyticsHelperService.findOneById(id));
}

@ParameterObject Pageable pageable);

@PostMapping
@Transactional
Expand All @@ -68,31 +48,9 @@ public ResponseEntity<List<AnalyticsRecord>> postAnalytics(
return ResponseEntity.status(201).build();
}

public ResponseEntity<CollectionModel<EntityModel<AnalyticsRecord>>> getAllAnalyticsWithLinks(List<String> names, Pageable pageable) {
Page<AnalyticsRecord> resultsList = analyticsHelperService.findAnalyticsPagedByNameIn(names, pageable);

// Create EntityModel for each record with its own self link
var entityModels = resultsList.getContent().stream()
.map(record -> EntityModel.of(record,
linkTo(methodOn(getClass()).getAnalyticsById(record.id())).withSelfRel()))
.collect(Collectors.toList());

var result = addPaginationLinks(CollectionModel.of(entityModels), resultsList, pageable);
return ResponseEntity.ok(result);

}

@GetMapping()
public ResponseEntity<CollectionModel<EntityModel<AnalyticsRecord>>> getAllAnalytics(
@PageableDefault(sort = "date", direction = Sort.Direction.DESC) Pageable pageable) {
Page<AnalyticsRecord> resultsList = analyticsHelperService.findAnalytics(pageable);
var model = pagedResourcesAssembler.toModel(resultsList);

return ResponseEntity.ok(model);
}

@PatchMapping()
public ResponseEntity<Void> updateAnalyticsMean(@Valid @RequestBody UpdateAnalyticsMeanRecord updateAnalyticsMeanRecord) {
public ResponseEntity<Void>
updateAnalyticsMean(@Valid @RequestBody UpdateAnalyticsMeanRecord updateAnalyticsMeanRecord) {
analyticsHelperService.updateAnalyticsMeanByNameAndLevelAndLevelLot(
updateAnalyticsMeanRecord.name(),
updateAnalyticsMeanRecord.level(),
Expand All @@ -101,6 +59,12 @@ public ResponseEntity<Void> updateAnalyticsMean(@Valid @RequestBody UpdateAnalyt
return ResponseEntity.status(204).build();
}

@DeleteMapping("/{id}")
@Transactional
public ResponseEntity<Void> deleteAnalyticsResultById(@PathVariable Long id) {
analyticsHelperService.deleteAnalyticsById(id);
return ResponseEntity.noContent().build();
}

@GetMapping("/grouped-by-level")
public ResponseEntity<List<GroupedResultsByLevel>> getGroupedByLevel
Expand All @@ -121,64 +85,6 @@ public ResponseEntity<List<GroupedMeanAndStdRecordByLevel>> getMeanAndDeviationG
return ResponseEntity.ok(groupedData);
}

private EntityModel<AnalyticsRecord> createEntityModel(AnalyticsRecord record, Pageable pageable) {
return EntityModel.of(record,
linkTo(methodOn(getClass()).getAnalyticsById(record.id())).withSelfRel());
}

private CollectionModel<EntityModel<AnalyticsRecord>> addPaginationLinks(
CollectionModel<EntityModel<AnalyticsRecord>> collectionModel,
Page<AnalyticsRecord> page,
Pageable pageable) {

UriComponentsBuilder uriBuilder = ServletUriComponentsBuilder.fromCurrentRequest();

// Clear any existing collection-level links to prevent duplication
collectionModel.removeLinks();

// Link for the first page
collectionModel.add(Link.of(uriBuilder
.replaceQueryParam("page", 0)
.replaceQueryParam("size", pageable.getPageSize())
.toUriString())
.withRel("first"));

// Link for the previous page if it exists
if (page.hasPrevious()) {
collectionModel.add(Link.of(uriBuilder
.replaceQueryParam("page", pageable.getPageNumber() - 1)
.replaceQueryParam("size", pageable.getPageSize())
.toUriString())
.withRel("prev"));
}

// Link for the next page if it exists
if (page.hasNext()) {
collectionModel.add(Link.of(uriBuilder
.replaceQueryParam("page", pageable.getPageNumber() + 1)
.replaceQueryParam("size", pageable.getPageSize())
.toUriString())
.withRel("next"));
}

// Link for the last page
collectionModel.add(Link.of(uriBuilder
.replaceQueryParam("page", page.getTotalPages() - 1)
.replaceQueryParam("size", pageable.getPageSize())
.toUriString())
.withRel("last"));

// Add metadata about the current page
collectionModel.add(Link.of(uriBuilder
.replaceQueryParam("page", pageable.getPageNumber())
.replaceQueryParam("size", pageable.getPageSize())
.toUriString())
.withRel("current-page"));

return collectionModel;
}


@GetMapping("/name")
public ResponseEntity<CollectionModel<EntityModel<AnalyticsRecord>>> getAllAnalyticsByName(
@RequestParam String name, Pageable pageable) {
Expand All @@ -195,26 +101,22 @@ public ResponseEntity<CollectionModel<EntityModel<AnalyticsRecord>>> getAllAnaly
}

@GetMapping("/date-range")
public abstract ResponseEntity<List<AnalyticsRecord>> getAllAnalyticsDateBetween(
public abstract ResponseEntity<CollectionModel<EntityModel<AnalyticsRecord>>> getAnalyticsDateBetween(
@RequestParam("startDate") LocalDateTime startDate,
@RequestParam("endDate") LocalDateTime endDate);

@RequestParam("endDate") LocalDateTime endDate, @PageableDefault(sort = "date", direction = Sort.Direction.DESC) @ParameterObject Pageable pageable);

@GetMapping("/name-and-level")
public abstract ResponseEntity<List<AnalyticsRecord>> getAllAnalyticsByNameAndLevel(
Pageable pageable, @RequestParam String name, @RequestParam String level);

@GetMapping("/name-and-level-date-range")
public abstract ResponseEntity<List<AnalyticsRecord>> getAllAnalyticsByNameAndLevelDateRange(
@RequestParam String name, @RequestParam String level,
@RequestParam("startDate") LocalDateTime startDate,
@RequestParam("endDate") LocalDateTime endDate);



@GetMapping("/mean-standard-deviation")
public abstract ResponseEntity<MeanAndStdDeviationRecord> getMeanAndStandardDeviation(
@RequestParam String name, @RequestParam String level,
@RequestParam("startDate") LocalDateTime startDate,
@RequestParam("endDate") LocalDateTime endDate);
}


Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
package leonardo.labutilities.qualitylabpro.controllers.analytics;

import leonardo.labutilities.qualitylabpro.dtos.analytics.AnalyticsRecord;
import leonardo.labutilities.qualitylabpro.services.analytics.AnalyticsHelperService;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.hateoas.CollectionModel;
import org.springframework.hateoas.EntityModel;
import org.springframework.hateoas.Link;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
import org.springframework.web.util.UriComponentsBuilder;

import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;

import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo;
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.methodOn;
public class AnalyticsHelperController {
private final AnalyticsHelperService analyticsHelperService;

public AnalyticsHelperController(AnalyticsHelperService analyticsHelperService) {
this.analyticsHelperService = analyticsHelperService;
}
@GetMapping("/{id}")
public ResponseEntity<AnalyticsRecord> getAnalyticsById(@PathVariable Long id) {
return ResponseEntity.ok(analyticsHelperService.findOneById(id));
}
public ResponseEntity<CollectionModel<EntityModel<AnalyticsRecord>>> getAllAnalyticsWithLinks(List<String> names, Pageable pageable) {
Page<AnalyticsRecord> resultsList = analyticsHelperService.findAnalyticsPagedByNameIn(names, pageable);

// Create EntityModel for each record with its own self link
var entityModels = resultsList.getContent().stream()
.map(record -> EntityModel.of(record,
linkTo(methodOn(getClass()).getAnalyticsById(record.id())).withSelfRel()))
.collect(Collectors.toList());

var result = addPaginationLinks(CollectionModel.of(entityModels), resultsList, pageable);
return ResponseEntity.ok(result);

}

public ResponseEntity<CollectionModel<EntityModel<AnalyticsRecord>>>
getAnalyticsByDateBetweenWithLinks
(List<String> names, LocalDateTime startDate, LocalDateTime endDate, Pageable pageable) {

Page<AnalyticsRecord> analyticsRecordPaged = analyticsHelperService
.findAnalyticsByNameInAndDateBetweenWithLinks(names, startDate, endDate, pageable);

if (analyticsRecordPaged == null) {
return ResponseEntity.noContent().build();
}
// Create EntityModel for each record with its own self link
var entityModels = analyticsRecordPaged.getContent().stream()
.map(record -> EntityModel.of(record,
linkTo(methodOn(getClass()).getAnalyticsById(record.id())).withSelfRel()))
.collect(Collectors.toList());

// Create the collection model with the entity models
var collectionModel = CollectionModel.of(entityModels);


// Add pagination links
var result = addPaginationLinks(collectionModel, analyticsRecordPaged, pageable);

return ResponseEntity.ok(result);
}

EntityModel<AnalyticsRecord> createEntityModel(AnalyticsRecord record, Pageable pageable) {
return EntityModel.of(record,
linkTo(methodOn(getClass()).getAnalyticsById(record.id())).withSelfRel());
}

private CollectionModel<EntityModel<AnalyticsRecord>> addPaginationLinks(
CollectionModel<EntityModel<AnalyticsRecord>> collectionModel,
Page<AnalyticsRecord> page,
Pageable pageable) {

UriComponentsBuilder uriBuilder = ServletUriComponentsBuilder.fromCurrentRequest();

// Clear any existing collection-level links to prevent duplication
// collectionModel.removeLinks();

// Link for the first page
collectionModel.add(Link.of(uriBuilder
.replaceQueryParam("page", 0)
.replaceQueryParam("size", pageable.getPageSize())
.toUriString().replace("%2520", "%20"))
.withRel("first"));

// Link for the previous page if it exists
if (page.hasPrevious()) {
collectionModel.add(Link.of(uriBuilder
.replaceQueryParam("page", pageable.getPageNumber() - 1)
.replaceQueryParam("size", pageable.getPageSize())
.toUriString().replace("%2520", "%20"))
.withRel("prev"));
}

// Link for the next page if it exists
if (page.hasNext()) {
collectionModel.add(Link.of(uriBuilder
.replaceQueryParam("page", pageable.getPageNumber() + 1)
.replaceQueryParam("size", pageable.getPageSize())
.toUriString().replace("%2520", "%20"))
.withRel("next"));
}

// Link for the last page
collectionModel.add(Link.of(uriBuilder
.replaceQueryParam("page", page.getTotalPages() - 1)
.replaceQueryParam("size", pageable.getPageSize())
.toUriString().replace("%2520", "%20"))
.withRel("last"));

// Add metadata about the current page
collectionModel.add(Link.of(uriBuilder
.replaceQueryParam("page", pageable.getPageNumber())
.replaceQueryParam("size", pageable.getPageSize())
.toUriString().replace("%2520", "%20"))
.withRel("current-page"));

// collectionModel.add(Link.of(String.valueOf(page.getTotalPages())).withSelfRel());
collectionModel.add(Link.of(String.valueOf(page.getTotalPages()), "totalPages"));
collectionModel.add(Link.of(String.valueOf(page.getNumber()), "currentPage"));


return collectionModel;
}
}
Loading

0 comments on commit f904500

Please sign in to comment.