package cc.nfscan.server.controller.fe;
import cc.nfscan.server.controller.be.AbstractController;
import cc.nfscan.server.controller.exclusion.ResultProcessStatusExclusion;
import cc.nfscan.server.controller.response.ResultProcessAuthResponse;
import cc.nfscan.server.controller.response.ResultProcessStatusResponse;
import cc.nfscan.server.controller.response.ResultResponse;
import cc.nfscan.server.dao.OCRTransactionDAO;
import cc.nfscan.server.dao.TaxReceiptDAO;
import cc.nfscan.server.domain.OCRTransaction;
import cc.nfscan.server.domain.TaxReceipt;
import cc.nfscan.server.service.cloudwatch.ProcessTaxReceiptCloudWatchService;
import cc.nfscan.server.service.s3.TaxReceiptS3Upload;
import cc.nfscan.server.service.sqs.impl.ProcessTaxReceiptSQSService;
import cc.nfscan.server.service.sqs.model.ProcessInQueueModel;
import cc.nfscan.server.utils.Constants;
import cc.nfscan.server.utils.SignatureUtils;
import cc.nfscan.server.utils.StringUtils;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import org.apache.commons.codec.binary.Base64;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import java.io.File;
import java.io.FileOutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;
import static cc.nfscan.server.utils.StringUtils.removeNonNumeric;
/**
* Controller in charge of handling TaxReceipt related requests on frontend interface
*
* @author Paulo Miguel Almeida <a href="http://github.com/PauloMigAlmeida">@PauloMigAlmeida</a>
*
*/
@Controller
public class TaxReceiptMobileController extends AbstractController {
/**
* TaxReceiptDAO DAO object
*/
@Autowired
private TaxReceiptDAO taxReceiptDAO;
/**
* OCRTransactionDAO DAO object
*/
@Autowired
private OCRTransactionDAO ocrTransactionDAO;
/**
* TaxReceiptS3Upload object
*/
@Autowired
private TaxReceiptS3Upload s3Upload;
/**
* SignatureUtils object
*/
@Autowired
private SignatureUtils signatureUtils;
/**
* ProcessTaxReceiptSQSService object
*/
@Autowired
private ProcessTaxReceiptSQSService processTaxReceiptSQSService;
/**
* ProcessTaxReceiptCloudWatchService object
*/
@Autowired
private ProcessTaxReceiptCloudWatchService processTaxReceiptCloudWatchService;
/**
* Upload an image encoded in base64 to a AWS bucket
*
* @param base64Image - a string containing a image base 64 encoded
* @return the AWS S3 path where the image has been saved
* @throws Exception
*/
private String uploadBase64FileToBucket(String base64Image) throws Exception {
Base64 base64 = new Base64();
byte[] bytes = base64.decode(base64Image.getBytes());
File tempFile = File.createTempFile("taxReceipt", ".jpg");
//Write TaxReceipt to a file
FileOutputStream fileOutputStream = new FileOutputStream(tempFile);
fileOutputStream.write(bytes);
fileOutputStream.flush();
fileOutputStream.close();
String s3OutputFileName = uploadFileToBucket(tempFile);
//Delete when it's done
tempFile.deleteOnExit();
return s3OutputFileName;
}
/**
* Upload a file to a AWS bucket
*
* @param file - the file you want to upload
* @return the AWS S3 path where the image has been saved
* @throws Exception
*/
private String uploadFileToBucket(File file) throws Exception {
//Upload to AWS S3
String s3OutputFileName = String.format("%s.jpg", UUID.randomUUID().toString());
s3Upload.startUpload(s3OutputFileName, file);
return s3OutputFileName;
}
/**
* Generates a signature and a transaction Id that must be processed and used on all other requests to fend off requests that aren't coming from our app
*
* @return a JSON containing the ResultProcessAuthResponse properties
* @see ResultProcessAuthResponse
*/
@RequestMapping(value = "/fe/taxreceipts/process/auth", method = RequestMethod.POST)
public ResponseEntity<String> auth() {
HttpHeaders responseHeaders = super.createBasicHttpHeaderResponse(APPLICATION_JSON);
Gson gson = new Gson();
ResultResponse resultResponse;
try {
String signature = signatureUtils.generateSignature();
Date currDate = new Date();
OCRTransaction ocrTransaction = new OCRTransaction(signature, currDate, new SimpleDateFormat(Constants.DATE_FORMAT).format(currDate));
ocrTransactionDAO.save(ocrTransaction);
resultResponse = new ResultProcessAuthResponse(true, ocrTransaction.getId(), ocrTransaction.getSignature());
} catch (Exception ex) {
ex.printStackTrace();
resultResponse = new ResultResponse(false);
}
return new ResponseEntity<>(gson.toJson(resultResponse), responseHeaders, HttpStatus.OK);
}
/**
* Uploads a Tax Receipt file encoded in Base64 and starts processing this file looking for meaningful values. Note this is
* a asynchronous process which means you aren't getting the values right away. You must periodically check if the process identified
* by the transactionId has been finished on the {@link TaxReceiptMobileController#check(String, String) check} method
*
* @param base64Image a string containing a file encoded in base64 (use JPEG compression for lighter sizes)
* @param transactionId a string containing the transactionId generated in the {@link TaxReceiptMobileController#auth() auth} method
* @param counterSignature a string of the signature generated in the auth method processed by you mobile app
* @return a JSON containing the ResultResponse properties
* @see ResultResponse
*/
@RequestMapping(value = "/fe/taxreceipts/process/start", method = RequestMethod.POST)
public ResponseEntity<String> start(@RequestParam(value = "base64Image", required = true) final String base64Image,
@RequestParam(value = "transactionId", required = true) final String transactionId,
@RequestParam(value = "counterSignature", required = true) final String counterSignature) {
HttpHeaders responseHeaders = super.createBasicHttpHeaderResponse(APPLICATION_JSON);
Gson gson = new Gson();
ResultResponse resultResponse;
try {
String s3OutputFileName = uploadBase64FileToBucket(base64Image);
OCRTransaction ocrTransaction = ocrTransactionDAO.findById(new OCRTransaction(transactionId));
Assert.notNull(ocrTransaction);
Assert.isTrue(signatureUtils.validateCounterSignature(ocrTransaction.getSignature(), counterSignature));
ocrTransaction.setS3Object(s3OutputFileName);
ocrTransaction.setProcessed(false);
ocrTransactionDAO.save(ocrTransaction);
processTaxReceiptSQSService.sendMessage(new ProcessInQueueModel(ocrTransaction.getId(), ocrTransaction.getS3Object()));
processTaxReceiptCloudWatchService.putMetricData(processTaxReceiptCloudWatchService.getMessagesSentMetric(), 1.0);
resultResponse = new ResultResponse(true);
} catch (Exception ex) {
ex.printStackTrace();
resultResponse = new ResultResponse(false);
}
return new ResponseEntity<>(gson.toJson(resultResponse), responseHeaders, HttpStatus.OK);
}
/**
* Checks whether or not the OCR process has been finished
*
* @param transactionId a string containing the transactionId generated in the {@link TaxReceiptMobileController#auth() auth} method
* @param counterSignature a string of the signature generated in the auth method processed by you mobile app
* @return a JSON containing the ResultProcessStatusResponse properties
* @see ResultProcessStatusResponse
*/
@RequestMapping(value = "/fe/taxreceipts/process/check", method = RequestMethod.POST)
public ResponseEntity<String> check(@RequestParam(value = "transactionId", required = true) final String transactionId,
@RequestParam(value = "counterSignature", required = true) final String counterSignature) {
HttpHeaders responseHeaders = super.createBasicHttpHeaderResponse(APPLICATION_JSON);
Gson gson = new GsonBuilder().setDateFormat(Constants.DATE_FORMAT).setExclusionStrategies(new ResultProcessStatusExclusion()).create();
ResultResponse resultResponse;
try {
OCRTransaction ocrTransaction = ocrTransactionDAO.findById(new OCRTransaction(transactionId));
Assert.notNull(ocrTransaction);
Assert.isTrue(signatureUtils.validateCounterSignature(ocrTransaction.getSignature(), counterSignature));
resultResponse = new ResultProcessStatusResponse(true, ocrTransaction);
} catch (Exception ex) {
ex.printStackTrace();
resultResponse = new ResultResponse(false);
}
return new ResponseEntity<>(gson.toJson(resultResponse), responseHeaders, HttpStatus.OK);
}
/**
* Donates the tax receipt after the user has verified all meaningful values and fixed if needed
*
* @param cnpj a valid National Registry of Legal Entities
* @param date a date in the form dd/MM/yyyy
* @param coo a valid COO (Operation Counter)
* @param total a total value of the receipt
* @param transactionId a string containing the transactionId generated in the {@link TaxReceiptMobileController#auth() auth} method
* @return a JSON containing the ResultResponse properties
* @see ResultResponse
* @see <a href="http://en.wikipedia.org/wiki/CNPJ">http://en.wikipedia.org/wiki/CNPJ</a>
*/
@RequestMapping(value = "/fe/taxreceipts/process/donate")
public ResponseEntity<String> processDonate(
@RequestParam(value = "cnpj") String cnpj
, @RequestParam(value = "date") Date date
, @RequestParam(value = "coo") String coo
, @RequestParam(value = "total") double total
, @RequestParam(value = "transactionId") String transactionId) {
HttpHeaders responseHeaders = super.createBasicHttpHeaderResponse(APPLICATION_JSON);
Gson gson = new Gson();
ResultResponse resultResponse;
try {
Assert.isTrue(StringUtils.validateCNPJ(cnpj));
Assert.notNull(coo);
Assert.isTrue(StringUtils.isNumeric(coo));
Assert.isTrue(total != 0);
Assert.notNull(transactionId);
Assert.isTrue(!transactionId.equals(""));
OCRTransaction ocrTransaction = ocrTransactionDAO.findById(new OCRTransaction(transactionId));
Assert.notNull(ocrTransaction);
//Save to database
TaxReceipt taxReceipt = new TaxReceipt();
taxReceipt.setCnpj(cnpj.replaceAll("[^\\d]", ""));
taxReceipt.setDate(date);
taxReceipt.setCoo(coo);
taxReceipt.setTotal(total);
taxReceipt.setS3Object(ocrTransaction.getS3Object());
taxReceipt.setDateInsertion(new Date());
taxReceiptDAO.save(taxReceipt);
ocrTransactionDAO.remove(ocrTransaction);
resultResponse = new ResultResponse(true);
} catch (Exception ex) {
ex.printStackTrace();
resultResponse = new ResultResponse(false);
}
return new ResponseEntity<>(gson.toJson(resultResponse), responseHeaders, HttpStatus.OK);
}
/**
* Donates the tax receipt without the user submit a picture of receipt.
*
* @param cnpj a valid National Registry of Legal Entities
* @param date a date in the form dd/MM/yyyy
* @param coo a valid COO (Operation Counter)
* @param total a total value of the receipt
* @return a JSON containing the ResultResponse properties
* @see ResultResponse
* @see <a href="http://en.wikipedia.org/wiki/CNPJ">http://en.wikipedia.org/wiki/CNPJ</a>
*/
@RequestMapping(value = "/fe/taxreceipts/manual/donate")
public ResponseEntity<String> manualDonate(
@RequestParam(value = "cnpj") String cnpj
, @RequestParam(value = "date") Date date
, @RequestParam(value = "coo") String coo
, @RequestParam(value = "total") double total) {
HttpHeaders responseHeaders = super.createBasicHttpHeaderResponse(APPLICATION_JSON);
Gson gson = new Gson();
ResultResponse resultResponse;
try {
Assert.isTrue(StringUtils.validateCNPJ(cnpj));
Assert.notNull(coo);
Assert.isTrue(StringUtils.isNumeric(coo));
Assert.isTrue(total > 0);
//Save to database
TaxReceipt taxReceipt = new TaxReceipt();
taxReceipt.setCnpj(removeNonNumeric(cnpj));
taxReceipt.setDate(date);
taxReceipt.setCoo(coo);
taxReceipt.setTotal(total);
taxReceipt.setDateInsertion(new Date());
taxReceiptDAO.save(taxReceipt);
resultResponse = new ResultResponse(true);
} catch (Exception ex) {
ex.printStackTrace();
resultResponse = new ResultResponse(false);
}
return new ResponseEntity<>(gson.toJson(resultResponse), responseHeaders, HttpStatus.OK);
}
}