CropTraitObservationController.java

/*
 * Copyright 2024 Global Crop Diversity Trust
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.gringlobal.api.v2.impl;

import com.querydsl.core.types.OrderSpecifier;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.apache.commons.lang3.ArrayUtils;
import org.genesys.filerepository.InvalidRepositoryFileDataException;
import org.genesys.filerepository.InvalidRepositoryPathException;
import org.gringlobal.api.ApiBaseController;
import org.gringlobal.api.FilteredPage;
import org.gringlobal.api.Pagination;
import org.gringlobal.api.model.CropTraitDTO;
import org.gringlobal.api.model.CropTraitObservationAttachDTO;
import org.gringlobal.api.model.CropTraitObservationDTO;
import org.gringlobal.api.model.CropTraitObservationDataAttachDTO;
import org.gringlobal.api.model.CropTraitObservationDataDTO;
import org.gringlobal.api.model.InventoryDTO;
import org.gringlobal.api.model.TranslatedCropTraitObservationDTO;
import org.gringlobal.api.v2.CRUDController;
import org.gringlobal.api.v2.FilteredCRUDController;
import org.gringlobal.api.v2.facade.CropTraitObservationApiService;
import org.gringlobal.api.v2.facade.CropTraitObservationAttachmentApiService;
import org.gringlobal.api.v2.facade.CropTraitObservationDataApiService;
import org.gringlobal.api.v2.facade.CropTraitObservationDataAttachmentApiService;
import org.gringlobal.custom.elasticsearch.SearchException;
import org.gringlobal.model.CropTraitObservation;
import org.gringlobal.model.CropTraitObservationAttach;
import org.gringlobal.model.CropTraitObservationData;
import org.gringlobal.model.CropTraitObservationDataAttach;
import org.gringlobal.model.QCropTrait;
import org.gringlobal.model.QCropTraitObservation;
import org.gringlobal.model.QCropTraitObservationData;
import org.gringlobal.model.QInventory;
import org.gringlobal.service.filter.CropTraitObservationDataFilter;
import org.gringlobal.service.filter.CropTraitObservationFilter;
import org.gringlobal.spring.MinifyingHttpMessageConverter;
import org.springdoc.api.annotations.ParameterObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.http.MediaType;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import javax.validation.Valid;
import java.io.IOException;

@RestController("cropTraitObservationApi2")
@RequestMapping(CropTraitObservationController.API_URL)
@PreAuthorize("isAuthenticated()")
@Tag(name = "CropTraitObservation")
public class CropTraitObservationController extends FilteredCRUDController<CropTraitObservationDTO, CropTraitObservation, CropTraitObservationApiService, CropTraitObservationFilter> {
	public static final String API_URL = ApiBaseController.APIv2_BASE + "/crop-trait/observations";

	@Autowired
	private CropTraitObservationAttachmentApiService cropTraitObservationAttachmentApiService;

	private OrderSpecifier<?>[] observationsDefaultSort() {
		return new OrderSpecifier[] { QCropTraitObservation.cropTraitObservation.inventory().id.asc() };
	}

	@PostMapping(value = "/attach/{cropTraitObservationId}", produces = { MediaType.APPLICATION_JSON_VALUE })
	@Operation(description = "Attach crop trait observation file", summary = "Attach file")
	public CropTraitObservationAttachDTO uploadFile(@PathVariable(name = "cropTraitObservationId") final Long cropTraitObservationId, @RequestPart(name = "file") final MultipartFile file,
		@RequestPart(name = "metadata") final CropTraitObservationAttachmentApiService.CropTraitObservationAttachmentRequestDTO metadata) throws InvalidRepositoryPathException, InvalidRepositoryFileDataException, IOException {

		return cropTraitObservationAttachmentApiService.uploadFile(cropTraitObservationId, file, metadata);
	}

	@DeleteMapping(value = "/attach/{cropTraitObservationId}/{attachmentId}", produces = { MediaType.APPLICATION_JSON_VALUE })
	@Operation(description = "Remove attached file", summary = "Remove file")
	public CropTraitObservationAttachDTO removeFile(@PathVariable(name = "cropTraitObservationId") final Long cropTraitObservationId, @PathVariable(name = "attachmentId") final Long attachmentId) {

		return cropTraitObservationAttachmentApiService.removeFile(cropTraitObservationId, attachmentId);
	}

	@RestController("cropTraitObservationAttachApi2")
	@RequestMapping(CropTraitObservationController.CropTraitObservationAttachController.API_URL)
	@PreAuthorize("isAuthenticated()")
	@Tag(name = "CropTraitObservation")
	public static class CropTraitObservationAttachController extends CRUDController<CropTraitObservationAttachDTO, CropTraitObservationAttach, CropTraitObservationAttachmentApiService> {
		/** The Constant API_URL. */
		public static final String API_URL = CropTraitObservationController.API_URL + "/attach/meta";

		@Override
		@Operation(operationId = "createCropTraitObservationAttach", description = "Create cropTraitObservationAttach", summary = "Create")
		public CropTraitObservationAttachDTO create(@RequestBody CropTraitObservationAttachDTO entity) {
			// Throws UnsupportedOperationException
			return super.create(entity);
		}

		@Override
		@Operation(operationId = "updateCropTraitObservationAttach", description = "Update an existing record", summary = "Update")
		public CropTraitObservationAttachDTO update(@RequestBody CropTraitObservationAttachDTO entity) {
			return super.update(entity);
		}

		@Override
		@Operation(operationId = "getCropTraitObservationAttach", description = "Get record by ID", summary = "Get")
		public CropTraitObservationAttachDTO get(@PathVariable long id) {
			return super.get(id);
		}

		@Override
		@Operation(operationId = "deleteCropTraitObservationAttach", description = "Delete existing record by ID", summary = "Delete")
		public CropTraitObservationAttachDTO remove(@PathVariable long id) {
			return super.remove(id);
		}
	}

	/**
	 * Get filtered translated list of CropTraitObservation.
	 *
	 * @param page the page
	 * @param filter the filter
	 * @return the page
	 * @throws SearchException
	 */
	@PostMapping(value = "/t/list", produces = { MediaType.APPLICATION_JSON_VALUE })
	@Operation(description = "Retrieve list of records matching the filter", summary = "List by filter")
	public FilteredPage<TranslatedCropTraitObservationDTO, CropTraitObservationFilter> listTranslated(@ParameterObject final Pagination page, @RequestBody(required = false) final CropTraitObservationFilter filter) throws SearchException, IOException {

		CropTraitObservationFilter normalizedFilter = shortFilterService.normalizeFilter(filter, filterType());
		Pageable pageable = ArrayUtils.isEmpty(page.getS()) ? page.toPageRequest(MAX_PAGE_SIZE, DEFAULT_PAGE_SIZE, defaultSort()) : page.toPageRequest(MAX_PAGE_SIZE, DEFAULT_PAGE_SIZE);
		return new FilteredPage<>(normalizedFilter, serviceFacade.listTranslated(filter, pageable));
	}

	/**
	 * Get distinct inventories from CropTraitObservations for selected method.
	 *
	 * @param methodId the id of method
	 * @param page the page
	 * @return the page of inventories
	 */
	@GetMapping(value = "/{methodId}/inventories", produces = { MediaType.APPLICATION_JSON_VALUE })
	public Page<InventoryDTO> getInventories(@ParameterObject final Pagination page, @PathVariable Long methodId) {

		Pageable pageable = ArrayUtils.isEmpty(page.getS()) ? page.toPageRequest(MAX_PAGE_SIZE, DEFAULT_PAGE_SIZE, QInventory.inventory.id.asc()) : page.toPageRequest(MAX_PAGE_SIZE, DEFAULT_PAGE_SIZE);
		return serviceFacade.getObservationInventoriesByMethod(methodId, pageable);
	}

	/**
	 * Get distinct crop trait from CropTraitObservations for selected method.
	 *
	 * @param methodId the id of method
	 * @param page the page
	 * @return the page of crop traits
	 */
	@GetMapping(value = "/{methodId}/traits", produces = { MediaType.APPLICATION_JSON_VALUE })
	public Page<CropTraitDTO> getTraits(@ParameterObject final Pagination page, @PathVariable Long methodId) {

		Pageable pageable = ArrayUtils.isEmpty(page.getS()) ? page.toPageRequest(MAX_PAGE_SIZE, DEFAULT_PAGE_SIZE, QCropTrait.cropTrait.id.asc()) : page.toPageRequest(MAX_PAGE_SIZE, DEFAULT_PAGE_SIZE);
		return serviceFacade.getObservationTraitsByMethod(methodId, pageable);
	}

	/**
	 * Ensure CropTraitObservations for given method, inventories and traits
	 *
	 * @param request the request
	 * @return the number of created observations
	 */
	@PostMapping(value = "/ensure")
	public int ensureObservations(@RequestBody @Valid CropTraitObservationApiService.EnsureObservationsRequest request) {

		return serviceFacade.ensureObservations(request);
	}

	@PostMapping(value = "/search", produces = { MinifyingHttpMessageConverter.APPLICATION_MINIFIED_JSON_VALUE })
	public CropTraitObservationApiService.FilteredObservations filterObservations(@ParameterObject final Pagination page,
		@RequestBody(required = true) final CropTraitObservationFilter filter) {

		Pageable pageable = ArrayUtils.isEmpty(page.getS()) ? page.toPageRequest(MAX_PAGE_SIZE, DEFAULT_PAGE_SIZE, observationsDefaultSort()) : page.toPageRequest(MAX_PAGE_SIZE, DEFAULT_PAGE_SIZE);
		return serviceFacade.search(filter, pageable);
	}

	@RestController("cropTraitObservationDataApi2")
	@RequestMapping(CropTraitObservationController.CropTraitObservationDataController.API_URL)
	@PreAuthorize("isAuthenticated()")
	@Tag(name = "CropTrait")
	public static class CropTraitObservationDataController extends FilteredCRUDController<CropTraitObservationDataDTO, CropTraitObservationData, CropTraitObservationDataApiService, CropTraitObservationDataFilter> {
		/** The Constant API_URL. */
		public static final String API_URL = CropTraitObservationController.API_URL + "-data";
		
		@Autowired
		private CropTraitObservationDataAttachmentApiService cropTraitObservationDataAttachmentApiService;

		private OrderSpecifier<?>[] observationsDataDefaultSort() {
			return new OrderSpecifier[] { QCropTraitObservationData.cropTraitObservationData.inventory().id.asc() };
		}

		@PostMapping(value = "/attach/{cropTraitObservationDataId}", produces = { MediaType.APPLICATION_JSON_VALUE })
		@Operation(description = "Attach crop trait observation data file", summary = "Attach file")
		public CropTraitObservationDataAttachDTO uploadFile(@PathVariable(name = "cropTraitObservationDataId") final Long cropTraitObservationDataId, @RequestPart(name = "file") final MultipartFile file,
			@RequestPart(name = "metadata") final CropTraitObservationDataAttachmentApiService.CropTraitObservationDataAttachmentRequestDTO metadata) throws InvalidRepositoryPathException, InvalidRepositoryFileDataException, IOException {

			return cropTraitObservationDataAttachmentApiService.uploadFile(cropTraitObservationDataId, file, metadata);
		}

		@DeleteMapping(value = "/attach/{cropTraitObservationDataId}/{attachmentId}", produces = { MediaType.APPLICATION_JSON_VALUE })
		@Operation(description = "Remove attached file", summary = "Remove file")
		public CropTraitObservationDataAttachDTO removeFile(@PathVariable(name = "cropTraitObservationDataId") final Long cropTraitObservationDataId, @PathVariable(name = "attachmentId") final Long attachmentId) {

			return cropTraitObservationDataAttachmentApiService.removeFile(cropTraitObservationDataId, attachmentId);
		}

		@RestController("cropTraitObservationDataAttachApi2")
		@RequestMapping(CropTraitObservationDataAttachController.API_URL)
		@PreAuthorize("isAuthenticated()")
		@Tag(name = "CropTraitObservationData")
		public static class CropTraitObservationDataAttachController extends CRUDController<CropTraitObservationDataAttachDTO, CropTraitObservationDataAttach, CropTraitObservationDataAttachmentApiService> {
			/** The Constant API_URL. */
			public static final String API_URL = CropTraitObservationDataController.API_URL + "/attach/meta";
						@Override
			@Operation(operationId = "createCropTraitObservationDataAttach", description = "Create cropTraitObservationDataAttach", summary = "Create")
			public CropTraitObservationDataAttachDTO create(@RequestBody CropTraitObservationDataAttachDTO entity) {
				return super.create(entity);
			}

			@Override
			@Operation(operationId = "updateCropTraitObservationDataAttach", description = "Update an existing record", summary = "Update")
			public CropTraitObservationDataAttachDTO update(@RequestBody CropTraitObservationDataAttachDTO entity) {
				return super.update(entity);
			}

			@Override
			@Operation(operationId = "getCropTraitObservationDataAttach", description = "Get record by ID", summary = "Get")
			public CropTraitObservationDataAttachDTO get(@PathVariable long id) {
				return super.get(id);
			}

			@Override
			@Operation(operationId = "deleteCropTraitObservationDataAttach", description = "Delete existing record by ID", summary = "Delete")
			public CropTraitObservationDataAttachDTO remove(@PathVariable long id) {
				return super.remove(id);
			}
		}

		/**
		 * Get distinct inventories from CropTraitObservationData for selected method.
		 *
		 * @param methodId the id of method
		 * @param page the page
		 * @return the page of inventories
		 */
		@GetMapping(value = "/{methodId}/inventories", produces = { MediaType.APPLICATION_JSON_VALUE })
		public Page<InventoryDTO> getInventories(@ParameterObject final Pagination page, @PathVariable Long methodId) {

			Pageable pageable = ArrayUtils.isEmpty(page.getS()) ? page.toPageRequest(MAX_PAGE_SIZE, DEFAULT_PAGE_SIZE, QInventory.inventory.id.asc()) : page.toPageRequest(MAX_PAGE_SIZE, DEFAULT_PAGE_SIZE);
			return serviceFacade.getObservationDataInventoriesByMethod(methodId, pageable);
		}

		/**
		 * Get distinct crop trait from CropTraitObservationData for selected method.
		 *
		 * @param methodId the id of method
		 * @param page the page
		 * @return the page of crop traits
		 */
		@GetMapping(value = "/{methodId}/traits", produces = { MediaType.APPLICATION_JSON_VALUE })
		public Page<CropTraitDTO> getTraits(@ParameterObject final Pagination page, @PathVariable Long methodId) {

			Pageable pageable = ArrayUtils.isEmpty(page.getS()) ? page.toPageRequest(MAX_PAGE_SIZE, DEFAULT_PAGE_SIZE, QCropTrait.cropTrait.id.asc()) : page.toPageRequest(MAX_PAGE_SIZE, DEFAULT_PAGE_SIZE);
			return serviceFacade.getObservationDataTraitsByMethod(methodId, pageable);
		}

		/**
		 * Ensure CropTraitObservationData for given method, inventories and traits
		 *
		 * @param request the request
		 * @return the number of created observation data
		 */
		@PostMapping(value = "/ensure")
		public int ensureObservationData(@RequestBody @Valid CropTraitObservationApiService.EnsureObservationsRequest request) {

			return serviceFacade.ensureObservationData(request);
		}

		@PostMapping(value = "/search", produces = { MinifyingHttpMessageConverter.APPLICATION_MINIFIED_JSON_VALUE })
		public CropTraitObservationDataApiService.FilteredObservationsData filterObservationsData(@ParameterObject final Pagination page,
			@RequestBody(required = true) final CropTraitObservationDataFilter filter) {

			Pageable pageable = ArrayUtils.isEmpty(page.getS()) ? page.toPageRequest(MAX_PAGE_SIZE, DEFAULT_PAGE_SIZE, observationsDataDefaultSort()) : page.toPageRequest(MAX_PAGE_SIZE, DEFAULT_PAGE_SIZE);
			return serviceFacade.search(filter, pageable);
		}

	}
}