/*
* Copyright 2015 Open mHealth
*
* 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.openmhealth.shimmer.common.controller;
import com.google.common.collect.Range;
import org.openmhealth.shimmer.common.domain.DataPointSearchCriteria;
import org.openmhealth.shimmer.common.domain.DataPointSearchResult;
import org.openmhealth.shimmer.common.service.DataPointSearchService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Validator;
import java.time.OffsetDateTime;
import static org.springframework.format.annotation.DateTimeFormat.ISO.DATE_TIME;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
import static org.springframework.http.ResponseEntity.badRequest;
import static org.springframework.http.ResponseEntity.ok;
import static org.springframework.web.bind.annotation.RequestMethod.GET;
/**
* A controller that finds and retrieves data points.
*
* @author Emerson Farrugia
*/
@RestController
public class DataPointSearchController {
private static final Logger logger = LoggerFactory.getLogger(DataPointSearchController.class);
/*
* These filtering parameters are temporary. They will likely change when a more generic filtering approach is
* implemented.
*/
public static final String CREATED_ON_OR_AFTER_PARAMETER = "created_on_or_after";
public static final String CREATED_BEFORE_PARAMETER = "created_before";
public static final String EFFECTIVE_ON_OR_AFTER_PARAMETER = "effective_on_or_after";
public static final String EFFECTIVE_BEFORE_PARAMETER = "effective_before";
public static final String SCHEMA_NAMESPACE_PARAMETER = "schema_namespace";
public static final String SCHEMA_NAME_PARAMETER = "schema_name";
// TODO searching by schema version should support wildcards, which requires more thought
// public static final String SCHEMA_VERSION_PARAMETER = "schema_version";
public static final String ACQUISITION_SOURCE_ID_PARAMETER = "acquisition_source_id"; // TODO confirm name
public static final String END_USER_ID_PARAMETER = "end_user_id"; // TODO confirm name and implementation
@Autowired
private Validator validator;
@Autowired
private DataPointSearchService dataPointSearchService;
/**
* Finds and retrieves data points.
*
* @param schemaNamespace the namespace of the schema the data points conform to
* @param schemaName the name of the schema the data points conform to
* @param createdOnOrAfter the earliest creation timestamp of the data points to return, inclusive
* @param createdBefore the latest creation timestamp of the data points to return, exclusive
* @return a list of matching data points
*/
@RequestMapping(value = "/dataPoints", method = GET, produces = APPLICATION_JSON_VALUE)
public ResponseEntity<DataPointSearchResult> findDataPoints(
@RequestParam(value = SCHEMA_NAMESPACE_PARAMETER) final String schemaNamespace,
@RequestParam(value = SCHEMA_NAME_PARAMETER) final String schemaName,
@RequestParam(value = CREATED_ON_OR_AFTER_PARAMETER, required = false)
@DateTimeFormat(iso = DATE_TIME)
final OffsetDateTime createdOnOrAfter,
@RequestParam(value = CREATED_BEFORE_PARAMETER, required = false)
@DateTimeFormat(iso = DATE_TIME)
final OffsetDateTime createdBefore,
@RequestParam(value = EFFECTIVE_ON_OR_AFTER_PARAMETER, required = false)
@DateTimeFormat(iso = DATE_TIME)
final OffsetDateTime effectiveOnOrAfter,
@RequestParam(value = EFFECTIVE_BEFORE_PARAMETER, required = false)
@DateTimeFormat(iso = DATE_TIME)
final OffsetDateTime effectiveBefore,
@RequestParam(value = ACQUISITION_SOURCE_ID_PARAMETER, required = false) final String acquisitionSourceId,
@RequestParam(value = END_USER_ID_PARAMETER, required = false) final String specifiedEndUserId,
Authentication authentication) { // FIXME revise authentication
// TODO this provides feature parity, but no security
// FIXME revise authentication
String endUserId = specifiedEndUserId;
if (specifiedEndUserId == null || specifiedEndUserId.isEmpty()) {
// determine the user associated with the access token to restrict the search accordingly
endUserId = getEndUserId(authentication);
}
DataPointSearchCriteria searchCriteria = new DataPointSearchCriteria();
searchCriteria.setUserId(specifiedEndUserId);
searchCriteria.setSchemaNamespace(schemaNamespace);
searchCriteria.setSchemaName(schemaName);
searchCriteria.setCreatedOnOrAfter(createdOnOrAfter);
searchCriteria.setCreatedBefore(createdBefore);
searchCriteria.setEffectiveOnOrAfter(effectiveOnOrAfter);
searchCriteria.setEffectiveBefore(effectiveBefore);
searchCriteria.setAcquisitionSourceId(acquisitionSourceId);
if (!validator.validate(searchCriteria).isEmpty()) {
// TODO add feedback
return badRequest().body(null);
}
DataPointSearchResult searchResult = dataPointSearchService.findDataPoints(searchCriteria);
return ok().body(searchResult);
}
// FIXME revise authentication
public String getEndUserId(Authentication authentication) {
return "foo";
}
public Range<OffsetDateTime> asRange(OffsetDateTime onOrAfterDateTime, OffsetDateTime beforeDateTime) {
if (onOrAfterDateTime != null && beforeDateTime != null) {
return Range.closedOpen(onOrAfterDateTime, beforeDateTime);
}
if (onOrAfterDateTime != null) {
return Range.atLeast(onOrAfterDateTime);
}
else if (beforeDateTime != null) {
return Range.lessThan(beforeDateTime);
}
return Range.all();
}
}