nu.yona.server.analysis.rest.ActivityControllerBase.java Source code

Java tutorial

Introduction

Here is the source code for nu.yona.server.analysis.rest.ActivityControllerBase.java

Source

/*******************************************************************************
 * Copyright (c) 2016, 2018 Stichting Yona Foundation This Source Code Form is subject to the terms of the Mozilla Public License,
 * v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/.
 *******************************************************************************/
package nu.yona.server.analysis.rest;

import java.time.DayOfWeek;
import java.time.LocalDate;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.web.PagedResourcesAssembler;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.PagedResources;
import org.springframework.hateoas.Resource;
import org.springframework.hateoas.mvc.ControllerLinkBuilder;
import org.springframework.hateoas.mvc.ResourceAssemblerSupport;
import org.springframework.http.HttpEntity;

import com.fasterxml.jackson.annotation.JsonFormat;

import nu.yona.server.analysis.entities.IntervalActivity;
import nu.yona.server.analysis.entities.WeekActivity;
import nu.yona.server.analysis.service.ActivityCommentMessageDto;
import nu.yona.server.analysis.service.ActivityService;
import nu.yona.server.analysis.service.DayActivityDto;
import nu.yona.server.analysis.service.DayActivityOverviewDto;
import nu.yona.server.analysis.service.WeekActivityDto;
import nu.yona.server.analysis.service.WeekActivityOverviewDto;
import nu.yona.server.crypto.seckey.CryptoSession;
import nu.yona.server.messaging.rest.MessageController;
import nu.yona.server.messaging.service.MessageDto;
import nu.yona.server.rest.ControllerBase;
import nu.yona.server.subscriptions.rest.BuddyController;
import nu.yona.server.subscriptions.service.BuddyDto;
import nu.yona.server.subscriptions.service.GoalIdMapping;
import nu.yona.server.subscriptions.service.UserDto;
import nu.yona.server.subscriptions.service.UserService;

/*
 * Activity controller base class.
 */
abstract class ActivityControllerBase extends ControllerBase {
    public static final String DAY_DETAIL_LINK = "dayDetails";
    public static final String WEEK_DETAIL_LINK = "weekDetails";
    public static final String DAY_OVERVIEW_LINK = "dailyActivityReports";
    public static final String WEEK_OVERVIEW_LINK = "weeklyActivityReports";

    @Autowired
    protected ActivityService activityService;

    @Autowired
    protected UserService userService;

    @Autowired
    private MessageController messageController;

    protected static final String WEEK_ACTIVITY_OVERVIEWS_URI_FRAGMENT = "/weeks/";
    protected static final String WEEK_ACTIVITY_OVERVIEW_URI_FRAGMENT = "/weeks/{date}";
    protected static final String DAY_OVERVIEWS_URI_FRAGMENT = "/days/";
    protected static final String DAY_OVERVIEW_URI_FRAGMENT = "/days/{date}";
    protected static final String WEEK_ACTIVITY_DETAIL_URI_FRAGMENT = "/weeks/{date}/details/{goalId}";
    protected static final String WEEK_ACTIVITY_DETAIL_MESSAGES_URI_FRAGMENT = WEEK_ACTIVITY_DETAIL_URI_FRAGMENT
            + "/messages/";
    protected static final String DAY_ACTIVITY_DETAIL_URI_FRAGMENT = "/days/{date}/details/{goalId}";
    protected static final String DAY_ACTIVITY_DETAIL_MESSAGES_URI_FRAGMENT = DAY_ACTIVITY_DETAIL_URI_FRAGMENT
            + "/messages/";
    protected static final String GOAL_PATH_VARIABLE = "goalId";
    protected static final String DATE_PATH_VARIABLE = "date";
    protected static final int WEEKS_DEFAULT_PAGE_SIZE = 2;
    protected static final int DAYS_DEFAULT_PAGE_SIZE = 3;
    protected static final int MESSAGES_DEFAULT_PAGE_SIZE = 4;
    protected static final String PREV_REL = "prev"; // IANA reserved, so will not be prefixed
    protected static final String NEXT_REL = "next"; // IANA reserved, so will not be prefixed

    protected HttpEntity<PagedResources<WeekActivityOverviewResource>> getWeekActivityOverviews(
            Optional<String> password, UUID userId,
            PagedResourcesAssembler<WeekActivityOverviewDto> pagedResourcesAssembler,
            Supplier<Page<WeekActivityOverviewDto>> activitySupplier, LinkProvider linkProvider) {
        try (CryptoSession cryptoSession = CryptoSession.start(password,
                () -> userService.doPreparationsAndCheckCanAccessPrivateData(userId))) {
            return createOkResponse(activitySupplier.get(), pagedResourcesAssembler,
                    createWeekActivityOverviewResourceAssembler(linkProvider));
        }
    }

    protected HttpEntity<WeekActivityOverviewResource> getWeekActivityOverview(Optional<String> password,
            UUID userId, String dateStr, Function<LocalDate, WeekActivityOverviewDto> activitySupplier,
            LinkProvider linkProvider) {
        try (CryptoSession cryptoSession = CryptoSession.start(password,
                () -> userService.doPreparationsAndCheckCanAccessPrivateData(userId))) {
            LocalDate date = WeekActivityDto.parseDate(dateStr);
            return createOkResponse(activitySupplier.apply(date),
                    createWeekActivityOverviewResourceAssembler(linkProvider));
        }
    }

    protected HttpEntity<PagedResources<DayActivityOverviewResource>> getDayActivityOverviews(
            Optional<String> password, UUID userId,
            PagedResourcesAssembler<DayActivityOverviewDto<DayActivityDto>> pagedResourcesAssembler,
            Supplier<Page<DayActivityOverviewDto<DayActivityDto>>> activitySupplier, LinkProvider linkProvider) {
        try (CryptoSession cryptoSession = CryptoSession.start(password,
                () -> userService.doPreparationsAndCheckCanAccessPrivateData(userId))) {
            return createOkResponse(activitySupplier.get(), pagedResourcesAssembler,
                    createDayActivityOverviewResourceAssembler(linkProvider));
        }
    }

    protected HttpEntity<DayActivityOverviewResource> getDayActivityOverview(Optional<String> password, UUID userId,
            String dateStr, Function<LocalDate, DayActivityOverviewDto<DayActivityDto>> activitySupplier,
            LinkProvider linkProvider) {
        try (CryptoSession cryptoSession = CryptoSession.start(password,
                () -> userService.doPreparationsAndCheckCanAccessPrivateData(userId))) {
            LocalDate date = DayActivityDto.parseDate(dateStr);
            return createOkResponse(activitySupplier.apply(date),
                    createDayActivityOverviewResourceAssembler(linkProvider));
        }
    }

    protected HttpEntity<WeekActivityResource> getWeekActivityDetail(Optional<String> password, UUID userId,
            String dateStr, Function<LocalDate, WeekActivityDto> activitySupplier, LinkProvider linkProvider) {
        try (CryptoSession cryptoSession = CryptoSession.start(password,
                () -> userService.doPreparationsAndCheckCanAccessPrivateData(userId))) {
            LocalDate date = WeekActivityDto.parseDate(dateStr);
            return createOkResponse(activitySupplier.apply(date),
                    createWeekActivityResourceAssembler(linkProvider));
        }
    }

    protected HttpEntity<DayActivityResource> getDayActivityDetail(Optional<String> password, UUID userId,
            String dateStr, Function<LocalDate, DayActivityDto> activitySupplier, LinkProvider linkProvider) {
        try (CryptoSession cryptoSession = CryptoSession.start(password,
                () -> userService.doPreparationsAndCheckCanAccessPrivateData(userId))) {
            LocalDate date = DayActivityDto.parseDate(dateStr);
            return createOkResponse(activitySupplier.apply(date), createDayActivityResourceAssembler(linkProvider));
        }
    }

    protected HttpEntity<PagedResources<MessageDto>> getActivityDetailMessages(Optional<String> password,
            UUID userId, PagedResourcesAssembler<MessageDto> pagedResourcesAssembler,
            Supplier<Page<MessageDto>> messageSupplier, LinkProvider linkProvider) {
        try (CryptoSession cryptoSession = CryptoSession.start(password,
                () -> userService.doPreparationsAndCheckCanAccessPrivateData(userId))) {
            UserDto user = userService.getPrivateValidatedUser(userId);
            return messageController.createOkResponse(user, messageSupplier.get(), pagedResourcesAssembler);
        }
    }

    private WeekActivityOverviewResourceAssembler createWeekActivityOverviewResourceAssembler(
            LinkProvider linkProvider) {
        return new WeekActivityOverviewResourceAssembler(linkProvider);
    }

    private DayActivityOverviewResourceAssembler createDayActivityOverviewResourceAssembler(
            LinkProvider linkProvider) {
        return new DayActivityOverviewResourceAssembler(linkProvider);
    }

    private WeekActivityResourceAssembler createWeekActivityResourceAssembler(LinkProvider linkProvider) {
        return new WeekActivityResourceAssembler(linkProvider, true);
    }

    private DayActivityResourceAssembler createDayActivityResourceAssembler(LinkProvider linkProvider) {
        return new DayActivityResourceAssembler(linkProvider, true, true);
    }

    protected GoalIdMapping createGoalIdMapping(UUID userId) {
        return GoalIdMapping.createInstance(userService.getPrivateUser(userId));
    }

    public abstract void addLinks(GoalIdMapping goalIdMapping, IntervalActivity activity,
            ActivityCommentMessageDto message);

    protected void addStandardLinks(GoalIdMapping goalIdMapping, LinkProvider linkProvider,
            IntervalActivity activity, ActivityCommentMessageDto message) {
        if (activity instanceof WeekActivity) {
            addWeekDetailsLink(linkProvider, activity, message);
        } else {
            addDayDetailsLink(linkProvider, activity, message);
        }
        if (!message.getSenderUser().get().getId().equals(goalIdMapping.getUserId())) {
            UUID buddyId = determineBuddyId(goalIdMapping, message);
            addBuddyLink(goalIdMapping.getUserId(), buddyId, message);
        }
        addThreadHeadMessageLink(goalIdMapping.getUserId(), message);
        message.getRepliedMessageId().ifPresent(rmid -> addRepliedMessageLink(goalIdMapping.getUserId(), message));
    }

    private UUID determineBuddyId(GoalIdMapping goalIdMapping, ActivityCommentMessageDto message) {
        return goalIdMapping.getUser().getOwnPrivateData().getBuddies().stream()
                .filter(b -> b.getUser().getId().equals(message.getSenderUser().get().getId())).map(BuddyDto::getId)
                .findAny().orElseThrow(() -> new IllegalArgumentException(
                        "User with ID " + message.getSenderUser().get().getId() + "is not a buddy"));
    }

    private void addWeekDetailsLink(LinkProvider linkProvider, IntervalActivity activity,
            ActivityCommentMessageDto message) {
        message.add(
                linkProvider.getWeekActivityDetailLinkBuilder(WeekActivityDto.formatDate(activity.getStartDate()),
                        activity.getGoal().getId()).withRel(WEEK_DETAIL_LINK));
    }

    private void addDayDetailsLink(LinkProvider linkProvider, IntervalActivity activity,
            ActivityCommentMessageDto message) {
        message.add(linkProvider.getDayActivityDetailLinkBuilder(DayActivityDto.formatDate(activity.getStartDate()),
                activity.getGoal().getId()).withRel(DAY_DETAIL_LINK));
    }

    private void addThreadHeadMessageLink(UUID userId, ActivityCommentMessageDto message) {
        message.add(MessageController.getAnonymousMessageLinkBuilder(userId, message.getThreadHeadMessageId())
                .withRel("threadHead"));
    }

    private void addRepliedMessageLink(UUID userId, ActivityCommentMessageDto message) {
        message.add(MessageController.getAnonymousMessageLinkBuilder(userId, message.getRepliedMessageId().get())
                .withRel("repliedMessage"));
    }

    private void addBuddyLink(UUID userId, UUID buddyId, ActivityCommentMessageDto message) {
        message.add(BuddyController.getBuddyLinkBuilder(userId, buddyId).withRel(BuddyController.BUDDY_LINK));
    }

    interface LinkProvider {
        public ControllerLinkBuilder getWeekActivityOverviewLinkBuilder(String dateStr);

        public ControllerLinkBuilder getDayActivityOverviewLinkBuilder(String dateStr);

        public ControllerLinkBuilder getDayActivityDetailLinkBuilder(String dateStr, UUID goalId);

        public ControllerLinkBuilder getWeekActivityDetailLinkBuilder(String dateStr, UUID goalId);

        public ControllerLinkBuilder getGoalLinkBuilder(UUID goalId);

        public ControllerLinkBuilder getDayActivityDetailMessagesLinkBuilder(String dateStr, UUID goalId);

        public Optional<ControllerLinkBuilder> getDayActivityDetailAddCommentLinkBuilder(String dateStr,
                UUID goalId);

        public ControllerLinkBuilder getWeekActivityDetailMessagesLinkBuilder(String dateStr, UUID goalId);

        public Optional<ControllerLinkBuilder> getWeekActivityDetailAddCommentLinkBuilder(String dateStr,
                UUID goalId);
    }

    static class WeekActivityResource extends Resource<WeekActivityDto> {
        private final LinkProvider linkProvider;

        public WeekActivityResource(LinkProvider linkProvider, WeekActivityDto weekActivity) {
            super(weekActivity);
            this.linkProvider = linkProvider;
        }

        @JsonFormat(shape = JsonFormat.Shape.OBJECT)
        public Map<DayOfWeek, DayActivityResource> getDayActivities() {
            DayActivityResourceAssembler a = new DayActivityResourceAssembler(linkProvider, false, false);
            return getContent().getDayActivities().entrySet().stream()
                    .collect(Collectors.toMap(Map.Entry::getKey, e -> a.toResource(e.getValue())));
        }
    }

    static class WeekActivityOverviewResource extends Resource<WeekActivityOverviewDto> {
        private final LinkProvider linkProvider;

        public WeekActivityOverviewResource(LinkProvider linkProvider,
                WeekActivityOverviewDto weekActivityOverview) {
            super(weekActivityOverview);
            this.linkProvider = linkProvider;
        }

        public List<WeekActivityResource> getWeekActivities() {
            return new WeekActivityResourceAssembler(linkProvider, false)
                    .toResources(getContent().getWeekActivities());
        }
    }

    static class DayActivityResource extends Resource<DayActivityDto> {
        public DayActivityResource(DayActivityDto dayActivity) {
            super(dayActivity);
        }
    }

    static class DayActivityOverviewResource extends Resource<DayActivityOverviewDto<DayActivityDto>> {
        private final LinkProvider linkProvider;

        public DayActivityOverviewResource(LinkProvider linkProvider,
                DayActivityOverviewDto<DayActivityDto> dayActivityOverview) {
            super(dayActivityOverview);
            this.linkProvider = linkProvider;
        }

        public List<DayActivityResource> getDayActivities() {
            return new DayActivityResourceAssembler(linkProvider, true, false)
                    .toResources(getContent().getDayActivities());
        }
    }

    static class WeekActivityOverviewResourceAssembler
            extends ResourceAssemblerSupport<WeekActivityOverviewDto, WeekActivityOverviewResource> {
        private final LinkProvider linkProvider;

        public WeekActivityOverviewResourceAssembler(LinkProvider linkProvider) {
            super(ActivityControllerBase.class, WeekActivityOverviewResource.class);
            this.linkProvider = linkProvider;
        }

        @Override
        public WeekActivityOverviewResource toResource(WeekActivityOverviewDto weekActivityOverview) {
            WeekActivityOverviewResource resource = instantiateResource(weekActivityOverview);
            addSelfLink(resource);
            return resource;
        }

        @Override
        protected WeekActivityOverviewResource instantiateResource(WeekActivityOverviewDto weekActivityOverview) {
            return new WeekActivityOverviewResource(linkProvider, weekActivityOverview);
        }

        private void addSelfLink(Resource<WeekActivityOverviewDto> resource) {
            resource.add(linkProvider.getWeekActivityOverviewLinkBuilder(resource.getContent().getDateStr())
                    .withSelfRel());
        }
    }

    static class WeekActivityResourceAssembler
            extends ResourceAssemblerSupport<WeekActivityDto, WeekActivityResource> {
        private final LinkProvider linkProvider;
        private final boolean isWeekDetail;

        public WeekActivityResourceAssembler(LinkProvider linkProvider, boolean isWeekDetail) {
            super(ActivityControllerBase.class, WeekActivityResource.class);
            this.linkProvider = linkProvider;
            this.isWeekDetail = isWeekDetail;
        }

        @Override
        public WeekActivityResource toResource(WeekActivityDto weekActivity) {
            WeekActivityResource weekActivityResource = instantiateResource(weekActivity);
            addWeekDetailsLink(weekActivityResource, (isWeekDetail) ? Link.REL_SELF : WEEK_DETAIL_LINK);
            addGoalLink(weekActivityResource);
            if (isWeekDetail) {
                addMessagesLink(weekActivityResource);
                addAddCommentLink(weekActivityResource);
                addPrevNextLinks(weekActivityResource);
            }
            return weekActivityResource;
        }

        @Override
        protected WeekActivityResource instantiateResource(WeekActivityDto weekActivity) {
            return new WeekActivityResource(linkProvider, weekActivity);
        }

        private void addWeekDetailsLink(WeekActivityResource weekActivityResource, String rel) {
            weekActivityResource.add(
                    linkProvider.getWeekActivityDetailLinkBuilder(weekActivityResource.getContent().getDateStr(),
                            weekActivityResource.getContent().getGoalId()).withRel(rel));
        }

        private void addGoalLink(WeekActivityResource weekActivityResource) {
            weekActivityResource.add(
                    linkProvider.getGoalLinkBuilder(weekActivityResource.getContent().getGoalId()).withRel("goal"));
        }

        private void addMessagesLink(WeekActivityResource weekActivityResource) {
            weekActivityResource.add(linkProvider
                    .getWeekActivityDetailMessagesLinkBuilder(weekActivityResource.getContent().getDateStr(),
                            weekActivityResource.getContent().getGoalId())
                    .withRel("messages"));
        }

        private void addAddCommentLink(WeekActivityResource weekActivityResource) {
            Optional<ControllerLinkBuilder> linkBuilder = linkProvider.getWeekActivityDetailAddCommentLinkBuilder(
                    weekActivityResource.getContent().getDateStr(), weekActivityResource.getContent().getGoalId());
            linkBuilder.ifPresent(lb -> weekActivityResource.add(lb.withRel("addComment")));
        }

        private void addPrevNextLinks(WeekActivityResource weekActivityResource) {
            if (weekActivityResource.getContent().hasPrevious()) {
                weekActivityResource.add(linkProvider
                        .getWeekActivityDetailLinkBuilder(weekActivityResource.getContent().getPreviousDateStr(),
                                weekActivityResource.getContent().getGoalId())
                        .withRel(PREV_REL));
            }
            if (weekActivityResource.getContent().hasNext()) {
                weekActivityResource.add(linkProvider
                        .getWeekActivityDetailLinkBuilder(weekActivityResource.getContent().getNextDateStr(),
                                weekActivityResource.getContent().getGoalId())
                        .withRel(NEXT_REL));
            }
        }
    }

    static class DayActivityResourceAssembler
            extends ResourceAssemblerSupport<DayActivityDto, DayActivityResource> {
        private final LinkProvider linkProvider;
        private final boolean addGoalLink;
        private final boolean isDayDetail;

        public DayActivityResourceAssembler(LinkProvider linkProvider, boolean addGoalLink, boolean isDayDetail) {
            super(ActivityControllerBase.class, DayActivityResource.class);
            this.linkProvider = linkProvider;
            this.addGoalLink = addGoalLink;
            this.isDayDetail = isDayDetail;
        }

        @Override
        public DayActivityResource toResource(DayActivityDto dayActivity) {
            DayActivityResource dayActivityResource = instantiateResource(dayActivity);
            addDayDetailsLink(dayActivityResource, (isDayDetail) ? Link.REL_SELF : DAY_DETAIL_LINK);
            if (addGoalLink) {
                addGoalLink(dayActivityResource);
            }
            if (isDayDetail) {
                addMessagesLink(dayActivityResource);
                addAddCommentLink(dayActivityResource);
                addPrevNextLinks(dayActivityResource);
            }
            return dayActivityResource;
        }

        @Override
        protected DayActivityResource instantiateResource(DayActivityDto dayActivity) {
            return new DayActivityResource(dayActivity);
        }

        private void addGoalLink(DayActivityResource dayActivityResource) {
            dayActivityResource.add(
                    linkProvider.getGoalLinkBuilder(dayActivityResource.getContent().getGoalId()).withRel("goal"));
        }

        private void addDayDetailsLink(DayActivityResource dayActivityResource, String rel) {
            dayActivityResource
                    .add(linkProvider.getDayActivityDetailLinkBuilder(dayActivityResource.getContent().getDateStr(),
                            dayActivityResource.getContent().getGoalId()).withRel(rel));
        }

        private void addMessagesLink(DayActivityResource dayActivityResource) {
            dayActivityResource.add(linkProvider
                    .getDayActivityDetailMessagesLinkBuilder(dayActivityResource.getContent().getDateStr(),
                            dayActivityResource.getContent().getGoalId())
                    .withRel("messages"));
        }

        private void addAddCommentLink(DayActivityResource dayActivityResource) {
            Optional<ControllerLinkBuilder> linkBuilder = linkProvider.getDayActivityDetailAddCommentLinkBuilder(
                    dayActivityResource.getContent().getDateStr(), dayActivityResource.getContent().getGoalId());
            linkBuilder.ifPresent(lb -> dayActivityResource.add(lb.withRel("addComment")));
        }

        private void addPrevNextLinks(DayActivityResource dayActivityResource) {
            if (dayActivityResource.getContent().hasPrevious()) {
                dayActivityResource.add(linkProvider
                        .getDayActivityDetailLinkBuilder(dayActivityResource.getContent().getPreviousDateStr(),
                                dayActivityResource.getContent().getGoalId())
                        .withRel(PREV_REL));
            }
            if (dayActivityResource.getContent().hasNext()) {
                dayActivityResource.add(linkProvider
                        .getDayActivityDetailLinkBuilder(dayActivityResource.getContent().getNextDateStr(),
                                dayActivityResource.getContent().getGoalId())
                        .withRel(NEXT_REL));
            }
        }
    }

    static class DayActivityOverviewResourceAssembler
            extends ResourceAssemblerSupport<DayActivityOverviewDto<DayActivityDto>, DayActivityOverviewResource> {
        private final LinkProvider linkProvider;

        public DayActivityOverviewResourceAssembler(LinkProvider linkProvider) {
            super(ActivityControllerBase.class, DayActivityOverviewResource.class);
            this.linkProvider = linkProvider;
        }

        @Override
        public DayActivityOverviewResource toResource(DayActivityOverviewDto<DayActivityDto> dayActivityOverview) {
            DayActivityOverviewResource resource = instantiateResource(dayActivityOverview);
            addSelfLink(resource);
            return resource;
        }

        @Override
        protected DayActivityOverviewResource instantiateResource(
                DayActivityOverviewDto<DayActivityDto> dayActivityOverview) {
            return new DayActivityOverviewResource(linkProvider, dayActivityOverview);
        }

        private void addSelfLink(Resource<DayActivityOverviewDto<DayActivityDto>> resource) {
            resource.add(linkProvider.getDayActivityOverviewLinkBuilder(resource.getContent().getDateStr())
                    .withSelfRel());
        }
    }
}