<file_sep>rootProject.name = 'priorityq'

<file_sep>package ie.jgriffin.priorityq.model.impl;

import ie.jgriffin.priorityq.model.WorkOrder;

import org.joda.time.DateTime;
import org.joda.time.Seconds;

/**
 * Created by jgriffin on 18/04/2017.
 * <p>
 * Computes the rank for any given WorkOrder object. Accepts a reference DateTime object in its constructor to ensure that all time computations performed by this instance reference the same instant in time.
 */
public class RankComputer {

    /*
     * This reference DateTime is used to ensure that all time computations performed by this instance reference the
     * same instant in time.
     */
    private final DateTime referenceDateTime;

    /**
     * Constructs a new instance of RankComputer.
     *
     * @param referenceDateTime This reference DateTime is used to ensure that all time computations performed by this instance reference the same instant in time.
     */
    public RankComputer(DateTime referenceDateTime) {
        this.referenceDateTime = referenceDateTime;
    }

    /**
     * Computes the rank for the passed WorkOrderImpl.
     *
     * @param workOrder The WorkOrder to compute the ranking for.
     * @return a double representing the ranking for the WorkOrder.
     */
    public Double compute(WorkOrder workOrder) {
        long workOrderId = workOrder.getId();
        long managementOverrideRemainder = workOrderId % RankConstants.MANAGEMENT_OVERRIDE;

        if (managementOverrideRemainder == 0) {
            return computeSecondsElapsed(workOrder);
        } else if (managementOverrideRemainder % RankConstants.VIP_FACTOR == 0) {
            return computeVIPRank(workOrder);
        } else if (managementOverrideRemainder % RankConstants.PRIORITY_FACTOR == 0) {
            return computePriorityRank(workOrder);
        } else {
            return computeSecondsElapsed(workOrder);
        }
    }


    /**
     * Computes how many seconds have elapsed since the WorkOrder was placed in the queue.
     *
     * @param workOrder The WorkOrder to compare to the reference DateTime to compute how many seconds have elapsed.
     * @return The number of seconds that have elapsed from the reference DateTime.
     */
    private double computeSecondsElapsed(WorkOrder workOrder) {
        return Seconds.secondsBetween(workOrder.getDateTime(), referenceDateTime).getSeconds();
    }

    /**
     * The rank for a VIP WorkOrder is calculated using the formula max(4, 2n log n) where n is the number of seconds that have elapsed from the reference DateTime.
     *
     * @param workOrder The VIP WorkOrder to compute the rank for.
     * @return The rank of the VIP WorkOrder
     */
    private double computeVIPRank(WorkOrder workOrder) {
        final double secondsElapsed = computeSecondsElapsed(workOrder);
        return Math.max(4, (2 * secondsElapsed) * Math.log(secondsElapsed));
    }

    /**
     * The rank for a Priority WorkOrder is calculated using the formula max(3, n log n) where n is the number of seconds that have elapsed from the reference DateTime.
     *
     * @param workOrder The Priority WorkOrder to compute the rank for.
     * @return The rank of the Priority WorkOrder
     */
    private double computePriorityRank(WorkOrder workOrder) {
        final double secondsElapsed = computeSecondsElapsed(workOrder);
        return Math.max(3, secondsElapsed * Math.log(secondsElapsed));
    }
}
<file_sep>package ie.jgriffin.priorityq.model.impl;

/**
 * Created by jgriffin on 18/04/2017.
 */
public class RankConstants {

    /**
     * The factor used to determine whether a WorkOrder's requestor Id is of type priority.
     */
    public static final long PRIORITY_FACTOR = 3L;
    /**
     * The factor used to determine whether a WorkOrder's requestor Id is of type VIP.
     */
    public static final long VIP_FACTOR = 5L;
    /**
     * The factor used to determine whether a WorkOrder's requestor Id is of type management override.
     */
    public static final long MANAGEMENT_OVERRIDE = PRIORITY_FACTOR * VIP_FACTOR;

}
<file_sep># F1zzBuzzPr10r1tyQ
This is a Spring Boot project which allows clients to add and remove WorkOrders to a priority queue in a RESTful manner.

# Building and running the app
This is a gradle project which uses the gradle wrapper.
On Windows the following commands can be used in the project's root folder

To build the project:
````
gradlew.bat build
````
To run the generated jar navigate to {projectDir}/build/libs then run:
````
java -jar app-1.0.jar
````
To run unit tests and generate jacoco reports run (from the project root), jacoco reports will be  at {projectDir}/build/reports/jacoco/test/html
````
gradlew.bat test jacocoTestReport
````
To generate javadocs run (from the project root), javadocs will be available at {projectDir}/build/docs/javadoc
````
gradlew.bat javadoc
````
SwaggerUI can be used to view documentation and test the REST API here when the app is running
````
http://localhost:8080/jgriffin-priorityq/api/v1/swagger-ui.html
````

# General Notes
* All REST API parameters are mandatory
* DateTime fields use the standard ISO-8601 DateTime format, example: "2017-04-18T23:28:12.000Z"
<file_sep>package ie.jgriffin.priorityq.config;

import ie.jgriffin.priorityq.persistence.WorkOrderQueue;
import ie.jgriffin.priorityq.persistence.impl.WorkOrderQueueImpl;
import ie.jgriffin.priorityq.rest.WorkOrderRestController;
import ie.jgriffin.priorityq.validation.WorkOrderValidator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * Created by jgriffin on 19/04/2017.
 * <p>
 * The JavaConfig class for this service, creates and configures all necessary beans.
 */
@Configuration
public class ApplicationConfig {

    @Bean
    public WorkOrderRestController workOrderRestController() {
        return new WorkOrderRestController(workOrderQueue(), workOrderValidator());
    }

    @Bean
    public WorkOrderQueue workOrderQueue() {
        return new WorkOrderQueueImpl();
    }

    @Bean
    public WorkOrderValidator workOrderValidator() {
        return new WorkOrderValidator();
    }

}
<file_sep>package ie.jgriffin.priorityq.model.impl;

import org.joda.time.DateTime;
import org.junit.Test;

import static org.junit.Assert.assertEquals;

/**
 * Created by jgriffin on 18/04/2017.
 */
public class WorkOrderImplTest {

    @Test
    public void constructor_ValidFields_GettersReturnPassedValues() {
        final Long id = 1L;
        final DateTime dateTime = DateTime.now();

        WorkOrderImpl workOrder = new WorkOrderImpl(id, dateTime);

        assertEquals("incorrect id", id, workOrder.getId());
        assertEquals("incorrect dateTime", dateTime, workOrder.getDateTime());
    }
}
<file_sep>server.contextPath=/jgriffin-priorityq/api/v1
spring.jackson.serialization.write_dates_as_timestamps=false
<file_sep>package ie.jgriffin.priorityq.model.impl;

import ie.jgriffin.priorityq.model.WorkOrder;


import java.io.Serializable;
import java.util.Comparator;

/**
 * Created by jgriffin on 18/04/2017.
 * <p>
 * The Comparator used to sort WorkOrders in the WorkOrderQueue by rank, higher ranked WorkOrders will be placed at the head of the queue.
 */
public class WorkOrderRankComparator implements Comparator<WorkOrder>, Serializable {

    private final RankComputer rankComputer;

    public WorkOrderRankComparator(RankComputer rankComputer) {
        this.rankComputer = rankComputer;
    }

    @Override
    public int compare(WorkOrder o1, WorkOrder o2) {
        boolean managementOverrideO1 = o1.getId() % RankConstants.MANAGEMENT_OVERRIDE == 0;
        boolean managementOverrideO2 = o2.getId() % RankConstants.MANAGEMENT_OVERRIDE == 0;

        //special case: if only one of the workOrders is a management override id then there is no need to compute rank
        if (managementOverrideO1 && !managementOverrideO2) {
            return -1;
        } else if (!managementOverrideO1 && managementOverrideO2) {
            return 1;
        } else {
            //the higher ranked objects should be at the head of the queue, so reverse the computed rank difference
            return -((Double) (rankComputer.compute(o1) - rankComputer.compute(o2))).intValue();
        }
    }
}
<file_sep>package ie.jgriffin.priorityq.model.impl;

import ie.jgriffin.priorityq.model.WorkOrder;
import org.joda.time.DateTime;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;

import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.when;

/**
 * Created by jgriffin on 18/04/2017.
 */
@RunWith(MockitoJUnitRunner.class)
public class WorkOrderComparatorTest {

    @InjectMocks
    private WorkOrderRankComparator workOrderComparator;

    @Mock
    private RankComputer mockRankComputer;
    @Mock
    private WorkOrder mockWO1;
    @Mock
    private WorkOrder mockWO2;

    @Test
    public void compare_BothComparatorsNotManagementOverride_InvertedRankDifferenceUsedToCompare(){
        when(mockWO1.getId()).thenReturn(RankConstants.PRIORITY_FACTOR);
        when(mockWO2.getId()).thenReturn(RankConstants.PRIORITY_FACTOR);
        when(mockRankComputer.compute(mockWO1)).thenReturn(10D);
        when(mockRankComputer.compute(mockWO2)).thenReturn(1D);

        int result = workOrderComparator.compare(mockWO1, mockWO2);

        assertTrue("incorrect compare result", result < 0);
    }

    @Test
    public void compare_ManagementOverrideFirst_NegativeResult(){
        when(mockWO1.getId()).thenReturn(RankConstants.MANAGEMENT_OVERRIDE);
        when(mockWO2.getId()).thenReturn(RankConstants.PRIORITY_FACTOR);

        int result = workOrderComparator.compare(mockWO1, mockWO2);

        assertTrue("incorrect compare result", result < 0);
    }

    @Test
    public void compare_ManagementOverrideSecond_PositiveResult(){
        when(mockWO1.getId()).thenReturn(RankConstants.PRIORITY_FACTOR);
        when(mockWO2.getId()).thenReturn(RankConstants.MANAGEMENT_OVERRIDE);

        int result = workOrderComparator.compare(mockWO1, mockWO2);

        assertTrue("incorrect compare result", result > 0);
    }

}
