BoxRestController extends RestController { @PostMapping(value = "/{boxId}/scan") public @ResponseBody ScanResultsDto getBoxScan(@PathVariable(required = true) int boxId, @RequestBody(required = true) ScanRequest requestData) { try { BoxScanner boxScanner = boxScanners.get(requestData.getScannerName()); if (boxScanner == null) { throw new RestException("Invalid scanner specified", Status.BAD_REQUEST); } BoxScan scan = boxScanner.getScan(); if (scan == null) { throw new RestException("The scanner did not detect a box!", Status.CONFLICT); } Map<String, String> barcodesByPosition = scan.getBarcodesMap(); Set<String> validBarcodes = barcodesByPosition.values().stream() .filter(barcode -> isRealBarcode(scan, barcode)).collect(Collectors.toSet()); Map<String, BoxableView> boxablesByBarcode = boxService.getViewsFromBarcodeList(validBarcodes).stream() .collect(Collectors.toMap(BoxableView::getIdentificationBarcode, Function.identity())); List<BoxableDto> items = barcodesByPosition.entrySet().stream() .filter(entry -> isRealBarcode(scan, entry.getValue()) && boxablesByBarcode.containsKey(entry.getValue())) .map(entry -> { BoxableDto dto = Dtos.asDto(boxablesByBarcode.get(entry.getValue())); dto.setCoordinates(entry.getKey()); return dto; }).collect(Collectors.toList()); List<ErrorMessage> errors = new ArrayList<>(); barcodesByPosition.entrySet().stream() .filter(entry -> isRealBarcode(scan, entry.getValue()) && !boxablesByBarcode.containsKey(entry.getValue())).map(entry -> { ErrorMessage dto = new ErrorMessage(); dto.setCoordinates(entry.getKey()); dto.setMessage("Barcode " + entry.getValue() + " not found."); return dto; }).forEachOrdered(errors::add); scan.getReadErrorPositions().stream().map(position -> { ErrorMessage dto = new ErrorMessage(); dto.setCoordinates(position); dto.setMessage("Cannot read tube."); return dto; }).forEachOrdered(errors::add); long totalBarcodes = barcodesByPosition.values().stream().filter(barcode -> isRealBarcode(scan, barcode)).count(); if (validBarcodes.size() != totalBarcodes) { ErrorMessage dto = new ErrorMessage(); dto.message = "Duplicate barcodes detected!"; errors.add(dto); } List<DiffMessage> diffs = new ArrayList<>(); Box box; Map<String, BoxableView> boxables; try { box = boxService.get(boxId); boxables = boxService.getBoxContents(boxId).stream().collect(Collectors.toMap(BoxableView::getBoxPosition, Function.identity())); } catch (IOException e) { throw new RestException("Cannot get the Box: " + e.getMessage(), Status.INTERNAL_SERVER_ERROR); } if (box.getSize().getRows() != scan.getRowCount() || box.getSize().getColumns() != scan.getColumnCount()) throw new RestException(String.format("Box is %d×%d, but scanner detected %d×%d.", box.getSize().getRows(), box.getSize().getColumns(), scan.getRowCount(), scan.getColumnCount()), Status.BAD_REQUEST); box.getSize().positionStream().map(position -> { BoxableView originalItem = boxables.containsKey(position) ? boxables.get(position) : null; BoxableView newItem = barcodesByPosition.containsKey(position) && boxablesByBarcode.containsKey(barcodesByPosition.get(position)) ? boxablesByBarcode.get(barcodesByPosition.get(position)) : null; if (originalItem != null && newItem != null && !newItem.getIdentificationBarcode().equals(originalItem.getIdentificationBarcode())) { DiffMessage dto = new DiffMessage(); dto.action = "changed"; dto.modified = Dtos.asDto(newItem); dto.original = Dtos.asDto(originalItem); return dto; } else if (originalItem != null && newItem == null) { DiffMessage dto = new DiffMessage(); dto.action = "removed"; dto.original = Dtos.asDto(originalItem); return dto; } else if (originalItem == null && newItem != null) { DiffMessage dto = new DiffMessage(); dto.action = "added"; dto.modified = Dtos.asDto(newItem); return dto; } else { return null; } }).filter(Objects::nonNull); ScanResultsDto scanResults = new ScanResultsDto(); scanResults.setEmptyPositions(barcodesByPosition.entrySet().stream() .filter(entry -> !isRealBarcode(scan, entry.getValue())) .map(Entry::getKey) .collect(Collectors.toList())); scanResults.setItems(items); scanResults.setErrors(errors); scanResults.setDiffs(diffs); scanResults.setRows(scan.getRowCount()); scanResults.setColumns(scan.getColumnCount()); return scanResults; } catch (IntegrationException | IOException e) { throw new RestException("Error scanning box: " + e.getMessage(), Status.INTERNAL_SERVER_ERROR); } }  void setBoxService(BoxService boxService); void setBoxScanners(Map<String, BoxScanner> boxScanners); @GetMapping(value = "/dt", produces = "application/json") @ResponseBody DataTablesResponseDto<BoxDto> dataTable(HttpServletRequest request); @GetMapping(value = "/dt/use/{id}", produces = "application/json") @ResponseBody DataTablesResponseDto<BoxDto> dataTableByUse(@PathVariable("id") Long id, HttpServletRequest request); @PutMapping(value = "/{boxId}/position/{position}", consumes = { "application/json" }, produces = { "application/json" }) @ResponseBody BoxDto setPosition(@PathVariable("boxId") Long boxId, @PathVariable("position") String position,
      @RequestParam("entity") String entity); @RequestMapping(value = "/search") @ResponseBody List<BoxDto> search(@RequestParam("q") String search); @RequestMapping(value = "/search/partial") @ResponseBody List<BoxDto> partialSearch(@RequestParam("q") String search, @RequestParam("b") boolean onlyMatchBeginning); @GetMapping(value = "/{boxId}/spreadsheet") @ResponseBody JSONObject createSpreadsheet(@PathVariable("boxId") Long boxId); @PostMapping(value = "/prepare-scan") @ResponseStatus(HttpStatus.NO_CONTENT) void prepareBoxScanner(@RequestBody(required = true) ScannerPreparationRequest requestData); @PostMapping(value = "/{boxId}/scan") @ResponseBody ScanResultsDto getBoxScan(@PathVariable(required = true) int boxId,
      @RequestBody(required = true) ScanRequest requestData); @DeleteMapping("/{boxId}/positions/{position}") @ResponseBody BoxDto removeSingleItem(@PathVariable long boxId, @PathVariable String position); @PostMapping("/{boxId}/positions/{position}/discard") @ResponseBody BoxDto discardSingleItem(@PathVariable long boxId, @PathVariable String position); @PostMapping("/{boxId}/bulk-remove") @ResponseBody BoxDto removeMultipleItems(@PathVariable long boxId, @RequestBody List<String> positions); @PostMapping("/{boxId}/bulk-discard") @ResponseBody BoxDto discardMultipleItems(@PathVariable long boxId, @RequestBody List<String> positions); @PostMapping(value = "/{boxId}/discard-all") @ResponseStatus(HttpStatus.NO_CONTENT) void discardEntireBox(@PathVariable long boxId); @PostMapping(value = "/{boxId}/bulk-update") @ResponseBody BoxDto bulkUpdatePositions(@PathVariable long boxId, @RequestBody List<BulkUpdateRequestItem> items); @PostMapping(value = "/{boxId}/setlocation") @ResponseStatus(HttpStatus.ACCEPTED) @ResponseBody void setBoxLocation(@PathVariable(name = "boxId", required = true) long boxId,
      @RequestParam("storageId") long storageId); @PostMapping(produces = "application/json") @ResponseBody BoxDto createBox(@RequestBody BoxDto dto); @PutMapping(value = "/{boxId}", produces = "application/json") @ResponseBody BoxDto updateBox(@PathVariable long boxId, @RequestBody BoxDto dto); @PostMapping(value = "/bulk-delete") @ResponseBody @ResponseStatus(HttpStatus.NO_CONTENT) void bulkDelete(@RequestBody(required = true) List<Long> ids); @PostMapping("/{boxId}/positions/fill-by-pattern") @ResponseStatus(HttpStatus.NO_CONTENT) void recreateBoxFromPrefix(@PathVariable long boxId, @RequestParam(name = "prefix", required = true) String prefix,
      @RequestParam(name = "suffix", required = true) String suffix); @GetMapping("/{boxId}/fragmentAnalyser") HttpEntity<byte[]> createFragmentAnalyserSheet(@PathVariable("boxId") Long boxId, HttpServletResponse response);  }
@Test public final void testGetBoxScanWithReadErrorsReturnsError() throws Exception { final long id = 1L; Box box = makeEmptyBox(); when(scan.getRowCount()).thenReturn(box.getSize().getRows()); when(scan.getColumnCount()).thenReturn(box.getSize().getColumns()); when(boxScanner.getScan()).thenReturn(scan); when(scan.getReadErrorPositions()).thenReturn(Arrays.asList("A01")); when(boxService.get(1L)).thenReturn(box); JSONObject json = new JSONObject(); json.put("boxId", id); ScanRequest request = new ScanRequest(); request.setScannerName(scannerName); assertTrue(sut.getBoxScan(1, request).getErrors().size() > 0); }