VocabularyApiServiceImpl.java

/*
 * Copyright 2025 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.genesys.server.api.v2.facade.impl;

import lombok.extern.slf4j.Slf4j;
import org.genesys.server.api.Pagination;
import org.genesys.server.api.v2.facade.VocabularyApiService;
import org.genesys.server.api.v2.mapper.MapstructMapper;
import org.genesys.server.api.v2.model.impl.VocabularyTermDTO;
import org.genesys.server.api.v2.model.vocab.ControlledVocabularyDTO;
import org.genesys.server.exception.InvalidApiUsageException;
import org.genesys.server.model.filters.ControlledVocabularyFilter;
import org.genesys.server.model.vocab.ControlledVocabulary;
import org.genesys.server.model.vocab.VocabularyTerm;
import org.genesys.server.service.CountryService;
import org.genesys.server.service.InstituteService;
import org.genesys.server.service.VocabularyService;
import org.genesys.server.service.worker.ISO639VocabularyUpdater;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

@Transactional(readOnly = true)
@Service
@Slf4j
public class VocabularyApiServiceImpl implements VocabularyApiService {

	@Value("${api.page.max:1000}")
	protected int MAX_PAGE_SIZE;
	@Value("${api.page.default:100}")
	protected int DEFAULT_PAGE_SIZE;

	@Autowired
	private VocabularyService service;
	
	@Autowired
	private InstituteService instituteService;

	@Autowired
	private CountryService geoService;
	
	@Autowired
	private ISO639VocabularyUpdater iso639VocabularyUpdater;

	@Autowired
	private MapstructMapper mapper;

	@Override
	public ControlledVocabularyDTO getVocabulary(UUID uuid) {
		return mapper.map(service.getVocabulary(uuid));
	}

	@Override
	public VocabularyTermDTO getTerm(UUID uuid, String code) {
		VocabularyTerm resultTerm;
		switch (uuid.toString()) {
			case FAO_WIEWS_UUID:
				resultTerm = instituteService.getInstituteTerm(code);
				break;
			case ISO3166_2ALPHA_UUID:
				resultTerm = geoService.get3166Alpha2Term(code);
				break;
			case ISO3166_3ALPHA_UUID:
				resultTerm = geoService.get3166Alpha3Term(code);
				break;
			case ISO3166_NUMERIC_UUID:
				resultTerm = geoService.get3166NumericTerm(code);
				break;
			default:
				resultTerm = service.getVocabularyTerm(uuid, code);
		}
		return mapper.map(resultTerm);
	}

	@Override
	public List<VocabularyTermDTO> autocompleteTerms(UUID uuid, String like) {
		return mapper.map(service.autocompleteTerms(uuid, like), mapper::map);
	}

	@Override
	public Map<String, String> decodeTerms(UUID uuid, String locale, Set<String> codes) {
		switch (uuid.toString()) {
			case FAO_WIEWS_UUID:
				return instituteService.decodeCodes(codes);
			case ISO3166_3ALPHA_UUID:
				return geoService.decode3166Alpha3Terms(codes, Locale.forLanguageTag(locale));
			default:
				throw new InvalidApiUsageException("No decoding for such vocabulary");
		}
	}

	@Override
	public Page<VocabularyTermDTO> listTerms(UUID uuid, Pagination page) {
		final ControlledVocabulary vocabulary = service.getVocabulary(uuid);

		switch (uuid.toString()) {
			case FAO_WIEWS_UUID:
				return mapper.map(instituteService.listTerms(page.toPageRequest(MAX_PAGE_SIZE, DEFAULT_PAGE_SIZE, Sort.Direction.ASC, "code")), mapper::map);
			case ISO3166_2ALPHA_UUID:
				return mapper.map(geoService.list3166Alpha2Terms(page.toPageRequest(MAX_PAGE_SIZE, DEFAULT_PAGE_SIZE, Sort.Direction.ASC, "code2")), mapper::map);
			case ISO3166_3ALPHA_UUID:
				return mapper.map(geoService.list3166Alpha3Terms(page.toPageRequest(MAX_PAGE_SIZE, DEFAULT_PAGE_SIZE, Sort.Direction.ASC, "code3")), mapper::map);
			case ISO3166_NUMERIC_UUID:
				return mapper.map(geoService.list3166NumericTerms(page.toPageRequest(MAX_PAGE_SIZE, DEFAULT_PAGE_SIZE, Sort.Direction.ASC, "codeNum")), mapper::map);
			default:
				return mapper.map(service.listTerms(vocabulary, page.toPageRequest(MAX_PAGE_SIZE, DEFAULT_PAGE_SIZE, Sort.Direction.ASC, "code")), mapper::map);
		}
	}

	@Override
	@Transactional
	public ControlledVocabularyDTO delete(UUID uuid, int version) {
		return mapper.map(service.deleteVocabulary(service.getVocabulary(uuid, version)));
	}

	@Override
	@Transactional
	public ControlledVocabularyDTO createVocabulary(ControlledVocabularyDTO source) {
		return mapper.map(service.createVocabulary(mapper.map(source)));
	}

	@Override
	@Transactional
	public ControlledVocabularyDTO updateVocabulary(ControlledVocabularyDTO source) {
		return mapper.map(service.updateVocabulary(mapper.map(source)));
	}

	@Override
	public Page<ControlledVocabularyDTO> listVocabularies(ControlledVocabularyFilter filter, Pageable page) {
		return mapper.map(service.listVocabularies(filter, page), mapper::map);
	}

	@Override
	@Transactional
	public String updateLanguageVocabulary() throws IOException {
		log.info("Updating ISO language codes");
		service.autoUpdateOrCreateVocabulary(ISO639VocabularyUpdater.ISO639_3, iso639VocabularyUpdater.getISO639Vocabulary());
		return "OK";
	}

	@Override
	public VocabularyTermDTO getVocabularyTerm(UUID iso6393, String code) {
		return mapper.map(service.getVocabularyTerm(iso6393, code));
	}
}