358 lines
15 KiB
Java

package no.asprusten.sykkelaksjon;
import no.asprusten.sykkelaksjon.db.services.ActivityService;
import no.asprusten.sykkelaksjon.db.services.ActivityTemplateService;
import no.asprusten.sykkelaksjon.db.services.ActivityTypeService;
import no.asprusten.sykkelaksjon.db.services.UserService;
import no.asprusten.sykkelaksjon.messages.*;
import no.asprusten.sykkelaksjon.security.ServerExceptionHandler;
import org.pac4j.core.profile.ProfileManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
/**
* Hello world!
*/
@RestController
@SpringBootApplication
public class Server {
@Autowired
private ProfileManager profileManager;
@Autowired
private UserService userService;
@Autowired
private ActivityTypeService activityTypeService;
@Autowired
private ActivityService activityService;
@Autowired
private ActivityTemplateService activityTemplateService;
@Value("${sykkelaksjon.openid.discoveryURI}")
private String discoveryURI;
@Value("${sykkelaksjon.openid.clientId}")
private String clientId;
@Value("$(sykkelaksjon.base-url)")
private String baseUrl;
private ActivityType getActivityTypeMessage(no.asprusten.sykkelaksjon.db.datatypes.ActivityType activityType) {
ActivityType activityTypeMessage = new ActivityType();
activityTypeMessage.setId(activityType.getId());
activityTypeMessage.setName(activityType.getActivityType());
activityTypeMessage.setConversionFactor(activityType.getConversion());
activityTypeMessage.setUnit(activityType.getUnit());
return activityTypeMessage;
}
private ActivityTemplate getActivityTemplateMessage(no.asprusten.sykkelaksjon.db.datatypes.ActivityTemplate activityTemplate) {
ActivityTemplate templateMessage = new ActivityTemplate();
templateMessage.setId(activityTemplate.getId());
templateMessage.setActivityType(getActivityTypeMessage(activityTemplate.getActivityType()));
templateMessage.setName(activityTemplate.getName());
templateMessage.setNumberOfUnits(activityTemplate.getNumberOfUnits());
return templateMessage;
}
private Activity getActivityMessage(no.asprusten.sykkelaksjon.db.datatypes.Activity activity) {
Activity activityMessage = new Activity();
activityMessage.setId(activity.getId());
activityMessage.setActivityType(getActivityTypeMessage(activity.getActivityType()));
activityMessage.setDescription(activity.getDescription());
activityMessage.setNumberOfUnits(activity.getNumberOfUnits());
activityMessage.setDate(activity.getDate().format(DateTimeFormatter.ISO_LOCAL_DATE));
return activityMessage;
}
@CrossOrigin
@GetMapping("/api")
public ServerMessageSchema respondToRequest() throws ServerExceptionHandler.InvalidUserException {
var optionalUserProfile = profileManager.getProfile();
if (optionalUserProfile.isEmpty()) {
throw new ServerExceptionHandler.InvalidUserException();
}
var userProfile = optionalUserProfile.get();
var optionalUser = userService.getUser(userProfile.getUsername());
var user = optionalUser
.orElseGet(
() -> userService.createUser(
userProfile.getUsername(),
userProfile.getAttribute("name").toString()
)
);
var allUsers = userService.list();
ServerMessageSchema serverMessage = new ServerMessageSchema();
serverMessage.setName(user.getFullName());
serverMessage.setIsAdmin(user.isAdmin());
boolean unitConversionNecessary = false;
for (var activityType : activityTypeService.list()) {
serverMessage.getActivityTypes().add(getActivityTypeMessage(activityType));
unitConversionNecessary |= activityType.getConversion() != 1.0;
}
serverMessage.setUnitConversionNecessary(unitConversionNecessary);
for (var activityTemplate : user.getTemplates()) {
serverMessage.getActivityTemplates().add(getActivityTemplateMessage(activityTemplate));
}
List<Activity> unsortedActivities = new ArrayList<>();
for (var activity : user.getActivities()) {
unsortedActivities.add(getActivityMessage(activity));
}
unsortedActivities.sort(Comparator.comparing(Activity::getDate));
serverMessage.getActivities().addAll(unsortedActivities.reversed());
List<OtherUser> unsortedOtherUsers = new ArrayList<>();
for (var otherUser : allUsers) {
// Don't describe the current user
if (otherUser.getId().equals(user.getId())) {
continue;
}
// If this is not an active user, and the requesting user is not an admin, skip
if (!otherUser.isActive() && !user.isAdmin()) {
continue;
}
// If the user has not registered anything yet, also skip it (unless admin)
if (otherUser.getActivities().isEmpty() && !user.isAdmin()) {
continue;
}
OtherUser otherUserMessage = new OtherUser();
otherUserMessage.setName(otherUser.getFullName());
double totalKilometers = otherUser.getActivities()
.stream()
.mapToDouble(activity -> activity.getActivityType().getConversion() * activity.getNumberOfUnits())
.sum();
otherUserMessage.setTotalKilometers(totalKilometers);
otherUser.getActivities().stream()
.map(no.asprusten.sykkelaksjon.db.datatypes.Activity::getDate)
.min(LocalDate::compareTo)
.ifPresent(earliestDate ->
otherUserMessage.setEarliestActivity(earliestDate.format(DateTimeFormatter.ISO_LOCAL_DATE))
);
otherUser.getActivities().stream()
.map(no.asprusten.sykkelaksjon.db.datatypes.Activity::getDate)
.max(LocalDate::compareTo)
.ifPresent(latestDate ->
otherUserMessage.setLatestActivity(latestDate.format(DateTimeFormatter.ISO_LOCAL_DATE))
);
// Additional info is only for administrators
if (user.isAdmin()) {
otherUserMessage.setUserId(otherUser.getId());
otherUserMessage.setUserName(otherUser.getUsername());
otherUserMessage.setIsActive(otherUser.isActive());
otherUserMessage.setIsAdmin(otherUser.isAdmin());
List<Activity> userUnsortedActivities = new ArrayList<>();
for (var activity : otherUser.getActivities()) {
userUnsortedActivities.add(getActivityMessage(activity));
}
userUnsortedActivities.sort(Comparator.comparing(Activity::getDate));
otherUserMessage.getActivities().addAll(userUnsortedActivities.reversed());
for (var activityTemplate : otherUser.getTemplates()) {
otherUserMessage.getActivityTemplates().add(getActivityTemplateMessage(activityTemplate));
}
}
unsortedOtherUsers.add(otherUserMessage);
}
unsortedOtherUsers.sort(Comparator.comparing(OtherUser::getTotalKilometers));
serverMessage.getOtherUsers().addAll(unsortedOtherUsers.reversed());
return serverMessage;
}
@CrossOrigin
@PostMapping(path = "/api/submitActivity", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public void submitActivity(
@RequestParam("activity-type") Long activityTypeId,
@RequestParam("activity-distance") double distance,
@RequestParam("activity-description") String description,
@RequestParam("activity-date") String date
) {
profileManager.getProfile().ifPresent(userProfile -> {
String username = userProfile.getUsername();
userService.getUser(username).ifPresent(dbUser -> {
activityTypeService.getById(activityTypeId).ifPresent(activityType -> {
no.asprusten.sykkelaksjon.db.datatypes.Activity activity = new no.asprusten.sykkelaksjon.db.datatypes.Activity(
activityType,
dbUser,
distance,
description,
LocalDate.parse(date)
);
activityService.saveActivity(activity);
});
});
});
}
@CrossOrigin
@PostMapping(path = "/api/submitActivityTemplate", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public void submitActivityTemplate(
@RequestParam("activity-type") Long activityTypeId,
@RequestParam("activity-distance") double distance,
@RequestParam("activity-description") String description,
@RequestParam("activity-date") String date
) {
profileManager.getProfile().ifPresent(userProfile -> {
String username = userProfile.getUsername();
userService.getUser(username).ifPresent(dbUser -> {
activityTypeService.getById(activityTypeId).ifPresent(activityType -> {
no.asprusten.sykkelaksjon.db.datatypes.ActivityTemplate activityTemplate = new no.asprusten.sykkelaksjon.db.datatypes.ActivityTemplate(
dbUser,
activityType,
description,
distance
);
activityTemplateService.saveActivityTemplate(activityTemplate);
});
});
});
}
@CrossOrigin
@DeleteMapping(path = "/api/deleteActivityTemplate", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public void deleteActivityTemplate(@RequestParam("activity-template-id") Long activityTemplateId) {
profileManager.getProfile().ifPresent(userProfile -> {
String username = userProfile.getUsername();
userService.getUser(username).ifPresent(dbUser -> {
activityTemplateService.findById(activityTemplateId).ifPresent(activityTemplate -> {
if (activityTemplate.getOwner().equals(dbUser)) {
activityTemplateService.deleteActivityTemplate(activityTemplate.getId());
}
});
});
});
}
@CrossOrigin
@DeleteMapping(path = "/api/deleteActivity", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public void deleteActivity(@RequestParam("activity-id") Long activityId) {
profileManager.getProfile().ifPresent(userProfile -> {
String username = userProfile.getUsername();
userService.getUser(username).ifPresent(dbUser -> {
activityService.findById(activityId).ifPresent(activity -> {
if (activity.getActivityOwner().equals(dbUser) || dbUser.isAdmin()) {
activityService.deleteActivity(activityId);
}
});
});
});
}
@CrossOrigin
@PostMapping(path = "/api/addActivityType", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public void createActivityType(
@RequestParam("activity-type-name") String name,
@RequestParam("activity-type-unit") String unit,
@RequestParam("activity-type-conversion-factor") double conversionFactor
) {
profileManager.getProfile().ifPresent(userProfile -> {
String username = userProfile.getUsername();
userService.getUser(username).ifPresent(dbUser -> {
if (dbUser.isAdmin()) {
no.asprusten.sykkelaksjon.db.datatypes.ActivityType activityType = new no.asprusten.sykkelaksjon.db.datatypes.ActivityType(
name, unit, conversionFactor
);
activityTypeService.saveActivityType(activityType);
}
});
});
}
@CrossOrigin
@DeleteMapping(path = "/api/deleteActivityType", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public void deleteActivityType(@RequestParam("activity-type-id") Long activityTypeId) {
profileManager.getProfile().ifPresent(userProfile -> {
String username = userProfile.getUsername();
userService.getUser(username).ifPresent(dbUser -> {
if (dbUser.isAdmin()) {
activityTypeService.deleteActivityType(activityTypeId);
}
});
});
}
@CrossOrigin
@PutMapping(path = "/api/makeAdmin", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public void makeAdmin(@RequestParam("user-id") Long userId) {
profileManager.getProfile().ifPresent(userProfile -> {
String username = userProfile.getUsername();
userService.getUser(username).ifPresent(dbUser -> {
if (dbUser.isAdmin()) {
userService.getUserById(userId).ifPresent(elevatedUser -> {
elevatedUser.setAdmin(true);
userService.saveUser(elevatedUser);
});
}
});
});
}
@CrossOrigin
@PutMapping(path = "/api/removeAdmin", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public void removeAdmin(@RequestParam("user-id") Long userId) {
profileManager.getProfile().ifPresent(userProfile -> {
String username = userProfile.getUsername();
userService.getUser(username).ifPresent(dbUser -> {
if (dbUser.isAdmin()) {
userService.getUserById(userId).ifPresent(elevatedUser -> {
elevatedUser.setAdmin(false);
userService.saveUser(elevatedUser);
});
}
});
});
}
@CrossOrigin
@DeleteMapping(path = "/api/deleteUser", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public void deleteUser(@RequestParam("user-id") Long userId) {
profileManager.getProfile().ifPresent(userProfile -> {
String username = userProfile.getUsername();
userService.getUser(username).ifPresent(dbUser -> {
if (dbUser.isAdmin()) {
userService.deleteUserById(userId);
}
});
});
}
@CrossOrigin
@GetMapping(path = "/api/openid")
public OpenidSchema provideOpenidConfig() {
OpenidSchema openidSchema = new OpenidSchema();
openidSchema.setClientId(clientId);
openidSchema.setOpenidDiscoveryUri(discoveryURI);
return openidSchema;
}
public static void main(String[] args) {
SpringApplication application = new SpringApplication(Server.class);
application.addListeners(new PropertiesListener());
application.run(args);
}
}