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 unsortedActivities = new ArrayList<>(); for (var activity : user.getActivities()) { unsortedActivities.add(getActivityMessage(activity)); } unsortedActivities.sort(Comparator.comparing(Activity::getDate)); serverMessage.getActivities().addAll(unsortedActivities.reversed()); List 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 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); } }