├── _config.yml ├── PULL_REQUEST_TEMPLATE.md ├── CONTRIBUTING.md ├── src ├── main │ ├── resources │ │ ├── images │ │ │ ├── teddy.jpeg │ │ │ └── velentine.jpg │ │ ├── application.properties │ │ └── logback.xml │ └── java │ │ └── com │ │ └── fm │ │ └── assignment │ │ ├── core │ │ ├── enums │ │ │ ├── AttachmentYnEnum.java │ │ │ ├── MailStatusEnum.java │ │ │ └── TransportTypeEnum.java │ │ ├── params │ │ │ ├── PlaceParam.java │ │ │ ├── ResultParam.java │ │ │ ├── FindPathParam.java │ │ │ ├── MailBoxParam.java │ │ │ └── PathParam.java │ │ ├── service │ │ │ ├── MailBoxService.java │ │ │ ├── PlaceService.java │ │ │ ├── PathService.java │ │ │ ├── MailBoxServiceImpl.java │ │ │ ├── PlaceServiceImpl.java │ │ │ └── PathServiceImpl.java │ │ ├── dao │ │ │ ├── MailBoxRepository.java │ │ │ ├── PathRepository.java │ │ │ └── PlaceRepository.java │ │ ├── entity │ │ │ ├── PlaceEntity.java │ │ │ ├── MailBoxEntity.java │ │ │ ├── PathEntity.java │ │ │ └── BaseEntity.java │ │ ├── util │ │ │ ├── PathFinder.java │ │ │ ├── GraphBuilder.java │ │ │ └── ParamAndEntityBuilder.java │ │ └── validator │ │ │ └── FindPathValidator.java │ │ ├── util │ │ ├── Constants.java │ │ ├── ApiConstants.java │ │ ├── ApiUrlBuilder.java │ │ ├── RequestValidationMessage.java │ │ └── RequestAndParamBuilder.java │ │ ├── websocket │ │ ├── Notification.java │ │ └── NotificationHandler.java │ │ ├── api │ │ ├── model │ │ │ ├── FindPathResponse.java │ │ │ ├── PlaceResource.java │ │ │ ├── ResultsResource.java │ │ │ ├── RouteResource.java │ │ │ ├── PathResource.java │ │ │ └── FindPathRequest.java │ │ ├── controller │ │ │ ├── MailBoxController.java │ │ │ ├── PlaceController.java │ │ │ └── PathController.java │ │ └── validator │ │ │ └── FindPathValidator.java │ │ ├── mail │ │ ├── EmailService.java │ │ ├── EmailScheduler.java │ │ └── EmailServiceImpl.java │ │ ├── remote │ │ ├── LatLongModel.java │ │ ├── LatLongService.java │ │ └── LatLongServiceImpl.java │ │ ├── config │ │ ├── ApplicationRepositoryConfiguration.java │ │ ├── WebSocketConfig.java │ │ ├── HibernateConfig.java │ │ └── AppConfig.java │ │ ├── FreightManagementApplication.java │ │ ├── errorhandler │ │ ├── ErrorResponse.java │ │ ├── DatabaseException.java │ │ ├── RemoteApiException.java │ │ ├── ResourceNotFoundException.java │ │ ├── ErrorCodes.java │ │ └── ApplicationExceptionHandler.java │ │ └── tenant │ │ ├── CurrentTenantIdentifierResolverImpl.java │ │ ├── TenantContext.java │ │ ├── TenantInterceptor.java │ │ └── MultiTenantConnectionProviderImpl.java └── test │ ├── resource │ ├── logback-test.xml │ └── application.properties │ └── java │ └── com │ └── fm │ └── assignment │ ├── repository │ ├── constants │ │ ├── PlaceEntityConstants.java │ │ └── PathEntityConstants.java │ ├── PathRepositoryTest.java │ └── PlaceRepositoryTest.java │ └── FreightManagementApplicationTests.java ├── .gitattributes ├── sample ├── AllPathFind │ ├── 3-success │ │ ├── input.json │ │ └── output.json │ ├── 4-error │ │ ├── input.json │ │ └── output.json │ ├── 2-success │ │ ├── input.json │ │ └── output.json │ ├── 6-success │ │ ├── input.json │ │ └── output.json │ ├── 8-error │ │ ├── output.json │ │ └── input.json │ ├── 15-fail │ │ ├── output.json │ │ └── input.json │ ├── 5-error │ │ ├── output.json │ │ └── input.json │ ├── 10-error │ │ ├── output.json │ │ └── input.json │ ├── 11-error │ │ ├── output.json │ │ └── input.json │ ├── 12-error │ │ ├── output.json │ │ └── input.json │ ├── 14-fail │ │ ├── output.json │ │ └── input.json │ ├── 7-error │ │ ├── output.json │ │ └── input.json │ ├── 1-success │ │ ├── input.json │ │ └── output.json │ ├── 9-success │ │ ├── input.json │ │ └── output.json │ └── 13-success │ │ ├── input.json │ │ └── output.json └── CRUD Sample │ ├── AddPlace.json │ └── AddPath ├── .gitignore ├── server-config └── database │ ├── V3__freight_management_ddl.sql │ ├── V1__freight_management_ddl.sql │ └── V2__freight_management_dml.sql ├── .github └── ISSUE_TEMPLATE │ ├── Feature_request.md │ └── Bug_report.md ├── LICENSE ├── README.md └── CODE_OF_CONDUCT.md /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Describe your changes clearly 2 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Anyone can contribute to my project who knows java spring 2 | -------------------------------------------------------------------------------- /src/main/resources/images/teddy.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sanjoy-sust/FreightManagement/HEAD/src/main/resources/images/teddy.jpeg -------------------------------------------------------------------------------- /src/main/resources/images/velentine.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sanjoy-sust/FreightManagement/HEAD/src/main/resources/images/velentine.jpg -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | src/main/resources/static/* linguist-vendored 2 | project-docs/* linguist-documentation 3 | *.rb linguist-language=Spring Boot 4 | -------------------------------------------------------------------------------- /sample/AllPathFind/3-success/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "source": "Cox's Bazar", 3 | "destination": "Banani", 4 | "modeOfTransports": ["All"], 5 | "containerSize": 20 6 | } -------------------------------------------------------------------------------- /sample/AllPathFind/4-error/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "source": "Cox's Bazar", 3 | "destination": "Banani", 4 | "modeOfTransports": ["Ocean"], 5 | "containerSize": 20 6 | } -------------------------------------------------------------------------------- /sample/AllPathFind/2-success/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "source": "Cox's Bazar", 3 | "destination": "Banani,Dhaka", 4 | "modeOfTransports": ["All"], 5 | "containerSize": 20 6 | } -------------------------------------------------------------------------------- /sample/AllPathFind/6-success/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "source": "Cox's Bazar", 3 | "destination": "Uttara,Dhaka", 4 | "modeOfTransports": ["Road"], 5 | "containerSize": 40 6 | } -------------------------------------------------------------------------------- /sample/AllPathFind/8-error/output.json: -------------------------------------------------------------------------------- 1 | { 2 | "feature": "Path Find", 3 | "code": "ERR-230", 4 | "message": "Path not found for specific source and destination" 5 | } -------------------------------------------------------------------------------- /sample/AllPathFind/15-fail/output.json: -------------------------------------------------------------------------------- 1 | { 2 | "feature": "Unknown", 3 | "code": "ERR-990", 4 | "message": "findPathRequest : [Cost from must be smaller than cost to] " 5 | } -------------------------------------------------------------------------------- /sample/AllPathFind/4-error/output.json: -------------------------------------------------------------------------------- 1 | { 2 | "feature": "Path Find", 3 | "code": "ERR-230", 4 | "message": "Path not found for specific source and destination" 5 | } -------------------------------------------------------------------------------- /sample/AllPathFind/5-error/output.json: -------------------------------------------------------------------------------- 1 | { 2 | "feature": "Path Find", 3 | "code": "ERR-232", 4 | "message": "Destination not correct. Please try with another" 5 | } -------------------------------------------------------------------------------- /sample/AllPathFind/10-error/output.json: -------------------------------------------------------------------------------- 1 | { 2 | "feature": "Path Find", 3 | "code": "ERR-233", 4 | "message": "Please check Cost or Duration Range. Path not found for request." 5 | } -------------------------------------------------------------------------------- /sample/AllPathFind/11-error/output.json: -------------------------------------------------------------------------------- 1 | { 2 | "feature": "Path Find", 3 | "code": "ERR-233", 4 | "message": "Please check Cost or Duration Range. Path not found for request." 5 | } -------------------------------------------------------------------------------- /sample/AllPathFind/12-error/output.json: -------------------------------------------------------------------------------- 1 | { 2 | "feature": "Unknown", 3 | "code": "ERR-990", 4 | "message": "findPathRequest : [Duration from must be smaller than duration to] " 5 | } -------------------------------------------------------------------------------- /sample/AllPathFind/14-fail/output.json: -------------------------------------------------------------------------------- 1 | { 2 | "feature": "Unknown", 3 | "code": "ERR-990", 4 | "message": "findPathRequest : [Duration from must be smaller than duration to] " 5 | } -------------------------------------------------------------------------------- /sample/AllPathFind/7-error/output.json: -------------------------------------------------------------------------------- 1 | { 2 | "feature": "Path Find", 3 | "code": "ERR-234", 4 | "message": "Please check transportation mode and container size. Path not found for request." 5 | } -------------------------------------------------------------------------------- /sample/CRUD Sample/AddPlace.json: -------------------------------------------------------------------------------- 1 | //Request: 2 | 3 | { 4 | "name":"Comilla", 5 | "code":"CML", 6 | "latitude":23.46232, 7 | "longitude":91.188435 8 | 9 | } 10 | 11 | //response 12 | 45 -------------------------------------------------------------------------------- /sample/AllPathFind/5-error/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "source": "Cox's Bazar", 3 | "destination": "Uttara", 4 | "modeOfTransports": ["Road"], 5 | "containerSize": 40 6 | } 7 | 8 | /*This locations comes some place in india*/ -------------------------------------------------------------------------------- /src/main/java/com/fm/assignment/core/enums/AttachmentYnEnum.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment.core.enums; 2 | 3 | /** 4 | * Created by Lenovo on 13/02/2018. 5 | */ 6 | public enum AttachmentYnEnum { 7 | YES,NO 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/fm/assignment/core/enums/MailStatusEnum.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment.core.enums; 2 | 3 | /** 4 | * Created by Lenovo on 12/02/2018. 5 | */ 6 | public enum MailStatusEnum { 7 | PENDING,FAILED,SENT 8 | } 9 | -------------------------------------------------------------------------------- /sample/CRUD Sample/AddPath: -------------------------------------------------------------------------------- 1 | //Requset: 2 | 3 | { 4 | "from":"Comilla", 5 | "to":"Mohakhali", 6 | "cost":65, 7 | "containerSize":40, 8 | "routeType":"Road", 9 | "duration":1 10 | } 11 | 12 | //Response : 13 | 14 | 56756 -------------------------------------------------------------------------------- /sample/AllPathFind/1-success/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "source": "Stockholm", 3 | "destination": "Orlando", 4 | "modeOfTransports": ["Ocean", "Road"], 5 | "containerSize": 20, 6 | "durationFrom": 20, 7 | "durationTo": 25, 8 | "costFrom": 2500, 9 | "costTo": 4000 10 | } -------------------------------------------------------------------------------- /sample/AllPathFind/9-success/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "source": "Gothenburg", 3 | "destination": "Orlando", 4 | "modeOfTransports": ["Ocean", "Road"], 5 | "containerSize": 20, 6 | "durationFrom": 20, 7 | "durationTo": 250, 8 | "costFrom": 0, 9 | "costTo": 4000 10 | } -------------------------------------------------------------------------------- /sample/AllPathFind/10-error/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "source": "Gothenburg", 3 | "destination": "Orlando", 4 | "modeOfTransports": ["Ocean", "Road"], 5 | "containerSize": 20, 6 | "durationFrom": 20, 7 | "durationTo": 250, 8 | "costFrom": 3000, 9 | "costTo": 4000 10 | } -------------------------------------------------------------------------------- /sample/AllPathFind/11-error/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "source": "Gothenburg", 3 | "destination": "Orlando", 4 | "modeOfTransports": ["Ocean", "Road"], 5 | "containerSize": 20, 6 | "durationFrom": 250, 7 | "durationTo": 500, 8 | "costFrom": 20, 9 | "costTo": 4000 10 | } -------------------------------------------------------------------------------- /sample/AllPathFind/12-error/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "source": "Gothenburg", 3 | "destination": "Orlando", 4 | "modeOfTransports": ["Ocean", "Road"], 5 | "containerSize": 20, 6 | "durationFrom": 250, 7 | "durationTo": 0, 8 | "costFrom": 20, 9 | "costTo": 4000 10 | } 11 | -------------------------------------------------------------------------------- /sample/AllPathFind/13-success/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "source": "Cox's Bazar", 3 | "destination": "Chitagong", 4 | "modeOfTransports": ["Ocean", "Road"], 5 | "containerSize": 20, 6 | "durationFrom": 0, 7 | "durationTo": 300, 8 | "costFrom": 20, 9 | "costTo": 4000 10 | } -------------------------------------------------------------------------------- /sample/AllPathFind/14-fail/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "source": "Cox's Bazar", 3 | "destination": "Chitagong", 4 | "modeOfTransports": ["Ocean", "Road"], 5 | "containerSize": 20, 6 | "durationFrom": 500, 7 | "durationTo": 300, 8 | "costFrom": 20, 9 | "costTo": 4000 10 | } -------------------------------------------------------------------------------- /sample/AllPathFind/15-fail/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "source": "Cox's Bazar", 3 | "destination": "Chitagong", 4 | "modeOfTransports": ["Ocean", "Road"], 5 | "containerSize": 20, 6 | "durationFrom": 0, 7 | "durationTo": 300, 8 | "costFrom": 20000, 9 | "costTo": 4000 10 | } -------------------------------------------------------------------------------- /src/main/java/com/fm/assignment/util/Constants.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment.util; 2 | 3 | /** 4 | * @author Sanjoy Kumer Deb 5 | * @since 10/10/2017. 6 | */ 7 | public class Constants { 8 | public final static Double MINIMUM_DISTANCE_TO_NEAR_LOCATION = 50.0; 9 | } 10 | -------------------------------------------------------------------------------- /sample/AllPathFind/8-error/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "source": "Savannah", 3 | "destination": "Ft. Lauderdale", 4 | "modeOfTransports": ["Ocean", "Road"], 5 | "containerSize": 20, 6 | "durationFrom": 20, 7 | "durationTo": 250, 8 | "costFrom": 2500, 9 | "costTo": 4000 10 | } -------------------------------------------------------------------------------- /src/main/java/com/fm/assignment/websocket/Notification.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment.websocket; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * Created by Lenovo on 14/02/2018. 7 | */ 8 | @Data 9 | public class Notification { 10 | String sender; 11 | String message; 12 | } 13 | -------------------------------------------------------------------------------- /sample/AllPathFind/7-error/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "source": "Stockholm", 3 | "destination": "Orlando", 4 | "modeOfTransports": ["Ocean", "Road"], 5 | "containerSize": 100,//Container Size mismatch 6 | "durationFrom": 20, 7 | "durationTo": 25, 8 | "costFrom": 2500, 9 | "costTo": 4000 10 | } -------------------------------------------------------------------------------- /src/main/java/com/fm/assignment/api/model/FindPathResponse.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment.api.model; 2 | 3 | import lombok.Data; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * @author Sanjoy Kumer Deb 9 | * @since 06/10/2017. 10 | */ 11 | @Data 12 | public class FindPathResponse { 13 | private List results; 14 | } 15 | -------------------------------------------------------------------------------- /src/test/resource/logback-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /sample/AllPathFind/13-success/output.json: -------------------------------------------------------------------------------- 1 | { 2 | "results": [ 3 | { 4 | "route": [ 5 | { 6 | "from": "Cox's Bazar", 7 | "to": "Chittagong", 8 | "transportType": "Road", 9 | "cost": 50, 10 | "duration": 1 11 | } 12 | ], 13 | "totalCost": 50, 14 | "totalDuration": 1 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /build/ 3 | !gradle/wrapper/gradle-wrapper.jar 4 | 5 | ### STS ### 6 | .apt_generated 7 | .classpath 8 | .factorypath 9 | .project 10 | .settings 11 | .springBeans 12 | 13 | ### IntelliJ IDEA ### 14 | .idea 15 | *.iws 16 | *.iml 17 | *.ipr 18 | 19 | ### NetBeans ### 20 | nbproject/private/ 21 | build/ 22 | nbbuild/ 23 | dist/ 24 | nbdist/ 25 | classes/ 26 | .nb-gradle/ -------------------------------------------------------------------------------- /src/main/java/com/fm/assignment/mail/EmailService.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment.mail; 2 | 3 | import javax.mail.MessagingException; 4 | import java.io.IOException; 5 | 6 | /** 7 | * Created by Lenovo on 08/02/2018. 8 | */ 9 | public interface EmailService { 10 | void sendSimpleMessage( 11 | String to, String subject, String text) throws IOException, MessagingException; 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/fm/assignment/remote/LatLongModel.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment.remote; 2 | 3 | import lombok.Data; 4 | import lombok.Getter; 5 | import lombok.experimental.Builder; 6 | 7 | /** 8 | * @author Sanjoy Kumer Deb 9 | * @since 10/10/2017. 10 | */ 11 | @Builder 12 | @Getter 13 | public class LatLongModel { 14 | private Double latitude; 15 | private Double longitude; 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/fm/assignment/api/model/PlaceResource.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment.api.model; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * @author Sanjoy Kumer Deb 7 | * @since 06/10/2017. 8 | */ 9 | @Data 10 | public class PlaceResource { 11 | private long id; 12 | private String name; 13 | private String code; 14 | private Double longitude; 15 | private Double latitude; 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/fm/assignment/api/model/ResultsResource.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment.api.model; 2 | 3 | import lombok.Data; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * @author Sanjoy Kumer Deb 9 | * @since 06/10/2017. 10 | */ 11 | @Data 12 | public class ResultsResource { 13 | private List route; 14 | private Double totalCost; 15 | private Long totalDuration; 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/fm/assignment/config/ApplicationRepositoryConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment.config; 2 | 3 | import org.springframework.context.annotation.Configuration; 4 | import org.springframework.data.jpa.repository.config.EnableJpaAuditing; 5 | 6 | /** 7 | * Created by Lenovo on 18/01/2018. 8 | */ 9 | @Configuration 10 | @EnableJpaAuditing 11 | public class ApplicationRepositoryConfiguration { 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/fm/assignment/core/params/PlaceParam.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment.core.params; 2 | 3 | import lombok.Getter; 4 | import lombok.experimental.Builder; 5 | 6 | /** 7 | * Created by Lenovo on 13/10/2017. 8 | */ 9 | @Builder 10 | @Getter 11 | public class PlaceParam { 12 | private long id; 13 | private String name; 14 | private String code; 15 | private Double longitude; 16 | private Double latitude; 17 | } 18 | -------------------------------------------------------------------------------- /src/test/resource/application.properties: -------------------------------------------------------------------------------- 1 | spring.datasource.url=jdbc:mysql://localhost:3306/frieght_management 2 | spring.datasource.username=root 3 | spring.datasource.password=admin 4 | endpoints.actuator.enabled=true 5 | endpoints.info.enabled=true 6 | 7 | spring.jpa.properties.hibernate.dialect = org.hibernate.spatial.dialect.mysql.MySQLSpatial5InnoDBDialect 8 | spring.jpa.database-platform = org.hibernate.spatial.dialect.mysql.MySQLSpatial5InnoDBDialect -------------------------------------------------------------------------------- /src/main/java/com/fm/assignment/remote/LatLongService.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment.remote; 2 | 3 | import com.fm.assignment.errorhandler.RemoteApiException; 4 | 5 | /** 6 | * @author Sanjoy Kumer Deb 7 | * @since 10/10/2017. 8 | */ 9 | public interface LatLongService { 10 | LatLongModel getLatLongPositions(String address) throws RemoteApiException; 11 | 12 | double distance(double lat1, double lon1, double lat2, double lon2); 13 | } 14 | -------------------------------------------------------------------------------- /src/test/java/com/fm/assignment/repository/constants/PlaceEntityConstants.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment.repository.constants; 2 | 3 | /** 4 | * Created by Lenovo on 21/01/2018. 5 | */ 6 | public class PlaceEntityConstants { 7 | public static final String PLACE_NAME = "DD"; 8 | public static final String PLACE_CODE = "CMI"; 9 | public static final Double PLACE_LATITUDE= 23.894929; 10 | public static final Double PLACE_LONGITUDE = 90.868706; 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/fm/assignment/core/params/ResultParam.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment.core.params; 2 | 3 | import com.fm.assignment.api.model.RouteResource; 4 | import lombok.Getter; 5 | import lombok.experimental.Builder; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * Created by Lenovo on 13/10/2017. 11 | */ 12 | 13 | @Builder 14 | @Getter 15 | public class ResultParam { 16 | private List route; 17 | private Double totalCost; 18 | private Long totalDuration; 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/fm/assignment/FreightManagementApplication.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | /** 7 | * @author Sanjoy Kumer Deb 8 | * @since 10/10/2017. 9 | */ 10 | 11 | @SpringBootApplication 12 | public class FreightManagementApplication { 13 | 14 | public static void main(String[] args) { 15 | SpringApplication.run(FreightManagementApplication.class, args); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/fm/assignment/errorhandler/ErrorResponse.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment.errorhandler; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * The {@code ErrorResponse} class represents response for any Exception. 7 | *

8 | * Feature identifies which feature Exception Occured. 9 | Code uniquely defined exception 10 | *

11 | * @author Sanjoy Kumer Deb 12 | * @since 06/10/2017. 13 | */ 14 | @Data 15 | public class ErrorResponse { 16 | String feature; 17 | String code; 18 | String message; 19 | } 20 | -------------------------------------------------------------------------------- /src/test/java/com/fm/assignment/repository/constants/PathEntityConstants.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment.repository.constants; 2 | 3 | /** 4 | * Created by Lenovo on 22/01/2018. 5 | */ 6 | public class PathEntityConstants { 7 | public static final String FROM_CODE = "CMI1"; 8 | public static final String TO_CODE = "CMI2"; 9 | public static final Double COST = 10.0; 10 | public static final Long CONTAINER_SIZE = 10L; 11 | public static final String ROUTE_TYPE = "ALL"; 12 | public static final Long DURATION = 100L; 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/fm/assignment/core/service/MailBoxService.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment.core.service; 2 | 3 | import com.fm.assignment.core.enums.MailStatusEnum; 4 | import com.fm.assignment.core.params.MailBoxParam; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * Created by Lenovo on 13/02/2018. 10 | */ 11 | 12 | public interface MailBoxService { 13 | long addMailBox(MailBoxParam param); 14 | long updateMailBox(MailBoxParam param); 15 | List getAllMailBox(); 16 | List getMailBoxByStatus(MailStatusEnum status); 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/fm/assignment/errorhandler/DatabaseException.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment.errorhandler; 2 | 3 | import lombok.Getter; 4 | 5 | /** 6 | * Created by Lenovo on 08/10/2017. 7 | */ 8 | @Getter 9 | public class DatabaseException extends Exception { 10 | private String code; 11 | private String feature; 12 | private String reason; 13 | 14 | public DatabaseException(String feature, String code, String reason) { 15 | super(reason); 16 | this.reason=reason; 17 | this.feature = feature; 18 | this.code = code; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/fm/assignment/api/model/RouteResource.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment.api.model; 2 | 3 | import com.fm.assignment.core.enums.TransportTypeEnum; 4 | import lombok.Data; 5 | import lombok.experimental.Builder; 6 | 7 | import javax.persistence.EnumType; 8 | import javax.persistence.Enumerated; 9 | 10 | /** 11 | * @author Sanjoy Kumer Deb 12 | * @since 06/10/2017. 13 | */ 14 | @Data 15 | public class RouteResource { 16 | String from; 17 | String to; 18 | @Enumerated(EnumType.STRING) 19 | TransportTypeEnum transportType; 20 | Double cost; 21 | Long duration; 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/fm/assignment/errorhandler/RemoteApiException.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment.errorhandler; 2 | 3 | import lombok.Getter; 4 | 5 | /** 6 | * @author Sanjoy Kumer Deb 7 | * @since 06/10/2017. 8 | */ 9 | @Getter 10 | public class RemoteApiException extends Exception{ 11 | private String code; 12 | private String feature; 13 | private String reason; 14 | 15 | public RemoteApiException(String feature, String code, String reason) { 16 | super(reason); 17 | this.reason=reason; 18 | this.feature = feature; 19 | this.code = code; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/fm/assignment/api/model/PathResource.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment.api.model; 2 | 3 | import com.fm.assignment.core.enums.TransportTypeEnum; 4 | import lombok.Data; 5 | 6 | import javax.persistence.*; 7 | 8 | /** 9 | * @author Sanjoy Kumer Deb 10 | * @since 06/10/2017. 11 | */ 12 | @Data 13 | public class PathResource { 14 | private long id; 15 | private String from; 16 | private String to; 17 | private Double cost; 18 | private long containerSize; 19 | @Enumerated(EnumType.STRING) 20 | private TransportTypeEnum routeType; 21 | private long duration; 22 | } 23 | -------------------------------------------------------------------------------- /server-config/database/V3__freight_management_ddl.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE `freight_management`.`mail_box` ( 2 | `ID` int(11) NOT NULL AUTO_INCREMENT, 3 | `TO_EMAIL` varchar(45) NOT NULL, 4 | `SUBJECT` varchar(45) NOT NULL, 5 | `BODY_TEXT` varchar(255) NOT NULL, 6 | `attachment_yn` varchar(10) NOT NULL, 7 | `attachment_name` varchar(255), 8 | `status` varchar(255) NOT NULL, 9 | `CREATED_BY` varchar(255) NOT NULL DEFAULT 'ADMIN', 10 | `UPDATED_BY` varchar(255) DEFAULT NULL, 11 | `CREATED` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 12 | `UPDATED` timestamp NULL DEFAULT NULL, 13 | PRIMARY KEY (`ID`) 14 | ) ; 15 | -------------------------------------------------------------------------------- /src/main/java/com/fm/assignment/core/params/FindPathParam.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment.core.params; 2 | 3 | import com.fm.assignment.core.enums.TransportTypeEnum; 4 | import lombok.Getter; 5 | import lombok.experimental.Builder; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * Created by Lenovo on 13/10/2017. 11 | */ 12 | @Builder 13 | @Getter 14 | public class FindPathParam { 15 | String source; 16 | String destination; 17 | List modeOfTransports; 18 | Long containerSize; 19 | Long durationFrom; 20 | Long durationTo; 21 | Double costFrom; 22 | Double costTo; 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/fm/assignment/errorhandler/ResourceNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment.errorhandler; 2 | 3 | import lombok.Getter; 4 | 5 | /** 6 | * @author Sanjoy Kumer Deb 7 | * @since 06/10/2017. 8 | */ 9 | @Getter 10 | public class ResourceNotFoundException extends Exception { 11 | private String code; 12 | private String feature; 13 | private String reason; 14 | 15 | public ResourceNotFoundException(String feature, String code, String reason) { 16 | super(reason); 17 | this.reason=reason; 18 | this.feature = feature; 19 | this.code = code; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/fm/assignment/api/model/FindPathRequest.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment.api.model; 2 | 3 | import com.fm.assignment.core.enums.TransportTypeEnum; 4 | import lombok.Data; 5 | 6 | import javax.validation.constraints.NotNull; 7 | import java.util.List; 8 | 9 | /** 10 | * @author Sanjoy Kumer Deb 11 | * @since 06/10/2017. 12 | */ 13 | @Data 14 | public class FindPathRequest { 15 | String source; 16 | String destination; 17 | List modeOfTransports; 18 | Long containerSize; 19 | Long durationFrom; 20 | Long durationTo; 21 | Double costFrom; 22 | Double costTo; 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/fm/assignment/core/params/MailBoxParam.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment.core.params; 2 | 3 | import com.fm.assignment.core.enums.AttachmentYnEnum; 4 | import com.fm.assignment.core.enums.MailStatusEnum; 5 | import lombok.Data; 6 | 7 | import javax.persistence.*; 8 | 9 | /** 10 | * Created by Lenovo on 13/02/2018. 11 | */ 12 | @Data 13 | public class MailBoxParam { 14 | 15 | private long id; 16 | private String toEmail; 17 | private String subject; 18 | private String text; 19 | private AttachmentYnEnum attachmentYN; 20 | private String attachmentName; 21 | private MailStatusEnum status; 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/fm/assignment/tenant/CurrentTenantIdentifierResolverImpl.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment.tenant; 2 | 3 | import org.hibernate.context.spi.CurrentTenantIdentifierResolver; 4 | import org.springframework.stereotype.Component; 5 | 6 | /** 7 | * Created by Lenovo on 18/01/2018. 8 | */ 9 | @Component 10 | public class CurrentTenantIdentifierResolverImpl implements CurrentTenantIdentifierResolver { 11 | 12 | @Override 13 | public String resolveCurrentTenantIdentifier() { 14 | return TenantContext.getCurrentTenant(); 15 | } 16 | 17 | @Override 18 | public boolean validateExistingCurrentSessions() { 19 | return true; 20 | } 21 | } -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/Feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | 5 | --- 6 | 7 | **Is your feature request related to a problem? Please describe.** 8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 9 | 10 | **Describe the solution you'd like** 11 | A clear and concise description of what you want to happen. 12 | 13 | **Describe alternatives you've considered** 14 | A clear and concise description of any alternative solutions or features you've considered. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /src/main/java/com/fm/assignment/core/enums/TransportTypeEnum.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment.core.enums; 2 | 3 | /** 4 | * @author Sanjoy Kumer Deb 5 | * @since 06/10/2017. 6 | */ 7 | public enum TransportTypeEnum { 8 | /*This is not recommended enum as camel case. 9 | * @TODO will use below code.But for simplicity we just use this code. 10 | * */ 11 | Road,Ocean,Air,All; 12 | 13 | /* ROAD("Road"),OCEAN("Ocean"),AIR("Air"),ALL("All"); 14 | private String value; 15 | private TransportTypeEnum(String value) 16 | { 17 | this.value = value; 18 | } 19 | 20 | public String getValue() 21 | { 22 | return this.value; 23 | }*/ 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/fm/assignment/core/params/PathParam.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment.core.params; 2 | 3 | import com.fm.assignment.core.enums.TransportTypeEnum; 4 | import lombok.Getter; 5 | import lombok.experimental.Builder; 6 | 7 | import javax.persistence.EnumType; 8 | import javax.persistence.Enumerated; 9 | 10 | /** 11 | * Created by Lenovo on 13/10/2017. 12 | */ 13 | @Builder 14 | @Getter 15 | public class PathParam { 16 | private long id; 17 | private String from; 18 | private String to; 19 | private Double cost; 20 | private long containerSize; 21 | @Enumerated(EnumType.STRING) 22 | private TransportTypeEnum routeType; 23 | private long duration; 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/fm/assignment/core/dao/MailBoxRepository.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment.core.dao; 2 | 3 | import com.fm.assignment.core.entity.MailBoxEntity; 4 | import com.fm.assignment.core.enums.MailStatusEnum; 5 | import org.springframework.data.jpa.repository.JpaRepository; 6 | import org.springframework.stereotype.Repository; 7 | import org.springframework.transaction.annotation.Transactional; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * Created by Lenovo on 13/02/2018. 13 | */ 14 | @Repository 15 | @Transactional(readOnly = true) 16 | public interface MailBoxRepository extends JpaRepository { 17 | List findByStatus(MailStatusEnum status); 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/fm/assignment/tenant/TenantContext.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment.tenant; 2 | 3 | /** 4 | * Created by Lenovo on 18/01/2018. 5 | */ 6 | public class TenantContext { 7 | final public static String DEFAULT_TENANT = "freight_management"; 8 | 9 | private static ThreadLocal currentTenant = new ThreadLocal() 10 | { 11 | @Override 12 | protected String initialValue() { 13 | return DEFAULT_TENANT; 14 | } 15 | }; 16 | 17 | public static void setCurrentTenant(String tenant) { 18 | currentTenant.set(tenant); 19 | } 20 | 21 | public static String getCurrentTenant() { 22 | return currentTenant.get(); 23 | } 24 | 25 | public static void clear() { 26 | currentTenant.remove(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/fm/assignment/util/ApiConstants.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment.util; 2 | 3 | /** 4 | * @author Sanjoy Kumer Deb 5 | * @since 10/10/2017. 6 | */ 7 | public class ApiConstants { 8 | /*Added constants for google map api to find latitude and longitude of an address*/ 9 | public static final String GOOGLE_MAP_URL = "http://maps.googleapis.com/maps/api/geocode/xml"; 10 | public static final String GOOGLE_MAP_ADDRESS_PREFIX_URL = "?address="; 11 | public static final String GOOGLE_MAP_ADDRESS_SUFFIX_URL = "&sensor=true"; 12 | public static final String XPATH_STATUS_FIELD = "/GeocodeResponse/status"; 13 | public static final String XPATH_LATITUDE_FIELD = "//geometry/location/lat"; 14 | public static final String XPATH_LONGITUDE_FIELD = "//geometry/location/lng"; 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/fm/assignment/core/service/PlaceService.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment.core.service; 2 | 3 | import com.fm.assignment.api.model.PlaceResource; 4 | import com.fm.assignment.core.entity.PlaceEntity; 5 | import com.fm.assignment.core.params.PlaceParam; 6 | import com.fm.assignment.errorhandler.DatabaseException; 7 | import com.vividsolutions.jts.geom.Geometry; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * @author Sanjoy Kumer Deb 13 | * @since 07/10/2017. 14 | */ 15 | public interface PlaceService { 16 | Long addPlace(PlaceParam param) throws DatabaseException; 17 | Long updatePlace(long id,PlaceParam param); 18 | List getAllNearestPlaces(Double latitude,Double longitude,Double distance); 19 | List getAllPlaces(); 20 | PlaceParam findOne(long id); 21 | } 22 | -------------------------------------------------------------------------------- /src/test/java/com/fm/assignment/FreightManagementApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment; 2 | 3 | import com.fm.assignment.api.controller.PathController; 4 | import com.fm.assignment.core.dao.PathRepository; 5 | import com.fm.assignment.core.entity.PathEntity; 6 | import org.junit.Assert; 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.boot.test.context.SpringBootTest; 11 | import org.springframework.test.context.junit4.SpringRunner; 12 | 13 | import java.util.List; 14 | 15 | 16 | @RunWith(SpringRunner.class) 17 | @SpringBootTest 18 | public class FreightManagementApplicationTests { 19 | @Autowired 20 | PathController pathController; 21 | @Test 22 | public void contextLoads(){ 23 | 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/fm/assignment/util/ApiUrlBuilder.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment.util; 2 | 3 | import java.io.UnsupportedEncodingException; 4 | import java.net.URLEncoder; 5 | 6 | /** 7 | * @author Sanjoy Kumer Deb 8 | * @since 10/10/2017. 9 | */ 10 | public class ApiUrlBuilder { 11 | /** 12 | * Build google map api url with address 13 | * @param address 14 | * @return 15 | * @throws UnsupportedEncodingException 16 | */ 17 | public static String buildGoogleMapApiUrl(String address) throws UnsupportedEncodingException { 18 | return ApiConstants.GOOGLE_MAP_URL 19 | .concat(ApiConstants.GOOGLE_MAP_ADDRESS_PREFIX_URL) 20 | .concat(URLEncoder.encode(address, "UTF-8")) 21 | .concat(ApiConstants.GOOGLE_MAP_ADDRESS_SUFFIX_URL); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/fm/assignment/util/RequestValidationMessage.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment.util; 2 | 3 | /** 4 | * Created by Sanjoy on 10/11/2017. 5 | */ 6 | public class RequestValidationMessage { 7 | public static final String COST_FROM_GREATER_THAN_TO = "Cost from must be smaller than cost to"; 8 | public static final String DURATION_FROM_GREATER_THAN_TO = "Duration from must be smaller than duration to"; 9 | public static final String CONTAINER_SIZE_NOT_NULL= "Container size is mandatory field. It will be greater than 0"; 10 | public static final String SOURCE_NOT_NULL= "Source can not be null or empty"; 11 | public static final String DESTINATION_NOT_NULL= "Destination can not be null or empty"; 12 | public static final String TRANSPORT_NOT_NULL= "Transport is mandatory field. Please give at least one transport type"; 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/fm/assignment/core/dao/PathRepository.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment.core.dao; 2 | 3 | import com.fm.assignment.core.entity.PathEntity; 4 | import com.fm.assignment.core.enums.TransportTypeEnum; 5 | import org.springframework.data.jpa.repository.JpaRepository; 6 | import org.springframework.data.jpa.repository.Query; 7 | import org.springframework.data.repository.query.Param; 8 | import org.springframework.stereotype.Repository; 9 | import org.springframework.transaction.annotation.Transactional; 10 | 11 | import java.util.List; 12 | 13 | /** 14 | * @author Sanjoy Kumer Deb 15 | * @since 06/10/2017. 16 | */ 17 | @Repository 18 | @Transactional(readOnly = true) 19 | public interface PathRepository extends JpaRepository { 20 | List findByRouteType(TransportTypeEnum routeType); 21 | List findByRouteTypeAndContainerSize(TransportTypeEnum routeType,long containerSize); 22 | List findByContainerSize(long containerSize); 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/fm/assignment/core/entity/PlaceEntity.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment.core.entity; 2 | 3 | import lombok.Data; 4 | import org.hibernate.annotations.Type; 5 | 6 | import javax.persistence.*; 7 | 8 | import com.vividsolutions.jts.geom.Point; 9 | 10 | /** 11 | * Here code field just use for unique Place Id. 12 | * Although in this project name also unique. 13 | * Keeping it for next extended implementation. 14 | * And currently used as foreign key of PathEntity. 15 | * @author Sanjoy Kumer Deb 16 | * @since 07/10/2017. 17 | */ 18 | @Data 19 | @Entity(name = "Place") 20 | public class PlaceEntity extends BaseEntity { 21 | @Id 22 | @GeneratedValue 23 | @Column(name = "ID") 24 | private long id; 25 | @Column(name = "NAME") 26 | private String name; 27 | @Column(name = "CODE") 28 | private String code; 29 | @Column(name = "LONGITUDE") 30 | private Double longitude; 31 | @Column(name = "LATITUDE") 32 | private Double latitude; 33 | 34 | } 35 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/Bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | **Describe the bug** 8 | A clear and concise description of what the bug is. 9 | 10 | **To Reproduce** 11 | Steps to reproduce the behavior: 12 | 1. Go to '...' 13 | 2. Click on '....' 14 | 3. Scroll down to '....' 15 | 4. See error 16 | 17 | **Expected behavior** 18 | A clear and concise description of what you expected to happen. 19 | 20 | **Screenshots** 21 | If applicable, add screenshots to help explain your problem. 22 | 23 | **Desktop (please complete the following information):** 24 | - OS: [e.g. iOS] 25 | - Browser [e.g. chrome, safari] 26 | - Version [e.g. 22] 27 | 28 | **Smartphone (please complete the following information):** 29 | - Device: [e.g. iPhone6] 30 | - OS: [e.g. iOS8.1] 31 | - Browser [e.g. stock browser, safari] 32 | - Version [e.g. 22] 33 | 34 | **Additional context** 35 | Add any other context about the problem here. 36 | -------------------------------------------------------------------------------- /src/main/java/com/fm/assignment/websocket/NotificationHandler.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment.websocket; 2 | 3 | import org.springframework.messaging.handler.annotation.MessageMapping; 4 | import org.springframework.messaging.handler.annotation.SendTo; 5 | import org.springframework.stereotype.Controller; 6 | import org.springframework.web.bind.annotation.RestController; 7 | 8 | import java.text.SimpleDateFormat; 9 | import java.util.Date; 10 | 11 | /** 12 | * Created by Lenovo on 14/02/2018. 13 | */ 14 | @Controller 15 | public class NotificationHandler { 16 | 17 | @MessageMapping("/notify") 18 | @SendTo("/topic/messages") 19 | public Notification send(String message) throws Exception { 20 | String time = new SimpleDateFormat("HH:mm").format(new Date()); 21 | Notification notification = new Notification(); 22 | notification.setSender("Flopcoder"); 23 | notification.setMessage("Welcome to my freight management project".concat(message)); 24 | return notification; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/fm/assignment/core/entity/MailBoxEntity.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment.core.entity; 2 | 3 | import com.fm.assignment.core.enums.AttachmentYnEnum; 4 | import com.fm.assignment.core.enums.MailStatusEnum; 5 | import lombok.Data; 6 | 7 | import javax.persistence.*; 8 | 9 | /** 10 | * Created by Lenovo on 12/02/2018. 11 | */ 12 | @Data 13 | @Entity(name = "MAIL_BOX") 14 | public class MailBoxEntity extends BaseEntity { 15 | 16 | @Id 17 | @GeneratedValue 18 | @Column(name = "ID") 19 | private long id; 20 | @Column(name = "TO_EMAIL") 21 | private String toEmail; 22 | @Column(name = "SUBJECT") 23 | private String subject; 24 | @Column(name = "BODY_TEXT") 25 | private String text; 26 | @Column(name = "ATTACHMENT_YN") 27 | @Enumerated(EnumType.STRING) 28 | private AttachmentYnEnum attachmentYN; 29 | @Column(name = "ATTACHMENT_NAME") 30 | private String attachmentName; 31 | @Column(name = "STATUS") 32 | @Enumerated(EnumType.STRING) 33 | private MailStatusEnum status; 34 | } 35 | -------------------------------------------------------------------------------- /sample/AllPathFind/9-success/output.json: -------------------------------------------------------------------------------- 1 | { 2 | "results": [ 3 | { 4 | "route": [ 5 | { 6 | "from": "Gothenburg", 7 | "to": "Savannah", 8 | "transportType": "Ocean", 9 | "cost": 1765, 10 | "duration": 23 11 | }, 12 | { 13 | "from": "Savannah", 14 | "to": "Orlando", 15 | "transportType": "Road", 16 | "cost": 600, 17 | "duration": 1 18 | } 19 | ], 20 | "totalCost": 2365, 21 | "totalDuration": 24 22 | }, 23 | { 24 | "route": [ 25 | { 26 | "from": "Gothenburg", 27 | "to": "Ft. Lauderdale", 28 | "transportType": "Ocean", 29 | "cost": 1623, 30 | "duration": 22 31 | }, 32 | { 33 | "from": "Ft. Lauderdale", 34 | "to": "Orlando", 35 | "transportType": "Road", 36 | "cost": 600, 37 | "duration": 1 38 | } 39 | ], 40 | "totalCost": 2223, 41 | "totalDuration": 23 42 | } 43 | ] 44 | } -------------------------------------------------------------------------------- /src/main/java/com/fm/assignment/core/service/PathService.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment.core.service; 2 | 3 | import com.fm.assignment.core.params.FindPathParam; 4 | import com.fm.assignment.core.params.PathParam; 5 | import com.fm.assignment.core.params.ResultParam; 6 | import com.fm.assignment.errorhandler.DatabaseException; 7 | import com.fm.assignment.errorhandler.ResourceNotFoundException; 8 | import com.fm.assignment.api.model.FindPathRequest; 9 | import com.fm.assignment.api.model.FindPathResponse; 10 | import com.fm.assignment.api.model.PathResource; 11 | import org.springframework.stereotype.Service; 12 | 13 | import java.util.List; 14 | 15 | /** 16 | * @author Sanjoy Kumer Deb 17 | * @since 10/10/2017. 18 | */ 19 | @Service 20 | public interface PathService { 21 | long addPath(PathParam pathParam) throws ResourceNotFoundException, DatabaseException; 22 | List getAllPaths(FindPathParam param) throws Exception; 23 | PathParam findById(long id); 24 | List findAll(); 25 | Long updateOne(long id, PathParam pathParam) throws ResourceNotFoundException; 26 | } 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Sanjoy Kumer Deb 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/main/java/com/fm/assignment/core/entity/PathEntity.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment.core.entity; 2 | 3 | import com.fm.assignment.core.enums.TransportTypeEnum; 4 | import lombok.Data; 5 | 6 | import javax.persistence.*; 7 | 8 | /** 9 | * This Entity is for Path from one place to another place. 10 | * @author Sanjoy Kumer Deb 11 | * @since 07/10/2017. 12 | */ 13 | @Data 14 | @Entity(name = "PATH") 15 | public class PathEntity extends BaseEntity { 16 | @Id 17 | @GeneratedValue 18 | @Column(name = "ID") 19 | private long id; 20 | 21 | @ManyToOne 22 | @JoinColumn(name = "FROM_CODE", referencedColumnName = "CODE") 23 | private PlaceEntity fromCode; 24 | 25 | @ManyToOne 26 | @JoinColumn(name = "TO_CODE", referencedColumnName = "CODE") 27 | private PlaceEntity toCode; 28 | 29 | @Column(name = "COST_EURO") 30 | private Double cost; 31 | 32 | @Column(name = "CONTAINER_SIZE_FEET") 33 | private long containerSize; 34 | 35 | @Column(name = "ROUTE_TYPE") 36 | @Enumerated(EnumType.STRING) 37 | private TransportTypeEnum routeType; 38 | 39 | @Column(name = "DURATION_DAY") 40 | private long duration; 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/fm/assignment/config/WebSocketConfig.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment.config; 2 | 3 | import org.springframework.context.annotation.Configuration; 4 | import org.springframework.messaging.simp.config.MessageBrokerRegistry; 5 | import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer; 6 | import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; 7 | import org.springframework.web.socket.config.annotation.StompEndpointRegistry; 8 | 9 | /** 10 | * Created by Lenovo on 14/02/2018. 11 | */ 12 | @Configuration 13 | @EnableWebSocketMessageBroker 14 | public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer { 15 | 16 | @Override 17 | public void registerStompEndpoints(StompEndpointRegistry registry) { 18 | registry.addEndpoint("/notify").setAllowedOrigins("*"); 19 | registry.addEndpoint("/notify").setAllowedOrigins("*").withSockJS(); 20 | } 21 | 22 | @Override 23 | public void configureMessageBroker(MessageBrokerRegistry config) { 24 | config.enableSimpleBroker("/topic"); 25 | config.setApplicationDestinationPrefixes("/freight"); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/fm/assignment/api/controller/MailBoxController.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment.api.controller; 2 | 3 | import com.fm.assignment.core.enums.AttachmentYnEnum; 4 | import com.fm.assignment.core.enums.MailStatusEnum; 5 | import com.fm.assignment.core.params.MailBoxParam; 6 | import com.fm.assignment.core.service.MailBoxService; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.web.bind.annotation.PostMapping; 9 | import org.springframework.web.bind.annotation.RequestMapping; 10 | import org.springframework.web.bind.annotation.RestController; 11 | 12 | /** 13 | * Created by Lenovo on 13/02/2018. 14 | * This Class only for Test 15 | */ 16 | @RestController 17 | @RequestMapping(value = "mailbox") 18 | public class MailBoxController { 19 | 20 | @Autowired 21 | private MailBoxService mailBoxService; 22 | 23 | @PostMapping 24 | public long addMailBox() 25 | { 26 | MailBoxParam param = new MailBoxParam(); 27 | param.setToEmail("sanjoyd.cse@gmailcom"); 28 | param.setSubject("Freight Ma"); 29 | param.setText("Test Freit"); 30 | param.setAttachmentYN(AttachmentYnEnum.YES); 31 | param.setAttachmentName("tedd"); 32 | param.setStatus(MailStatusEnum.PENDING); 33 | return mailBoxService.addMailBox(param); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | #Database Properties 2 | spring.datasource.url=jdbc:mysql://localhost:3306/freight_management 3 | spring.datasource.username=root 4 | spring.datasource.password=root 5 | 6 | #Server port 7 | #Default port. can be changed to another if available 8 | #server.port=10230 9 | 10 | #logging config. Read default value from logback.xml 11 | #logging.config=logback.xml 12 | 13 | #Actuator properties 14 | endpoints.actuator.enabled=true 15 | endpoints.info.enabled=true 16 | 17 | spring.jpa.properties.javax.persistence.sharedCache.mode=ALL 18 | 19 | 20 | flyway.url = jdbc:mysql://localhost:3306/mysql 21 | flyway.schemas = freight_management 22 | flyway.user = root 23 | flyway.password = root 24 | flyway.locations=filesystem:server-config/database 25 | 26 | 27 | #SMTP configuration 28 | spring.mail.host=smtp.gmail.com 29 | spring.mail.port=587 30 | spring.mail.username=flop.official@gmail.com 31 | spring.mail.password=Sanju@123 32 | spring.mail.properties.mail.smtp.starttls.enable=true 33 | spring.mail.properties.mail.smtp.ssl.trust=smtp.gmail.com 34 | spring.mail.properties.mail.smtp.starttls.required=true 35 | spring.mail.properties.mail.smtp.auth=true 36 | spring.mail.properties.mail.smtp.connectiontimeout=5000 37 | spring.mail.properties.mail.smtp.timeout=5000 38 | spring.mail.properties.mail.smtp.writetimeout=5000 39 | 40 | -------------------------------------------------------------------------------- /server-config/database/V1__freight_management_ddl.sql: -------------------------------------------------------------------------------- 1 | 2 | /*Table Creation*/ 3 | CREATE TABLE `freight_management`.`place` ( 4 | `ID` int(11) NOT NULL AUTO_INCREMENT, 5 | `NAME` varchar(255) NOT NULL, 6 | `CODE` varchar(45) NOT NULL, 7 | `ADDRESS` varchar(255) DEFAULT NULL, 8 | `LONGITUDE` double NOT NULL, 9 | `LATITUDE` double NOT NULL, 10 | `CREATED_BY` varchar(45) NOT NULL DEFAULT 'ADMIN', 11 | `UPDATED_BY` varchar(45) DEFAULT NULL, 12 | `CREATED` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 13 | `UPDATED` timestamp NULL DEFAULT NULL, 14 | PRIMARY KEY (`ID`), 15 | UNIQUE KEY `CODE_UNIQUE` (`CODE`), 16 | UNIQUE KEY `NAME_UNIQUE` (`NAME`) 17 | ) ; 18 | 19 | CREATE TABLE `freight_management`.`path` ( 20 | `ID` int(11) NOT NULL AUTO_INCREMENT, 21 | `FROM_CODE` varchar(45) NOT NULL, 22 | `TO_CODE` varchar(45) NOT NULL, 23 | `CONTAINER_SIZE_FEET` int(11) NOT NULL, 24 | `COST_EURO` double NOT NULL, 25 | `ROUTE_TYPE` varchar(45) NOT NULL, 26 | `DURATION_DAY` int(11) NOT NULL, 27 | `CREATED_BY` varchar(255) NOT NULL DEFAULT 'ADMIN', 28 | `UPDATED_BY` varchar(255) DEFAULT NULL, 29 | `CREATED` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 30 | `UPDATED` timestamp NULL DEFAULT NULL, 31 | PRIMARY KEY (`ID`), 32 | UNIQUE KEY `unique_container_route` (`FROM_CODE`,`TO_CODE`,`CONTAINER_SIZE_FEET`,`ROUTE_TYPE`), 33 | KEY `FK_FROM_idx` (`FROM_CODE`), 34 | KEY `FK_TO_idx` (`TO_CODE`), 35 | CONSTRAINT `FK_FROM` FOREIGN KEY (`FROM_CODE`) REFERENCES `place` (`CODE`) ON DELETE NO ACTION ON UPDATE NO ACTION, 36 | CONSTRAINT `FK_TO` FOREIGN KEY (`TO_CODE`) REFERENCES `place` (`CODE`) ON DELETE NO ACTION ON UPDATE NO ACTION 37 | ) ; -------------------------------------------------------------------------------- /src/main/java/com/fm/assignment/tenant/TenantInterceptor.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment.tenant; 2 | 3 | import org.springframework.http.MediaType; 4 | import org.springframework.stereotype.Component; 5 | import org.springframework.util.StringUtils; 6 | import org.springframework.web.servlet.ModelAndView; 7 | import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; 8 | 9 | import javax.servlet.http.HttpServletRequest; 10 | import javax.servlet.http.HttpServletResponse; 11 | 12 | /** 13 | * Created by Lenovo on 18/01/2018. 14 | */ 15 | @Component 16 | public class TenantInterceptor extends HandlerInterceptorAdapter { 17 | private static final String TENANT_HEADER = "X-TenantID"; 18 | 19 | @Override 20 | public boolean preHandle(HttpServletRequest req, HttpServletResponse res, Object handler) 21 | throws Exception { 22 | 23 | String tenant = req.getHeader(TENANT_HEADER); 24 | boolean tenantSet = false; 25 | 26 | if(StringUtils.isEmpty(tenant)) { 27 | res.setStatus(HttpServletResponse.SC_BAD_REQUEST); 28 | res.setContentType(MediaType.APPLICATION_JSON_VALUE); 29 | res.getWriter().write("{\"error\": \"No tenant supplied\"}"); 30 | res.getWriter().flush(); 31 | } else { 32 | TenantContext.setCurrentTenant(tenant); 33 | tenantSet = true; 34 | } 35 | 36 | return tenantSet; 37 | } 38 | 39 | @Override 40 | public void postHandle( 41 | HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) 42 | throws Exception { 43 | TenantContext.clear(); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/fm/assignment/core/entity/BaseEntity.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment.core.entity; 2 | 3 | import javax.persistence.PrePersist; 4 | import javax.persistence.PreUpdate; 5 | import javax.persistence.Temporal; 6 | import javax.persistence.TemporalType; 7 | import java.io.Serializable; 8 | import java.util.Date; 9 | 10 | /** 11 | * This is parent class of all entity. 12 | * This contains common properties of all entity means DB table. 13 | * @author Sanjoy Kumer Deb 14 | * @since 07/10/2017. 15 | */ 16 | public abstract class BaseEntity implements Serializable{ 17 | 18 | private String createdBy; 19 | 20 | private String updatedBy; 21 | 22 | private Date created; 23 | 24 | private Date updated; 25 | 26 | public String getUpdatedBy() { 27 | return updatedBy; 28 | } 29 | 30 | public void setUpdatedBy(String updatedBy) { 31 | this.updatedBy = updatedBy; 32 | } 33 | 34 | public String getCreatedBy() { 35 | return createdBy; 36 | } 37 | 38 | public void setCreatedBy(String createdBy) { 39 | this.createdBy = createdBy; 40 | } 41 | 42 | 43 | @PrePersist 44 | protected void onCreate(){ 45 | created = new Date(); 46 | } 47 | 48 | @PreUpdate 49 | protected void onUpdate(){ 50 | updated = new Date(); 51 | } 52 | 53 | @Temporal(TemporalType.TIMESTAMP) 54 | public Date getCreated() { 55 | return created; 56 | } 57 | 58 | public void setCreated(Date created) { 59 | this.created = created; 60 | } 61 | 62 | @Temporal(TemporalType.TIMESTAMP) 63 | public Date getUpdated() { 64 | return updated; 65 | } 66 | 67 | public void setUpdated(Date updated) { 68 | this.updated = updated; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/com/fm/assignment/core/dao/PlaceRepository.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment.core.dao; 2 | 3 | import com.fm.assignment.core.entity.PlaceEntity; 4 | import com.vividsolutions.jts.geom.Geometry; 5 | import org.springframework.cache.annotation.Cacheable; 6 | import org.springframework.data.jpa.repository.JpaRepository; 7 | import org.springframework.data.jpa.repository.Query; 8 | import org.springframework.data.repository.query.Param; 9 | import org.springframework.stereotype.Repository; 10 | import org.springframework.transaction.annotation.Transactional; 11 | 12 | import java.util.List; 13 | 14 | /** 15 | * @author Sanjoy Kumer Deb 16 | * @since 10/10/2017. 17 | */ 18 | @Repository 19 | @Transactional(readOnly = true) 20 | public interface PlaceRepository extends JpaRepository { 21 | PlaceEntity findByCode(String code); 22 | PlaceEntity findByName(String name); 23 | //@TODO need to move JPQL. 24 | @Query(value = "select p.* from (SELECT a.*,\n" + 25 | " 111.111 *\n" + 26 | " DEGREES(ACOS(COS(RADIANS(a.Latitude))\n" + 27 | " * COS(RADIANS(:latitude))\n" + 28 | " * COS(RADIANS(a.Longitude - :longitude))\n" + 29 | " + SIN(RADIANS(a.Latitude))\n" + 30 | " * SIN(RADIANS(:latitude)))) AS distance_in_km\n" + 31 | " FROM place AS a HAVING distance_in_km < :distance \n" + 32 | " ORDER BY distance_in_km ASC) b\n" + 33 | " inner join \n" + 34 | " place as p\n" + 35 | " on b.id = p.id",nativeQuery = true) 36 | @Cacheable("findLocationWithin") 37 | List findLocationWithin(@Param("latitude")Double latitude,@Param("longitude")Double longitude,@Param("distance") double distance); 38 | } 39 | 40 | -------------------------------------------------------------------------------- /src/main/java/com/fm/assignment/mail/EmailScheduler.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment.mail; 2 | 3 | import com.fm.assignment.core.enums.MailStatusEnum; 4 | import com.fm.assignment.core.params.MailBoxParam; 5 | import com.fm.assignment.core.service.MailBoxService; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.scheduling.annotation.Scheduled; 9 | import org.springframework.stereotype.Service; 10 | 11 | import java.util.Date; 12 | import java.util.List; 13 | 14 | /** 15 | * Created by Lenovo on 12/02/2018. 16 | */ 17 | @Service 18 | @Slf4j 19 | public class EmailScheduler { 20 | 21 | @Autowired 22 | private EmailService emailService; 23 | 24 | @Autowired 25 | private MailBoxService mailBoxService; 26 | 27 | @Scheduled(fixedDelay = 9000) 28 | public void sendEmailSchedule(){ 29 | log.info("Email schedule started at {}",new Date().getTime()); 30 | List mailBoxParams = mailBoxService.getMailBoxByStatus(MailStatusEnum.PENDING); 31 | 32 | 33 | for (MailBoxParam param : mailBoxParams) { 34 | try { 35 | emailService.sendSimpleMessage(param.getToEmail(), param.getSubject(), param.getText()); 36 | param.setStatus(MailStatusEnum.SENT); 37 | mailBoxService.updateMailBox(param); 38 | log.info("Email SENT to {}", param.getToEmail()); 39 | }catch (Exception ert) 40 | { 41 | param.setStatus(MailStatusEnum.FAILED); 42 | mailBoxService.updateMailBox(param); 43 | log.info("Email SENT Failed to {} {}", param.getToEmail(),ert); 44 | } 45 | 46 | } 47 | log.info("Email schedule ended {}",new Date().getTime()); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | INFO 9 | 10 | 11 | %date [%thread] %-5level %logger - %X %msg %n 12 | 13 | 14 | 15 | 17 | ${DEV_HOME}/fm.log 18 | 19 | 20 | %d{yyyy-MM-dd HH:mm:ss} - %msg%n 21 | 22 | 23 | 24 | 25 | 26 | ${DEV_HOME}/archived/debug.%d{yyyy-MM-dd}.%i.log 27 | 28 | 30 | 10MB 31 | 32 | 33 | 34 | 35 | 36 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /src/main/java/com/fm/assignment/core/util/PathFinder.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment.core.util; 2 | 3 | import com.fm.assignment.core.entity.PathEntity; 4 | import com.fm.assignment.errorhandler.ResourceNotFoundException; 5 | import org.springframework.stereotype.Component; 6 | 7 | import java.util.*; 8 | 9 | /** 10 | * This class use to build total graph which determines a directed graph 11 | * from one node to another. 12 | * @author Sanjoy Kumer Deb 13 | * @since 10/10/2017. 14 | */ 15 | public class PathFinder { 16 | private final GraphBuilder graphBuilder; 17 | 18 | public PathFinder(GraphBuilder graphBuilder) { 19 | this.graphBuilder = graphBuilder; 20 | } 21 | 22 | public List> getAllPaths(String source, String destination) throws ResourceNotFoundException { 23 | List> paths = new ArrayList<>(); 24 | recursive(source, destination, paths, new LinkedHashSet(), null, new LinkedHashSet()); 25 | return paths; 26 | } 27 | 28 | 29 | private void recursive(String current, String destination, List> routes, LinkedHashSet path, PathEntity pathEntity, LinkedHashSet pathList) throws ResourceNotFoundException { 30 | path.add(current); 31 | if (pathEntity != null) { 32 | pathList.add(pathEntity); 33 | } 34 | if (current.equals(destination)) { 35 | routes.add(new ArrayList<>(pathList)); 36 | pathList.remove(pathEntity); 37 | path.remove(current); 38 | return; 39 | } 40 | 41 | Map nodesFromCurrentNode = graphBuilder.pathFrom(current); 42 | final Set paths = nodesFromCurrentNode.keySet(); 43 | 44 | for (String t : paths) { 45 | if (!path.contains(t)) { 46 | recursive(t, destination, routes, path, nodesFromCurrentNode.get(t), pathList); 47 | } 48 | } 49 | path.remove(current); 50 | pathList.remove(pathEntity); 51 | } 52 | } -------------------------------------------------------------------------------- /src/main/java/com/fm/assignment/core/util/GraphBuilder.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment.core.util; 2 | 3 | import com.fm.assignment.core.entity.PathEntity; 4 | import com.fm.assignment.errorhandler.ErrorCodes; 5 | import com.fm.assignment.errorhandler.ResourceNotFoundException; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.stereotype.Component; 8 | 9 | import java.util.*; 10 | 11 | /** 12 | * @author Sanjoy Kumer Deb 13 | * @since 07/10/2017. 14 | */ 15 | public class GraphBuilder{ 16 | private final Map> graph = new HashMap>(); 17 | 18 | /** 19 | * This will add all locations. In graph terms its called node. 20 | * @param location 21 | * @return 22 | */ 23 | public boolean addLocation(String location) 24 | { 25 | if (graph.containsKey(location)) return false; 26 | 27 | graph.put(location, new HashMap()); 28 | return true; 29 | } 30 | 31 | /** 32 | * This will add a directed path in graph. 33 | * Which will help to find all possible path from Source Location to destination Location. 34 | * @param source 35 | * @param destination 36 | * @param pathEntity 37 | */ 38 | public void addPath(String source, String destination, PathEntity pathEntity){ 39 | graph.get(source).put(destination, pathEntity); 40 | } 41 | 42 | /** 43 | * This will give all possible path no next location. 44 | * Here path in map will be unmodifiable. 45 | * @param location 46 | * @return 47 | */ 48 | public Map pathFrom(String location) throws ResourceNotFoundException { 49 | Map paths = graph.get(location); 50 | if (paths == null) 51 | { 52 | throw new ResourceNotFoundException(ErrorCodes.Feature.PATH_FIND, 53 | ErrorCodes.CODE.PATH_NOT_FOUND, 54 | ErrorCodes.REASON_MAP.get(ErrorCodes.CODE.PATH_NOT_FOUND)); 55 | } 56 | return paths; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/com/fm/assignment/mail/EmailServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment.mail; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.core.io.ClassPathResource; 6 | import org.springframework.core.io.FileSystemResource; 7 | import org.springframework.mail.SimpleMailMessage; 8 | import org.springframework.mail.javamail.JavaMailSender; 9 | import org.springframework.mail.javamail.MimeMessageHelper; 10 | import org.springframework.stereotype.Service; 11 | 12 | import javax.mail.MessagingException; 13 | import javax.mail.Multipart; 14 | import javax.mail.internet.MimeBodyPart; 15 | import javax.mail.internet.MimeMessage; 16 | import javax.mail.internet.MimeMultipart; 17 | import java.io.File; 18 | import java.io.IOException; 19 | 20 | /** 21 | * Created by Lenovo on 08/02/2018. 22 | */ 23 | @Service 24 | @Slf4j 25 | public class EmailServiceImpl implements EmailService { 26 | @Autowired 27 | public JavaMailSender emailSender; 28 | private final String imageLink = "images/teddy.jpeg"; 29 | private final String imageNameToSend = "teddy.jpeg"; 30 | 31 | @Override 32 | public void sendSimpleMessage(String to, String subject, String text) throws IOException, MessagingException { 33 | MimeMessage message = emailSender.createMimeMessage(); 34 | MimeMessageHelper helper = null; 35 | try { 36 | helper = new MimeMessageHelper(message, true); 37 | 38 | 39 | helper.setTo(to); 40 | helper.setSubject(subject); 41 | 42 | FileSystemResource file 43 | = new FileSystemResource(new ClassPathResource(imageLink).getFile()); 44 | helper.addAttachment(imageNameToSend, file); 45 | 46 | MimeBodyPart messageBodyPart = new MimeBodyPart(); 47 | messageBodyPart.setContent(text, "text/html"); 48 | 49 | 50 | Multipart multipart = new MimeMultipart(); 51 | multipart.addBodyPart(messageBodyPart); 52 | 53 | message.setContent(multipart); 54 | emailSender.send(message); 55 | } catch (MessagingException | IOException e ) { 56 | log.info("Exception catched {}",e); 57 | throw e; 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/com/fm/assignment/api/controller/PlaceController.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment.api.controller; 2 | 3 | import com.fm.assignment.api.model.PlaceResource; 4 | import com.fm.assignment.core.params.PlaceParam; 5 | import com.fm.assignment.core.service.PlaceService; 6 | import com.fm.assignment.errorhandler.DatabaseException; 7 | import com.fm.assignment.util.RequestAndParamBuilder; 8 | import lombok.extern.slf4j.Slf4j; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.web.bind.annotation.*; 11 | 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | 15 | /** 16 | * @author Sanjoy Kumer Deb 17 | * @since 06/10/2017. 18 | */ 19 | @RestController 20 | @Slf4j 21 | @RequestMapping(value = "place") 22 | public class PlaceController { 23 | @Autowired 24 | private PlaceService placeService; 25 | 26 | @PostMapping 27 | public long addPlace(@RequestBody PlaceResource resource) throws DatabaseException { 28 | log.info("add place request"); 29 | PlaceParam placeParam = RequestAndParamBuilder.buildPlaceParam(resource); 30 | return placeService.addPlace(placeParam); 31 | } 32 | @GetMapping 33 | public List getAll() 34 | { 35 | List placeParams = placeService.getAllPlaces(); 36 | List placeResourceList = new ArrayList<>(); 37 | for (PlaceParam placeParam:placeParams) 38 | { 39 | PlaceResource resource = RequestAndParamBuilder.buildPlaceResource(placeParam); 40 | placeResourceList.add(resource); 41 | } 42 | return placeResourceList; 43 | } 44 | 45 | @GetMapping(value = "{id}") 46 | public PlaceResource getOne(@PathVariable long id) 47 | { 48 | PlaceParam param = placeService.findOne(id); 49 | return RequestAndParamBuilder.buildPlaceResource(param); 50 | } 51 | 52 | @PutMapping(value = "{id}") 53 | public Long update(@PathVariable long id,@RequestBody PlaceResource placeResource) 54 | { 55 | return placeService.updatePlace(id, 56 | RequestAndParamBuilder.buildPlaceParam(placeResource)); 57 | } 58 | 59 | @DeleteMapping(value = "{id}") 60 | public void delete(@PathVariable long id) 61 | { 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/com/fm/assignment/errorhandler/ErrorCodes.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment.errorhandler; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | /** 7 | * @author Sanjoy Kumer Deb 8 | * @since 06/10/2017. 9 | */ 10 | public class ErrorCodes { 11 | public interface Feature{ 12 | String PATH_FIND = "Path Find"; 13 | String PATH_ADD = "Path Add"; 14 | String PLACE_ADD = "Place Add"; 15 | String UNKNOWN = "Unknown"; 16 | } 17 | 18 | public interface CODE{ 19 | String PATH_NOT_FOUND = "ERR-4200"; 20 | String SOURCE_NOT_FOUND = "ERR-4201"; 21 | String DESTINATION_NOT_FOUND = "ERR-4202"; 22 | String COST_DURATION_NOT_MATCHED = "ERR-4203"; 23 | String ROUTE_TYPE_OR_CONTAINER_NOT_MATCHED = "ERR-4204"; 24 | String PATH_SAVE_FAIL = "ERR-4301"; 25 | String PLACE_SAVE_FAIL = "ERR-4302"; 26 | String REMOTE_API_FAIL = "ERR-4000"; 27 | String METHOD_ARG_NOT_VALID = "ERR-4001"; 28 | String GENERIC_ERROR = "ERR-4444"; 29 | } 30 | 31 | public static final Map REASON_MAP = new HashMap(); 32 | 33 | static { 34 | REASON_MAP.put(CODE.PATH_NOT_FOUND,"Path not found for specific source and destination"); 35 | REASON_MAP.put(CODE.SOURCE_NOT_FOUND,"Source not correct. Please try with another"); 36 | REASON_MAP.put(CODE.DESTINATION_NOT_FOUND,"Destination not correct. Please try with another"); 37 | REASON_MAP.put(CODE.PATH_SAVE_FAIL,"Path add fail. probably UK exception. Please check data"); 38 | REASON_MAP.put(CODE.PLACE_SAVE_FAIL,"Place add fail. Probably UK exception. Check Data"); 39 | REASON_MAP.put(CODE.REMOTE_API_FAIL,"Google Map remote call error. Please check internet connection."); 40 | REASON_MAP.put(CODE.GENERIC_ERROR,"Internal Server Error!!!. Please communicate with vendor"); 41 | REASON_MAP.put(CODE.METHOD_ARG_NOT_VALID,"Please check mandatory/optional fields of your request."); 42 | REASON_MAP.put(CODE.COST_DURATION_NOT_MATCHED,"Please check Cost or Duration Range. Path not found for request."); 43 | REASON_MAP.put(CODE.ROUTE_TYPE_OR_CONTAINER_NOT_MATCHED,"Please check transportation mode and container size. Path not found for request."); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/fm/assignment/core/service/MailBoxServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment.core.service; 2 | 3 | import com.fm.assignment.core.dao.MailBoxRepository; 4 | import com.fm.assignment.core.entity.MailBoxEntity; 5 | import com.fm.assignment.core.enums.MailStatusEnum; 6 | import com.fm.assignment.core.params.MailBoxParam; 7 | import com.fm.assignment.core.util.ParamAndEntityBuilder; 8 | import lombok.extern.slf4j.Slf4j; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.stereotype.Service; 11 | import org.springframework.transaction.annotation.Transactional; 12 | 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | 16 | /** 17 | * Created by Lenovo on 13/02/2018. 18 | */ 19 | @Service 20 | @Slf4j 21 | public class MailBoxServiceImpl implements MailBoxService { 22 | 23 | @Autowired 24 | private MailBoxRepository mailBoxRepository; 25 | 26 | @Autowired 27 | private ParamAndEntityBuilder paramAndEntityBuilder; 28 | 29 | @Override 30 | @Transactional 31 | public long addMailBox(MailBoxParam param) { 32 | MailBoxEntity entity = paramAndEntityBuilder.buildMailBoxEntity(param); 33 | MailBoxEntity saved = mailBoxRepository.save(entity); 34 | return saved.getId(); 35 | } 36 | 37 | @Override 38 | public long updateMailBox(MailBoxParam param) { 39 | MailBoxEntity entity = paramAndEntityBuilder.buildMailBoxEntity(param); 40 | MailBoxEntity saved = mailBoxRepository.save(entity); 41 | return saved.getId(); 42 | } 43 | 44 | @Override 45 | public List getAllMailBox() { 46 | List entityList = mailBoxRepository.findAll(); 47 | return getMailBoxParamsList(entityList); 48 | } 49 | 50 | @Override 51 | public List getMailBoxByStatus(MailStatusEnum status) { 52 | List entityList = mailBoxRepository.findByStatus(status); 53 | return getMailBoxParamsList(entityList); 54 | } 55 | 56 | private List getMailBoxParamsList(List entityList) { 57 | List paramList = new ArrayList<>(); 58 | for (MailBoxEntity entity : entityList) { 59 | paramList.add(paramAndEntityBuilder.buildMailBoxParam(entity)); 60 | } 61 | return paramList; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/com/fm/assignment/config/HibernateConfig.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment.config; 2 | 3 | import org.hibernate.MultiTenancyStrategy; 4 | import org.hibernate.cfg.Environment; 5 | import org.hibernate.context.spi.CurrentTenantIdentifierResolver; 6 | import org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties; 9 | import org.springframework.context.annotation.Bean; 10 | import org.springframework.context.annotation.Configuration; 11 | import org.springframework.orm.jpa.JpaVendorAdapter; 12 | import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; 13 | import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; 14 | 15 | import javax.sql.DataSource; 16 | import java.util.HashMap; 17 | import java.util.Map; 18 | 19 | /** 20 | * Created by Lenovo on 17/01/2018. 21 | */ 22 | @Configuration 23 | public class HibernateConfig { 24 | 25 | @Autowired 26 | JpaProperties jpaProperties; 27 | 28 | @Bean 29 | public JpaVendorAdapter jpaVendorAdapter() { 30 | return new HibernateJpaVendorAdapter(); 31 | } 32 | 33 | @Bean 34 | public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource, 35 | MultiTenantConnectionProvider multiTenantConnectionProviderImpl, 36 | CurrentTenantIdentifierResolver currentTenantIdentifierResolverImpl) { 37 | Map properties = new HashMap<>(); 38 | properties.putAll(jpaProperties.getHibernateProperties(dataSource)); 39 | properties.put(Environment.MULTI_TENANT, MultiTenancyStrategy.SCHEMA); 40 | properties.put(Environment.MULTI_TENANT_CONNECTION_PROVIDER, multiTenantConnectionProviderImpl); 41 | properties.put(Environment.MULTI_TENANT_IDENTIFIER_RESOLVER, currentTenantIdentifierResolverImpl); 42 | 43 | LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean(); 44 | em.setDataSource(dataSource); 45 | em.setPackagesToScan("com.fm.assignment"); 46 | em.setJpaVendorAdapter(jpaVendorAdapter()); 47 | em.setJpaPropertyMap(properties); 48 | return em; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/fm/assignment/api/validator/FindPathValidator.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment.api.validator; 2 | 3 | 4 | import com.fm.assignment.api.model.FindPathRequest; 5 | import com.fm.assignment.api.model.PathResource; 6 | import com.fm.assignment.util.RequestValidationMessage; 7 | import org.springframework.validation.Errors; 8 | import org.springframework.validation.Validator; 9 | 10 | /** 11 | * @author Sanjoy Kumer Deb 12 | * @since 11/10/2017. 13 | */ 14 | public class FindPathValidator implements Validator { 15 | @Override 16 | public boolean supports(Class clazz) { 17 | return FindPathRequest.class.equals(clazz); 18 | } 19 | 20 | @Override 21 | public void validate(Object target, Errors errors) { 22 | FindPathRequest findPathRequest = (FindPathRequest) target; 23 | if(findPathRequest.getCostFrom() != null && findPathRequest.getCostTo() != null){ 24 | if (findPathRequest.getCostTo()` value to your favourate location. Unix user must need to change log location. 35 | c) Project setup almost done. 36 | 37 | 7. Open command promt. Goto project directory. 38 | 8. Run following command "**gradle clean build**" . Some test code added. You can browse test result from **{ProjectDir}/FreightManagement/build/reports/tests/test/classes/com.fm.assignment.repository.PathRepositoryTest.html** 39 | 9. If clean build success then run "**gradle bootrun**". 40 | 10. I think thats all to setup project. 41 | 42 | 43 | For more details Please check following URLs: 44 | 45 | 1. https://github.com/sanjoy-sust/FreightManagement/wiki/1.-Installation-guide 46 | 2. https://github.com/sanjoy-sust/FreightManagement/wiki/2.-Description-and-Improvement-Item 47 | 3. https://github.com/sanjoy-sust/FreightManagement/wiki/3.-API-Document 48 | 4. https://github.com/sanjoy-sust/FreightManagement/wiki/4.-Test-Cases 49 | 5. https://github.com/sanjoy-sust/FreightManagement/wiki/5.-Samples-with-test-Cases 50 | 51 | 52 | -------------------------------------------------------------------------------- /src/main/java/com/fm/assignment/tenant/MultiTenantConnectionProviderImpl.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment.tenant; 2 | 3 | import org.hibernate.HibernateException; 4 | import org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.stereotype.Component; 7 | 8 | import javax.sql.DataSource; 9 | import java.sql.Connection; 10 | import java.sql.SQLException; 11 | 12 | /** 13 | * Created by Lenovo on 18/01/2018. 14 | */ 15 | @Component 16 | public class MultiTenantConnectionProviderImpl implements MultiTenantConnectionProvider { 17 | 18 | private static final long serialVersionUID = 6246085840652870138L; 19 | 20 | @Autowired 21 | private DataSource dataSource; 22 | 23 | @Override 24 | public Connection getAnyConnection() throws SQLException { 25 | return dataSource.getConnection(); 26 | } 27 | 28 | @Override 29 | public void releaseAnyConnection(Connection connection) throws SQLException { 30 | connection.close(); 31 | } 32 | 33 | @Override 34 | public Connection getConnection(String tenantIdentifier) throws SQLException { 35 | final Connection connection = getAnyConnection(); 36 | try { 37 | connection.createStatement().execute( "USE " + tenantIdentifier ); 38 | } 39 | catch ( SQLException e ) { 40 | throw new HibernateException( 41 | "Could not alter JDBC connection to specified schema [" + tenantIdentifier + "]", 42 | e 43 | ); 44 | } 45 | return connection; 46 | } 47 | 48 | @Override 49 | public void releaseConnection(String tenantIdentifier, Connection connection) throws SQLException { 50 | try { 51 | connection.createStatement().execute( "USE " + TenantContext.DEFAULT_TENANT ); 52 | } 53 | catch ( SQLException e ) { 54 | throw new HibernateException( 55 | "Could not alter JDBC connection to specified schema [" + tenantIdentifier + "]", 56 | e 57 | ); 58 | } 59 | connection.close(); 60 | } 61 | 62 | @SuppressWarnings("rawtypes") 63 | @Override 64 | public boolean isUnwrappableAs(Class unwrapType) { 65 | return false; 66 | } 67 | 68 | @Override 69 | public T unwrap(Class unwrapType) { 70 | return null; 71 | } 72 | 73 | @Override 74 | public boolean supportsAggressiveRelease() { 75 | return true; 76 | } 77 | 78 | } -------------------------------------------------------------------------------- /sample/AllPathFind/2-success/output.json: -------------------------------------------------------------------------------- 1 | { 2 | "results": [ 3 | { 4 | "route": [ 5 | { 6 | "from": "Cox's Bazar", 7 | "to": "Comilla", 8 | "transportType": "Road", 9 | "cost": 85, 10 | "duration": 1 11 | }, 12 | { 13 | "from": "Comilla", 14 | "to": "Mohakhali", 15 | "transportType": "Road", 16 | "cost": 35, 17 | "duration": 1 18 | } 19 | ], 20 | "totalCost": 120, 21 | "totalDuration": 2 22 | }, 23 | { 24 | "route": [ 25 | { 26 | "from": "Cox's Bazar", 27 | "to": "Chittagong", 28 | "transportType": "Road", 29 | "cost": 50, 30 | "duration": 1 31 | }, 32 | { 33 | "from": "Chittagong", 34 | "to": "Mohakhali", 35 | "transportType": "Road", 36 | "cost": 65, 37 | "duration": 1 38 | } 39 | ], 40 | "totalCost": 115, 41 | "totalDuration": 2 42 | }, 43 | { 44 | "route": [ 45 | { 46 | "from": "Cox's Bazar", 47 | "to": "Comilla", 48 | "transportType": "Road", 49 | "cost": 85, 50 | "duration": 1 51 | }, 52 | { 53 | "from": "Comilla", 54 | "to": "Mirpur", 55 | "transportType": "Road", 56 | "cost": 32, 57 | "duration": 1 58 | } 59 | ], 60 | "totalCost": 117, 61 | "totalDuration": 2 62 | }, 63 | { 64 | "route": [ 65 | { 66 | "from": "Cox's Bazar", 67 | "to": "Chittagong", 68 | "transportType": "Road", 69 | "cost": 50, 70 | "duration": 1 71 | }, 72 | { 73 | "from": "Chittagong", 74 | "to": "Mirpur", 75 | "transportType": "Road", 76 | "cost": 62, 77 | "duration": 1 78 | } 79 | ], 80 | "totalCost": 112, 81 | "totalDuration": 2 82 | } 83 | ] 84 | } -------------------------------------------------------------------------------- /sample/AllPathFind/3-success/output.json: -------------------------------------------------------------------------------- 1 | { 2 | "results": [ 3 | { 4 | "route": [ 5 | { 6 | "from": "Cox's Bazar", 7 | "to": "Comilla", 8 | "transportType": "Road", 9 | "cost": 85, 10 | "duration": 1 11 | }, 12 | { 13 | "from": "Comilla", 14 | "to": "Mohakhali", 15 | "transportType": "Road", 16 | "cost": 35, 17 | "duration": 1 18 | } 19 | ], 20 | "totalCost": 120, 21 | "totalDuration": 2 22 | }, 23 | { 24 | "route": [ 25 | { 26 | "from": "Cox's Bazar", 27 | "to": "Chittagong", 28 | "transportType": "Road", 29 | "cost": 50, 30 | "duration": 1 31 | }, 32 | { 33 | "from": "Chittagong", 34 | "to": "Mohakhali", 35 | "transportType": "Road", 36 | "cost": 65, 37 | "duration": 1 38 | } 39 | ], 40 | "totalCost": 115, 41 | "totalDuration": 2 42 | }, 43 | { 44 | "route": [ 45 | { 46 | "from": "Cox's Bazar", 47 | "to": "Comilla", 48 | "transportType": "Road", 49 | "cost": 85, 50 | "duration": 1 51 | }, 52 | { 53 | "from": "Comilla", 54 | "to": "Mirpur", 55 | "transportType": "Road", 56 | "cost": 32, 57 | "duration": 1 58 | } 59 | ], 60 | "totalCost": 117, 61 | "totalDuration": 2 62 | }, 63 | { 64 | "route": [ 65 | { 66 | "from": "Cox's Bazar", 67 | "to": "Chittagong", 68 | "transportType": "Road", 69 | "cost": 50, 70 | "duration": 1 71 | }, 72 | { 73 | "from": "Chittagong", 74 | "to": "Mirpur", 75 | "transportType": "Road", 76 | "cost": 62, 77 | "duration": 1 78 | } 79 | ], 80 | "totalCost": 112, 81 | "totalDuration": 2 82 | } 83 | ] 84 | } -------------------------------------------------------------------------------- /sample/AllPathFind/6-success/output.json: -------------------------------------------------------------------------------- 1 | { 2 | "results": [ 3 | { 4 | "route": [ 5 | { 6 | "from": "Cox's Bazar", 7 | "to": "Comilla", 8 | "transportType": "Road", 9 | "cost": 115, 10 | "duration": 1 11 | }, 12 | { 13 | "from": "Comilla", 14 | "to": "Mirpur", 15 | "transportType": "Road", 16 | "cost": 62, 17 | "duration": 1 18 | } 19 | ], 20 | "totalCost": 177, 21 | "totalDuration": 2 22 | }, 23 | { 24 | "route": [ 25 | { 26 | "from": "Cox's Bazar", 27 | "to": "Chittagong", 28 | "transportType": "Road", 29 | "cost": 80, 30 | "duration": 1 31 | }, 32 | { 33 | "from": "Chittagong", 34 | "to": "Mirpur", 35 | "transportType": "Road", 36 | "cost": 92, 37 | "duration": 1 38 | } 39 | ], 40 | "totalCost": 172, 41 | "totalDuration": 2 42 | }, 43 | { 44 | "route": [ 45 | { 46 | "from": "Cox's Bazar", 47 | "to": "Comilla", 48 | "transportType": "Road", 49 | "cost": 115, 50 | "duration": 1 51 | }, 52 | { 53 | "from": "Comilla", 54 | "to": "Mohakhali", 55 | "transportType": "Road", 56 | "cost": 65, 57 | "duration": 1 58 | } 59 | ], 60 | "totalCost": 180, 61 | "totalDuration": 2 62 | }, 63 | { 64 | "route": [ 65 | { 66 | "from": "Cox's Bazar", 67 | "to": "Chittagong", 68 | "transportType": "Road", 69 | "cost": 80, 70 | "duration": 1 71 | }, 72 | { 73 | "from": "Chittagong", 74 | "to": "Mohakhali", 75 | "transportType": "Road", 76 | "cost": 92, 77 | "duration": 1 78 | } 79 | ], 80 | "totalCost": 172, 81 | "totalDuration": 2 82 | } 83 | ] 84 | } -------------------------------------------------------------------------------- /sample/AllPathFind/1-success/output.json: -------------------------------------------------------------------------------- 1 | { 2 | "results": [ 3 | { 4 | "route": [ 5 | { 6 | "from": "Stockholm", 7 | "to": "Gothenburg", 8 | "transportType": "Road", 9 | "cost": 420, 10 | "duration": 1 11 | }, 12 | { 13 | "from": "Gothenburg", 14 | "to": "Savannah", 15 | "transportType": "Ocean", 16 | "cost": 1765, 17 | "duration": 23 18 | }, 19 | { 20 | "from": "Savannah", 21 | "to": "Orlando", 22 | "transportType": "Road", 23 | "cost": 600, 24 | "duration": 1 25 | } 26 | ], 27 | "totalCost": 2785, 28 | "totalDuration": 25 29 | }, 30 | { 31 | "route": [ 32 | { 33 | "from": "Stockholm", 34 | "to": "Gothenburg", 35 | "transportType": "Road", 36 | "cost": 420, 37 | "duration": 1 38 | }, 39 | { 40 | "from": "Gothenburg", 41 | "to": "Ft. Lauderdale", 42 | "transportType": "Ocean", 43 | "cost": 1623, 44 | "duration": 22 45 | }, 46 | { 47 | "from": "Ft. Lauderdale", 48 | "to": "Orlando", 49 | "transportType": "Road", 50 | "cost": 600, 51 | "duration": 1 52 | } 53 | ], 54 | "totalCost": 2643, 55 | "totalDuration": 24 56 | }, 57 | { 58 | "route": [ 59 | { 60 | "from": "Stockholm", 61 | "to": "Rotterdam", 62 | "transportType": "Road", 63 | "cost": 1430, 64 | "duration": 3 65 | }, 66 | { 67 | "from": "Rotterdam", 68 | "to": "Ft. Lauderdale", 69 | "transportType": "Ocean", 70 | "cost": 1623, 71 | "duration": 18 72 | }, 73 | { 74 | "from": "Ft. Lauderdale", 75 | "to": "Orlando", 76 | "transportType": "Road", 77 | "cost": 600, 78 | "duration": 1 79 | } 80 | ], 81 | "totalCost": 3653, 82 | "totalDuration": 22 83 | } 84 | ] 85 | } -------------------------------------------------------------------------------- /src/test/java/com/fm/assignment/repository/PathRepositoryTest.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment.repository; 2 | 3 | import com.fm.assignment.core.dao.PathRepository; 4 | import com.fm.assignment.core.dao.PlaceRepository; 5 | import com.fm.assignment.core.entity.PathEntity; 6 | import com.fm.assignment.core.entity.PlaceEntity; 7 | import com.fm.assignment.core.enums.TransportTypeEnum; 8 | import com.fm.assignment.repository.constants.PathEntityConstants; 9 | import com.fm.assignment.repository.constants.PlaceEntityConstants; 10 | import org.junit.Assert; 11 | import org.junit.Test; 12 | import org.junit.runner.RunWith; 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | import org.springframework.boot.test.context.SpringBootTest; 15 | import org.springframework.test.context.junit4.SpringRunner; 16 | 17 | import java.util.ArrayList; 18 | import java.util.List; 19 | 20 | /** 21 | * Created by Lenovo on 16/01/2018. 22 | */ 23 | @RunWith(SpringRunner.class) 24 | @SpringBootTest 25 | public class PathRepositoryTest { 26 | @Autowired 27 | PathRepository pathRepository; 28 | 29 | @Autowired 30 | PlaceRepository placeRepository; 31 | 32 | @Test 33 | public void addTest() 34 | { 35 | List placeEntities = savePlaceData(2); 36 | 37 | PathEntity pathEntity = new PathEntity(); 38 | pathEntity.setFromCode(placeEntities.get(0)); 39 | pathEntity.setToCode(placeEntities.get(1)); 40 | pathEntity.setContainerSize(PathEntityConstants.CONTAINER_SIZE); 41 | pathEntity.setCost(PathEntityConstants.COST); 42 | pathEntity.setDuration(PathEntityConstants.DURATION); 43 | pathEntity.setRouteType(TransportTypeEnum.Road); 44 | PathEntity savedPath = pathRepository.save(pathEntity); 45 | Assert.assertNotNull(savedPath); 46 | pathRepository.delete(savedPath); 47 | Assert.assertNull(pathRepository.findOne(savedPath.getId())); 48 | placeRepository.delete(placeEntities); 49 | } 50 | 51 | @Test 52 | public void findPathByContainerSize() 53 | { 54 | List byContainerSize = pathRepository.findByContainerSize(20); 55 | Assert.assertNotNull(byContainerSize); 56 | } 57 | 58 | private List savePlaceData(int noOfPlaces) { 59 | List savedPlaces = new ArrayList<>(); 60 | for (int i=1;i<=noOfPlaces;i++) { 61 | PlaceEntity pe = new PlaceEntity(); 62 | pe.setName(PlaceEntityConstants.PLACE_NAME.concat(String.valueOf(i))); 63 | pe.setCode(PlaceEntityConstants.PLACE_CODE.concat(String.valueOf(i))); 64 | pe.setLatitude(PlaceEntityConstants.PLACE_LATITUDE+i); 65 | pe.setLongitude(PlaceEntityConstants.PLACE_LONGITUDE+i); 66 | pe = placeRepository.save(pe); 67 | savedPlaces.add(pe); 68 | } 69 | return savedPlaces; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at sanjoyd.cse@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /server-config/database/V2__freight_management_dml.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO `freight_management`.`place` 2 | VALUES (1, 'Stockholm', 'STKHM', NULL, 18.069915, 59.347553, 'ADMIN', NULL, '2017-10-10 16:46:24', NULL), 3 | (2, 'Gothenburg', 'GTB', NULL, 11.974831, 57.72929, 'ADMIN', NULL, '2017-10-10 16:47:31', NULL), 4 | (3, 'Ft. Lauderdale', 'FLD', NULL, -80.134927, 26.127534, 'ADMIN', NULL, '2017-10-10 16:49:18', NULL), 5 | (4, 'Orlando', 'ORLD', NULL, -81.384556, 28.537212, 'ADMIN', NULL, '2017-10-10 16:50:58', NULL), 6 | (5, 'Savannah', 'SVNNH', NULL, -81.1021, 32.081829, 'ADMIN', NULL, '2017-10-10 16:52:00', NULL), 7 | (6, 'Rotterdam', 'RTTD', NULL, 4.484904, 51.922384, 'ADMIN', NULL, '2017-10-10 16:53:41', NULL), 8 | (7, 'Cox\'s Bazar', 'COX', NULL, 92.005302, 21.440019, 'ADMIN', NULL, '2017-10-10 15:10:17', NULL), 9 | (8, 'Chittagong', 'CTG', NULL, 91.819854, 22.353184, 'ADMIN', NULL, '2017-10-10 17:11:53', NULL), 10 | (9, 'Mirpur', 'MRP', NULL, 90.366228, 23.823343, 'ADMIN', NULL, '2017-10-10 17:13:02', NULL), 11 | (10, 'Mohakhali', 'MKH', NULL, 90.404933, 23.777658, 'ADMIN', NULL, '2017-10-10 17:13:59', NULL), 12 | (11, 'Comilla', 'CML', NULL, 91.188435, 23.46232, 'ADMIN', NULL, '2017-10-10 17:14:48', NULL); 13 | 14 | 15 | INSERT INTO `freight_management`.`path` VALUES (1, 'STKHM', 'GTB', 20, 420, 'Road', 1, 'ADMIN', NULL, '2017-10-10 17:29:26', NULL), 16 | (2, 'STKHM', 'GTB', 40, 430, 'Road', 1, 'ADMIN', NULL, '2017-10-10 17:30:16', NULL), 17 | (23, 'GTB', 'FLD', 20, 1623, 'Ocean', 22, 'ADMIN', NULL, '2017-10-10 18:45:47', NULL), 18 | (24, 'GTB', 'FLD', 40, 2500, 'Ocean', 22, 'ADMIN', NULL, '2017-10-10 18:46:28', NULL), 19 | (25, 'FLD', 'ORLD', 20, 600, 'Road', 1, 'ADMIN', NULL, '2017-10-10 18:47:46', NULL), 20 | (26, 'FLD', 'ORLD', 40, 900, 'Road', 1, 'ADMIN', NULL, '2017-10-10 18:48:07', NULL), 21 | (27, 'GTB', 'SVNNH', 20, 1765, 'Ocean', 23, 'ADMIN', NULL, '2017-10-10 18:49:06', NULL), 22 | (28, 'GTB', 'SVNNH', 40, 2600, 'Ocean', 23, 'ADMIN', NULL, '2017-10-10 18:50:35', NULL), 23 | (29, 'SVNNH', 'ORLD', 20, 600, 'Road', 1, 'ADMIN', NULL, '2017-10-10 18:51:30', NULL), 24 | (30, 'SVNNH', 'ORLD', 40, 900, 'Road', 1, 'ADMIN', NULL, '2017-10-10 18:51:49', NULL), 25 | (31, 'STKHM', 'RTTD', 20, 1430, 'Road', 3, 'ADMIN', NULL, '2017-10-10 18:52:59', NULL), 26 | (32, 'STKHM', 'RTTD', 40, 2600, 'Road', 3, 'ADMIN', NULL, '2017-10-10 18:53:16', NULL), 27 | (33, 'RTTD', 'FLD', 20, 1623, 'Ocean', 18, 'ADMIN', NULL, '2017-10-10 18:54:45', NULL), 28 | (35, 'RTTD', 'FLD', 40, 2600, 'Ocean', 18, 'ADMIN', NULL, '2017-10-10 18:55:12', NULL), 29 | (36, 'COX', 'CTG', 20, 50, 'Road', 1, 'ADMIN', NULL, '2017-10-10 18:56:35', NULL), 30 | (37, 'CTG', 'MRP', 20, 62, 'Road', 1, 'ADMIN', NULL, '2017-10-10 18:57:24', NULL), 31 | (38, 'CTG', 'MKH', 20, 65, 'Road', 1, 'ADMIN', NULL, '2017-10-10 18:58:03', NULL), 32 | (39, 'COX', 'CML', 20, 85, 'Road', 1, 'ADMIN', NULL, '2017-10-10 18:59:05', NULL), 33 | (40, 'CML', 'MRP', 20, 32, 'Road', 1, 'ADMIN', NULL, '2017-10-10 18:59:45', NULL), 34 | (41, 'CML', 'MKH', 20, 35, 'Road', 1, 'ADMIN', NULL, '2017-10-10 19:00:12', NULL), 35 | (42, 'COX', 'CTG', 40, 80, 'Road', 1, 'ADMIN', NULL, '2017-10-10 19:01:15', NULL), 36 | (43, 'CTG', 'MRP', 40, 92, 'Road', 1, 'ADMIN', NULL, '2017-10-10 19:01:46', NULL), 37 | (44, 'CTG', 'MKH', 40, 92, 'Road', 1, 'ADMIN', NULL, '2017-10-10 19:02:03', NULL), 38 | (45, 'COX', 'CML', 40, 115, 'Road', 1, 'ADMIN', NULL, '2017-10-10 19:02:56', NULL), 39 | (46, 'CML', 'MRP', 40, 62, 'Road', 1, 'ADMIN', NULL, '2017-10-10 19:03:28', NULL), 40 | (47, 'CML', 'MKH', 40, 65, 'Road', 1, 'ADMIN', NULL, '2017-10-10 19:03:57', NULL); 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /src/main/java/com/fm/assignment/core/validator/FindPathValidator.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment.core.validator; 2 | 3 | import com.fm.assignment.api.model.FindPathRequest; 4 | import com.fm.assignment.core.entity.PlaceEntity; 5 | import com.fm.assignment.core.params.FindPathParam; 6 | import com.fm.assignment.core.params.PathParam; 7 | import com.fm.assignment.core.params.ResultParam; 8 | import com.fm.assignment.errorhandler.ErrorCodes; 9 | import com.fm.assignment.errorhandler.ResourceNotFoundException; 10 | 11 | import java.util.List; 12 | 13 | /** 14 | * Created by Lenovo on 13/10/2017. 15 | */ 16 | public class FindPathValidator { 17 | 18 | /** 19 | * This will check Source and Destination is valid or not 20 | * @param placeEntityFrom 21 | * @param placeEntityTo 22 | * @throws ResourceNotFoundException 23 | */ 24 | public static void isPlaceNull(PlaceEntity placeEntityFrom, PlaceEntity placeEntityTo) throws ResourceNotFoundException { 25 | if (placeEntityFrom == null) { 26 | throw new ResourceNotFoundException(ErrorCodes.Feature.PATH_FIND, 27 | ErrorCodes.CODE.SOURCE_NOT_FOUND, ErrorCodes.REASON_MAP.get(ErrorCodes.CODE.SOURCE_NOT_FOUND)); 28 | } 29 | if (placeEntityTo == null) { 30 | throw new ResourceNotFoundException(ErrorCodes.Feature.PATH_FIND, 31 | ErrorCodes.CODE.DESTINATION_NOT_FOUND, ErrorCodes.REASON_MAP.get(ErrorCodes.CODE.DESTINATION_NOT_FOUND)); 32 | } 33 | } 34 | 35 | /** 36 | * Filter path for cost limitation and duration limitation 37 | * Both fields are optional 38 | * 39 | * @param param 40 | * @param results 41 | * @param routes 42 | * @param totalCost 43 | * @param totalDuration 44 | */ 45 | public static void filterPaths(FindPathParam param, List results, List routes, 46 | Double totalCost, Long totalDuration) { 47 | if ((param.getCostTo() == null && param.getCostFrom() == null) 48 | && (param.getDurationTo() == null && param.getDurationFrom() == null)) { 49 | buildResult(results, routes, totalCost, totalDuration); 50 | } else if ((param.getCostTo() != null && param.getCostFrom() != null) 51 | && (param.getDurationTo() == null && param.getDurationFrom() == null)) { 52 | if (totalCost >= param.getCostFrom() && totalCost <= param.getCostTo()) { 53 | buildResult(results, routes, totalCost, totalDuration); 54 | } 55 | } else if ((param.getCostTo() == null && param.getCostFrom() == null) 56 | && (param.getDurationTo() != null && param.getDurationFrom() != null)) { 57 | if (totalDuration >= param.getDurationFrom() && totalDuration <= param.getDurationTo()) { 58 | buildResult(results, routes, totalCost, totalDuration); 59 | } 60 | } else if ((param.getCostTo() != null && param.getCostFrom() != null) 61 | && (param.getDurationTo() != null && param.getDurationFrom() != null)) { 62 | if (totalDuration >= param.getDurationFrom() && totalDuration <= param.getDurationTo()) { 63 | if (totalCost >= param.getCostFrom() && totalCost <= param.getCostTo()) { 64 | buildResult(results, routes, totalCost, totalDuration); 65 | } 66 | } 67 | } 68 | } 69 | 70 | /** 71 | * Build results from all routes. 72 | * @param results 73 | * @param routes 74 | * @param totalCost 75 | * @param totalDuration 76 | */ 77 | private static void buildResult(List results, List routes, Double totalCost, Long totalDuration) { 78 | 79 | ResultParam result = ResultParam.builder() 80 | .route(routes) 81 | .totalCost(totalCost) 82 | .totalDuration(totalDuration) 83 | .build(); 84 | results.add(result); 85 | } 86 | 87 | 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/com/fm/assignment/api/controller/PathController.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment.api.controller; 2 | 3 | import com.fm.assignment.api.validator.FindPathValidator; 4 | import com.fm.assignment.core.params.FindPathParam; 5 | import com.fm.assignment.core.params.PathParam; 6 | import com.fm.assignment.core.params.ResultParam; 7 | import com.fm.assignment.errorhandler.DatabaseException; 8 | import com.fm.assignment.errorhandler.ResourceNotFoundException; 9 | import com.fm.assignment.api.model.*; 10 | import com.fm.assignment.core.service.PathService; 11 | import com.fm.assignment.util.RequestAndParamBuilder; 12 | import lombok.extern.slf4j.Slf4j; 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | import org.springframework.web.bind.WebDataBinder; 15 | import org.springframework.web.bind.annotation.*; 16 | 17 | import javax.validation.Valid; 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | 21 | /** 22 | * @author Sanjoy Kumer Deb 23 | * @since 06/10/2017. 24 | */ 25 | @RestController 26 | @Slf4j 27 | @RequestMapping(value = "path") 28 | public class PathController { 29 | 30 | @InitBinder("findPathRequest") 31 | protected void initBinder(WebDataBinder webDataBinder) { 32 | webDataBinder.setValidator(new FindPathValidator()); 33 | } 34 | 35 | @Autowired 36 | private PathService pathService; 37 | 38 | @PostMapping 39 | public long addPath(@RequestBody @Valid PathResource resource) throws ResourceNotFoundException, DatabaseException { 40 | log.info("Add Path Request"); 41 | return pathService.addPath(RequestAndParamBuilder.buildPathParam(resource)); 42 | } 43 | 44 | /** 45 | * Find all possible paths from source to destination. 46 | * 47 | * @param resource 48 | * @return 49 | * @throws ResourceNotFoundException 50 | */ 51 | @PostMapping(value = "find-all-path") 52 | public FindPathResponse findAllPath(@RequestBody @Valid FindPathRequest resource) throws Exception { 53 | FindPathParam findPathParam = RequestAndParamBuilder.buildFindPathParam(resource); 54 | List allPaths = pathService.getAllPaths(findPathParam); 55 | return buildResponse(allPaths); 56 | } 57 | 58 | @GetMapping 59 | public List getAll() { 60 | List params = pathService.findAll(); 61 | List resourceList = new ArrayList<>(); 62 | for (PathParam param: params) 63 | { 64 | resourceList.add(RequestAndParamBuilder.buildPathResource(param)); 65 | } 66 | return resourceList; 67 | } 68 | 69 | @GetMapping(value = "{id}") 70 | public PathResource getOne(@PathVariable long id) { 71 | return RequestAndParamBuilder.buildPathResource(pathService.findById(id)); 72 | } 73 | 74 | @PutMapping(value = "{id}") 75 | public Long updateOne(@PathVariable long id, @RequestBody PathResource resource) throws ResourceNotFoundException { 76 | return pathService.updateOne(id,RequestAndParamBuilder.buildPathParam(resource)); 77 | } 78 | 79 | @DeleteMapping(value = "{id}") 80 | public void delete(@PathVariable long id) { 81 | } 82 | 83 | private FindPathResponse buildResponse(List allPaths) { 84 | FindPathResponse response = new FindPathResponse(); 85 | List results = new ArrayList<>(); 86 | for (ResultParam param : allPaths) 87 | { 88 | ResultsResource resultsResource = new ResultsResource(); 89 | resultsResource.setTotalCost(param.getTotalCost()); 90 | resultsResource.setTotalDuration(param.getTotalDuration()); 91 | List routes = new ArrayList<>(); 92 | for (PathParam pathParam : param.getRoute()) 93 | { 94 | RouteResource routeResource = RequestAndParamBuilder.buildRoutResource(pathParam); 95 | routes.add(routeResource); 96 | } 97 | resultsResource.setRoute(routes); 98 | results.add(resultsResource); 99 | } 100 | response.setResults(results); 101 | return response; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/main/java/com/fm/assignment/core/util/ParamAndEntityBuilder.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment.core.util; 2 | 3 | import com.fm.assignment.core.dao.PlaceRepository; 4 | import com.fm.assignment.core.entity.MailBoxEntity; 5 | import com.fm.assignment.core.entity.PathEntity; 6 | import com.fm.assignment.core.entity.PlaceEntity; 7 | import com.fm.assignment.core.params.MailBoxParam; 8 | import com.fm.assignment.core.params.PathParam; 9 | import com.fm.assignment.core.params.PlaceParam; 10 | import com.fm.assignment.core.validator.FindPathValidator; 11 | import com.fm.assignment.errorhandler.ResourceNotFoundException; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.stereotype.Component; 14 | 15 | /** 16 | * Created by Lenovo on 13/10/2017. 17 | */ 18 | @Component 19 | public class ParamAndEntityBuilder { 20 | @Autowired 21 | private PlaceRepository placeRepository; 22 | 23 | public PlaceParam buildPlaceParam(PlaceEntity placeEntity) { 24 | return PlaceParam.builder() 25 | .id(placeEntity.getId()) 26 | .code(placeEntity.getCode()) 27 | .latitude(placeEntity.getLatitude()) 28 | .longitude(placeEntity.getLongitude()) 29 | .name(placeEntity.getName()) 30 | .build(); 31 | } 32 | 33 | public PlaceEntity buildPlaceEntity(PlaceParam param) { 34 | 35 | PlaceEntity placeEntity = new PlaceEntity(); 36 | placeEntity.setId(param.getId()); 37 | placeEntity.setName(param.getName()); 38 | placeEntity.setCode(param.getCode()); 39 | placeEntity.setLatitude(param.getLatitude()); 40 | placeEntity.setLongitude(param.getLongitude()); 41 | 42 | return placeEntity; 43 | } 44 | 45 | public PathParam buildPathParam(PathEntity entity) { 46 | return PathParam.builder() 47 | .id(entity.getId()) 48 | .from(entity.getFromCode() == null ? null : entity.getFromCode().getName()) 49 | .to(entity.getToCode() == null ? null : entity.getToCode().getName()) 50 | .containerSize(entity.getContainerSize()) 51 | .cost(entity.getCost()) 52 | .duration(entity.getDuration()) 53 | .routeType(entity.getRouteType()) 54 | .build(); 55 | 56 | } 57 | 58 | public PathEntity buildPathEntity(PathParam param) throws ResourceNotFoundException { 59 | PathEntity pathEntity = new PathEntity(); 60 | pathEntity.setId(param.getId()); 61 | PlaceEntity from = placeRepository.findByName(param.getFrom()); 62 | PlaceEntity to = placeRepository.findByName(param.getTo()); 63 | FindPathValidator.isPlaceNull(from, to); 64 | pathEntity.setFromCode(from); 65 | pathEntity.setToCode(to); 66 | pathEntity.setContainerSize(param.getContainerSize()); 67 | pathEntity.setCost(param.getCost()); 68 | pathEntity.setDuration(param.getDuration()); 69 | pathEntity.setRouteType(param.getRouteType()); 70 | return pathEntity; 71 | } 72 | 73 | public MailBoxParam buildMailBoxParam(MailBoxEntity entity){ 74 | MailBoxParam param = new MailBoxParam(); 75 | 76 | param.setId(entity.getId()); 77 | param.setToEmail(entity.getToEmail()); 78 | param.setAttachmentYN(entity.getAttachmentYN()); 79 | param.setAttachmentName(entity.getAttachmentName()); 80 | param.setStatus(entity.getStatus()); 81 | param.setSubject(entity.getSubject()); 82 | param.setText(entity.getText()); 83 | 84 | return param; 85 | } 86 | 87 | 88 | public MailBoxEntity buildMailBoxEntity(MailBoxParam param){ 89 | MailBoxEntity entity = new MailBoxEntity(); 90 | 91 | entity.setId(param.getId()); 92 | entity.setToEmail(param.getToEmail()); 93 | entity.setText(param.getText()); 94 | entity.setAttachmentYN(param.getAttachmentYN()); 95 | entity.setAttachmentName(param.getAttachmentName()); 96 | entity.setStatus(param.getStatus()); 97 | entity.setSubject(param.getSubject()); 98 | 99 | return entity; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/main/java/com/fm/assignment/util/RequestAndParamBuilder.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment.util; 2 | 3 | import com.fm.assignment.api.model.FindPathRequest; 4 | import com.fm.assignment.api.model.PathResource; 5 | import com.fm.assignment.api.model.PlaceResource; 6 | import com.fm.assignment.api.model.RouteResource; 7 | import com.fm.assignment.core.entity.PathEntity; 8 | import com.fm.assignment.core.params.FindPathParam; 9 | import com.fm.assignment.core.params.PathParam; 10 | import com.fm.assignment.core.params.PlaceParam; 11 | import com.fm.assignment.errorhandler.ResourceNotFoundException; 12 | 13 | /** 14 | * Created by Lenovo on 13/10/2017. 15 | */ 16 | public class RequestAndParamBuilder { 17 | 18 | public static PlaceParam buildPlaceParam(PlaceResource resource) { 19 | return PlaceParam.builder() 20 | .id(resource.getId()) 21 | .code(resource.getCode()) 22 | .latitude(resource.getLatitude()) 23 | .longitude(resource.getLongitude()) 24 | .name(resource.getName()) 25 | .build(); 26 | } 27 | 28 | public static PlaceResource buildPlaceResource(PlaceParam param) { 29 | 30 | PlaceResource placeResource = new PlaceResource(); 31 | placeResource.setId(param.getId()); 32 | placeResource.setName(param.getName()); 33 | placeResource.setCode(param.getCode()); 34 | placeResource.setLatitude(param.getLatitude()); 35 | placeResource.setLongitude(param.getLongitude()); 36 | 37 | return placeResource; 38 | } 39 | 40 | public static PathParam buildPathParam(PathResource resource) { 41 | return PathParam.builder() 42 | .id(resource.getId()) 43 | .from(resource.getFrom()) 44 | .to(resource.getTo()) 45 | .containerSize(resource.getContainerSize()) 46 | .cost(resource.getCost()) 47 | .duration(resource.getDuration()) 48 | .routeType(resource.getRouteType()) 49 | .build(); 50 | } 51 | 52 | public static PathResource buildPathResource(PathParam param) { 53 | PathResource pathResource = new PathResource(); 54 | pathResource.setId(param.getId()); 55 | pathResource.setFrom(param.getFrom()); 56 | pathResource.setTo(param.getTo()); 57 | pathResource.setContainerSize(param.getContainerSize()); 58 | pathResource.setCost(param.getCost()); 59 | pathResource.setDuration(param.getDuration()); 60 | pathResource.setRouteType(param.getRouteType()); 61 | 62 | return pathResource; 63 | } 64 | 65 | public static FindPathParam buildFindPathParam(FindPathRequest request) { 66 | return FindPathParam.builder() 67 | .source(request.getSource()) 68 | .destination(request.getDestination()) 69 | .containerSize(request.getContainerSize()) 70 | .modeOfTransports(request.getModeOfTransports()) 71 | .costFrom(request.getCostFrom()) 72 | .costTo(request.getCostTo()) 73 | .durationFrom(request.getDurationFrom()) 74 | .durationTo(request.getDurationTo()) 75 | .build(); 76 | } 77 | 78 | public static FindPathRequest buildFindPathRequest(FindPathParam param) { 79 | FindPathRequest request = new FindPathRequest(); 80 | request.setSource(param.getSource()); 81 | request.setDestination(param.getDestination()); 82 | request.setContainerSize(param.getContainerSize()); 83 | request.setModeOfTransports(param.getModeOfTransports()); 84 | request.setCostFrom(param.getCostFrom()); 85 | request.setCostTo(param.getCostTo()); 86 | request.setDurationFrom(param.getDurationFrom()); 87 | request.setDurationTo(param.getDurationTo()); 88 | 89 | return request; 90 | } 91 | 92 | public static RouteResource buildRoutResource(PathParam pathParam) { 93 | 94 | RouteResource route = new RouteResource(); 95 | route.setFrom(pathParam.getFrom()); 96 | route.setTo(pathParam.getTo()); 97 | route.setCost(pathParam.getCost()); 98 | route.setTransportType(pathParam.getRouteType()); 99 | route.setDuration(pathParam.getDuration()); 100 | return route; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/main/java/com/fm/assignment/errorhandler/ApplicationExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment.errorhandler; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.springframework.http.HttpStatus; 5 | import org.springframework.validation.BindingResult; 6 | import org.springframework.validation.FieldError; 7 | import org.springframework.validation.ObjectError; 8 | import org.springframework.web.bind.MethodArgumentNotValidException; 9 | import org.springframework.web.bind.annotation.ControllerAdvice; 10 | import org.springframework.web.bind.annotation.ExceptionHandler; 11 | import org.springframework.web.bind.annotation.ResponseBody; 12 | import org.springframework.web.bind.annotation.ResponseStatus; 13 | import org.springframework.web.servlet.config.annotation.EnableWebMvc; 14 | 15 | import javax.servlet.http.HttpServletRequest; 16 | import java.util.List; 17 | 18 | /** 19 | * This is Exception Handler Class. This will resolve all exceptions. 20 | * Created by Lenovo on 06/10/2017. 21 | */ 22 | @EnableWebMvc 23 | @ControllerAdvice 24 | @Slf4j 25 | public class ApplicationExceptionHandler { 26 | 27 | @ResponseStatus(HttpStatus.BAD_REQUEST) 28 | @ExceptionHandler(ResourceNotFoundException.class) 29 | @ResponseBody 30 | public ErrorResponse handleException(HttpServletRequest request, ResourceNotFoundException exp) { 31 | log.error("Resource Not Found{}", exp); 32 | ErrorResponse response = getErrorResponse(exp.getCode(), exp.getFeature(), exp.getReason()); 33 | return response; 34 | } 35 | 36 | @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) 37 | @ExceptionHandler(DatabaseException.class) 38 | @ResponseBody 39 | public ErrorResponse handleDatabaseException(HttpServletRequest request, DatabaseException exp) { 40 | log.error("Database Exception {}", exp); 41 | ErrorResponse response = getErrorResponse(exp.getCode(), exp.getFeature(), exp.getReason()); 42 | return response; 43 | } 44 | 45 | @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) 46 | @ExceptionHandler(RemoteApiException.class) 47 | @ResponseBody 48 | public ErrorResponse handleRemoteApiException(HttpServletRequest request, RemoteApiException exp) { 49 | log.error("Remote Api Exception {}", exp); 50 | ErrorResponse response = getErrorResponse(exp.getCode(), exp.getFeature(), exp.getReason()); 51 | return response; 52 | } 53 | 54 | @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) 55 | @ExceptionHandler(MethodArgumentNotValidException.class) 56 | @ResponseBody 57 | public ErrorResponse methodArgumentExceptionHandler(HttpServletRequest request, MethodArgumentNotValidException exp) { 58 | log.error("Generic Exception ", exp); 59 | ErrorResponse response = getErrorResponse(ErrorCodes.CODE.METHOD_ARG_NOT_VALID, 60 | ErrorCodes.Feature.UNKNOWN, 61 | getErrorMessage(exp.getBindingResult())); 62 | return response; 63 | } 64 | 65 | @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) 66 | @ExceptionHandler(Exception.class) 67 | @ResponseBody 68 | public ErrorResponse genericExceptionHandler(HttpServletRequest request, Exception exp) { 69 | log.error("Generic Exception ", exp); 70 | ErrorResponse response = getErrorResponse(ErrorCodes.CODE.GENERIC_ERROR, 71 | ErrorCodes.Feature.UNKNOWN, 72 | ErrorCodes.REASON_MAP.get(ErrorCodes.CODE.GENERIC_ERROR)); 73 | return response; 74 | } 75 | 76 | public static String getErrorMessage(BindingResult bindingResult){ 77 | StringBuilder message = new StringBuilder(); 78 | 79 | List objectErrors = bindingResult.getGlobalErrors(); 80 | for (ObjectError objectError : objectErrors) { 81 | message.append(objectError.getObjectName()) 82 | .append(" : ") 83 | .append(" [") 84 | .append(objectError.getDefaultMessage()) 85 | .append("] "); 86 | } 87 | 88 | return message.toString(); 89 | } 90 | 91 | 92 | private ErrorResponse getErrorResponse(String code, String feature, String reason) { 93 | ErrorResponse response = new ErrorResponse(); 94 | response.setCode(code); 95 | response.setFeature(feature); 96 | response.setMessage(reason); 97 | return response; 98 | } 99 | } 100 | 101 | -------------------------------------------------------------------------------- /src/main/java/com/fm/assignment/remote/LatLongServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment.remote; 2 | 3 | import com.fm.assignment.errorhandler.ErrorCodes; 4 | import com.fm.assignment.errorhandler.RemoteApiException; 5 | import com.fm.assignment.util.ApiConstants; 6 | import com.fm.assignment.util.ApiUrlBuilder; 7 | import org.springframework.http.HttpStatus; 8 | import org.springframework.stereotype.Component; 9 | import org.springframework.web.client.HttpServerErrorException; 10 | import org.springframework.web.client.HttpStatusCodeException; 11 | import org.w3c.dom.Document; 12 | import org.xml.sax.SAXException; 13 | 14 | import javax.xml.parsers.DocumentBuilder; 15 | import javax.xml.parsers.DocumentBuilderFactory; 16 | import javax.xml.parsers.ParserConfigurationException; 17 | import javax.xml.xpath.*; 18 | import java.io.IOException; 19 | import java.io.UnsupportedEncodingException; 20 | import java.net.HttpURLConnection; 21 | import java.net.URL; 22 | import java.net.URLEncoder; 23 | 24 | /** 25 | * @author Sanjoy Kumer Deb 26 | * @since 08/10/2017. 27 | */ 28 | @Component 29 | public class LatLongServiceImpl implements LatLongService { 30 | /** 31 | * Using google map api find latitude longitude of an address 32 | * 33 | * @param address 34 | * @return 35 | * @throws RemoteApiException 36 | */ 37 | @Override 38 | public LatLongModel getLatLongPositions(String address) throws RemoteApiException { 39 | LatLongModel latLongModel = null; 40 | try { 41 | int responseCode; 42 | URL url = new URL(ApiUrlBuilder.buildGoogleMapApiUrl(address)); 43 | HttpURLConnection httpConnection = (HttpURLConnection) url.openConnection(); 44 | httpConnection.connect(); 45 | responseCode = httpConnection.getResponseCode(); 46 | if (responseCode == HttpStatus.OK.value()) { 47 | DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); 48 | Document document = builder.parse(httpConnection.getInputStream()); 49 | XPathFactory xPathfactory = XPathFactory.newInstance(); 50 | XPath xpath = xPathfactory.newXPath(); 51 | XPathExpression expr = xpath.compile(ApiConstants.XPATH_STATUS_FIELD); 52 | String status = (String) expr.evaluate(document, XPathConstants.STRING); 53 | if (status.equals(HttpStatus.OK.name())) { 54 | expr = xpath.compile(ApiConstants.XPATH_LATITUDE_FIELD); 55 | String latitude = (String) expr.evaluate(document, XPathConstants.STRING); 56 | expr = xpath.compile(ApiConstants.XPATH_LONGITUDE_FIELD); 57 | String longitude = (String) expr.evaluate(document, XPathConstants.STRING); 58 | latLongModel = LatLongModel.builder().latitude(Double.valueOf(latitude)).longitude(Double.valueOf(longitude)).build(); 59 | } else { 60 | throw new HttpServerErrorException(HttpStatus.INTERNAL_SERVER_ERROR); 61 | } 62 | } 63 | } catch (IOException 64 | | ParserConfigurationException 65 | | XPathExpressionException 66 | | SAXException 67 | exp) { 68 | throw new RemoteApiException(ErrorCodes.Feature.UNKNOWN, 69 | ErrorCodes.CODE.REMOTE_API_FAIL, ErrorCodes.REASON_MAP.get(ErrorCodes.CODE.DESTINATION_NOT_FOUND)); 70 | } 71 | if(latLongModel == null) 72 | { 73 | throw new RemoteApiException(ErrorCodes.Feature.UNKNOWN, 74 | ErrorCodes.CODE.REMOTE_API_FAIL, ErrorCodes.REASON_MAP.get(ErrorCodes.CODE.DESTINATION_NOT_FOUND)); 75 | } 76 | return latLongModel; 77 | } 78 | 79 | /** 80 | * To calculate distance between two latitude and longitude. 81 | * @param lat1 82 | * @param lon1 83 | * @param lat2 84 | * @param lon2 85 | * @return 86 | */ 87 | @Override 88 | public double distance(double lat1, double lon1, double lat2, double lon2) { 89 | double theta = lon1 - lon2; 90 | double dist = Math.sin(deg2rad(lat1)) * Math.sin(deg2rad(lat2)) + Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) * Math.cos(deg2rad(theta)); 91 | dist = Math.acos(dist); 92 | dist = rad2deg(dist); 93 | dist = dist * 60 * 1.1515; 94 | dist = dist * 1.609344; 95 | return dist; 96 | } 97 | 98 | private double deg2rad(double deg) { 99 | return (deg * Math.PI / 180.0); 100 | } 101 | 102 | private double rad2deg(double rad) { 103 | return (rad * 180.0 / Math.PI); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/test/java/com/fm/assignment/repository/PlaceRepositoryTest.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment.repository; 2 | 3 | import com.fm.assignment.core.dao.PlaceRepository; 4 | import com.fm.assignment.core.entity.PlaceEntity; 5 | import com.fm.assignment.repository.constants.PlaceEntityConstants; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.junit.Assert; 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.boot.test.context.SpringBootTest; 12 | import org.springframework.dao.DataIntegrityViolationException; 13 | import org.springframework.test.context.junit4.SpringRunner; 14 | 15 | /** 16 | * Created by Lenovo on 17/01/2018. 17 | */ 18 | @RunWith(SpringRunner.class) 19 | @SpringBootTest 20 | @Slf4j 21 | public class PlaceRepositoryTest { 22 | @Autowired 23 | PlaceRepository placeRepository; 24 | 25 | @Test 26 | public void addTest() { 27 | PlaceEntity save = savePlaceData(); 28 | long id = save.getId(); 29 | Assert.assertNotNull(save); 30 | placeRepository.delete(save); 31 | Assert.assertNull(placeRepository.findOne(id)); 32 | } 33 | 34 | @Test 35 | public void findPathByName() { 36 | PlaceEntity placeEntity = savePlaceData(); 37 | Assert.assertEquals(placeEntity.getName(), PlaceEntityConstants.PLACE_NAME); 38 | placeRepository.delete(placeEntity); 39 | } 40 | 41 | @Test 42 | public void findPathByVal() { 43 | PlaceEntity save = savePlaceData(); 44 | PlaceEntity placeEntity = placeRepository.findByCode(PlaceEntityConstants.PLACE_CODE); 45 | Assert.assertEquals(placeEntity.getName(), PlaceEntityConstants.PLACE_NAME); 46 | placeRepository.delete(save); 47 | } 48 | 49 | @Test 50 | public void findPathByValNotExits() { 51 | PlaceEntity placeEntity = null; 52 | placeEntity = placeRepository.findByCode(PlaceEntityConstants.PLACE_CODE); 53 | if(placeEntity != null) 54 | { 55 | placeRepository.delete(placeEntity); 56 | PlaceEntity pe = savePlaceData(); 57 | PlaceEntity save = placeRepository.save(pe); 58 | placeRepository.delete(save); 59 | } 60 | placeEntity = placeRepository.findByCode(PlaceEntityConstants.PLACE_CODE); 61 | Assert.assertNull(placeEntity); 62 | } 63 | 64 | @Test(expected = NullPointerException.class) 65 | public void findPathByValNullPointer() { 66 | PlaceEntity placeEntity = placeRepository.findByCode(PlaceEntityConstants.PLACE_CODE); 67 | placeEntity.getName(); 68 | } 69 | 70 | @Test(expected = DataIntegrityViolationException.class) 71 | public void checkDuplicateEntry() { 72 | savePlaceData(); 73 | PlaceEntity placeEntity = placeRepository.findByCode(PlaceEntityConstants.PLACE_CODE); 74 | Assert.assertNotNull(placeEntity.getName()); 75 | try { 76 | savePlaceData(); 77 | }finally { 78 | placeRepository.delete(placeEntity); 79 | } 80 | } 81 | 82 | @Test(expected = DataIntegrityViolationException.class) 83 | public void checkNullCodeEntry() { 84 | PlaceEntity placeEntity = new PlaceEntity(); 85 | placeEntity.setName(PlaceEntityConstants.PLACE_NAME); 86 | placeEntity.setLatitude(PlaceEntityConstants.PLACE_LATITUDE); 87 | placeEntity.setLongitude(PlaceEntityConstants.PLACE_LONGITUDE); 88 | placeRepository.save(placeEntity); 89 | } 90 | 91 | @Test(expected = DataIntegrityViolationException.class) 92 | public void checkNullNameEntry() { 93 | PlaceEntity placeEntity = new PlaceEntity(); 94 | placeEntity.setCode(PlaceEntityConstants.PLACE_CODE); 95 | placeEntity.setLatitude(PlaceEntityConstants.PLACE_LATITUDE); 96 | placeEntity.setLongitude(PlaceEntityConstants.PLACE_LONGITUDE); 97 | placeRepository.save(placeEntity); 98 | } 99 | 100 | @Test(expected = DataIntegrityViolationException.class) 101 | public void checkNullLatLongEntry() { 102 | PlaceEntity placeEntity = new PlaceEntity(); 103 | placeEntity.setName(PlaceEntityConstants.PLACE_NAME); 104 | placeEntity.setCode(PlaceEntityConstants.PLACE_CODE); 105 | placeRepository.save(placeEntity); 106 | } 107 | 108 | private PlaceEntity savePlaceData() { 109 | PlaceEntity pe = new PlaceEntity(); 110 | pe.setName(PlaceEntityConstants.PLACE_NAME); 111 | pe.setCode(PlaceEntityConstants.PLACE_CODE); 112 | pe.setLatitude(PlaceEntityConstants.PLACE_LATITUDE); 113 | pe.setLongitude(PlaceEntityConstants.PLACE_LONGITUDE); 114 | pe = placeRepository.save(pe); 115 | return pe; 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/main/java/com/fm/assignment/core/service/PlaceServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment.core.service; 2 | 3 | import com.fasterxml.jackson.databind.util.JSONPObject; 4 | import com.fm.assignment.core.entity.PlaceEntity; 5 | import com.fm.assignment.api.model.PlaceResource; 6 | import com.fm.assignment.core.dao.PlaceRepository; 7 | import com.fm.assignment.core.enums.AttachmentYnEnum; 8 | import com.fm.assignment.core.enums.MailStatusEnum; 9 | import com.fm.assignment.core.params.MailBoxParam; 10 | import com.fm.assignment.core.params.PlaceParam; 11 | import com.fm.assignment.core.util.ParamAndEntityBuilder; 12 | import com.fm.assignment.errorhandler.DatabaseException; 13 | import com.fm.assignment.errorhandler.ErrorCodes; 14 | import com.fm.assignment.mail.EmailService; 15 | import com.vividsolutions.jts.awt.PointShapeFactory; 16 | import com.vividsolutions.jts.geom.Coordinate; 17 | import com.vividsolutions.jts.geom.Geometry; 18 | import com.vividsolutions.jts.geom.GeometryFactory; 19 | import com.vividsolutions.jts.geom.Point; 20 | import lombok.extern.slf4j.Slf4j; 21 | import org.springframework.beans.factory.annotation.Autowired; 22 | import org.springframework.stereotype.Service; 23 | import org.springframework.transaction.annotation.Transactional; 24 | 25 | import java.util.ArrayList; 26 | import java.util.List; 27 | import java.util.stream.Collectors; 28 | 29 | /** This service is used to add Locations to location table 30 | * @author Sanjoy Kumer Deb 31 | * @since 07/10/2017. 32 | */ 33 | @Service 34 | @Slf4j 35 | public class PlaceServiceImpl implements PlaceService { 36 | 37 | @Autowired 38 | private PlaceRepository placeRepository; 39 | 40 | @Autowired 41 | private ParamAndEntityBuilder paramAndEntityBuilder; 42 | 43 | @Autowired 44 | private MailBoxService emailService; 45 | 46 | @Override 47 | @Transactional 48 | public Long addPlace(PlaceParam placeParam) throws DatabaseException { 49 | PlaceEntity entity = new PlaceEntity(); 50 | entity.setCode(placeParam.getCode()); 51 | entity.setName(placeParam.getName()); 52 | entity.setLongitude(placeParam.getLongitude()); 53 | entity.setLatitude(placeParam.getLatitude()); 54 | PlaceEntity savedPlaceEntity; 55 | try { 56 | savedPlaceEntity = placeRepository.save(entity); 57 | addMailBox(savedPlaceEntity); 58 | } catch (Exception exp) { 59 | log.info("{}",exp); 60 | throw new DatabaseException(ErrorCodes.Feature.PLACE_ADD, 61 | ErrorCodes.CODE.PLACE_SAVE_FAIL,ErrorCodes.REASON_MAP.get(ErrorCodes.CODE.PLACE_SAVE_FAIL)); 62 | } 63 | return savedPlaceEntity.getId(); 64 | } 65 | 66 | private void addMailBox(PlaceEntity placeEntity) { 67 | MailBoxParam param = new MailBoxParam(); 68 | param.setToEmail("sanjoyd.cse@gmail.com"); 69 | param.setSubject("Freight Management Place Added."); 70 | param.setText("

Place added for "+placeEntity.getName()+"


" + 71 | "

" + 72 | "\"Smiley" + 74 | "Latitude : "+placeEntity.getLatitude()+"
" + 75 | "Longitude : "+placeEntity.getLongitude()+"
" + 76 | "

" + 77 | ""); 78 | param.setAttachmentYN(AttachmentYnEnum.YES); 79 | param.setAttachmentName("teddy.jpeg"); 80 | param.setStatus(MailStatusEnum.PENDING); 81 | emailService.addMailBox(param); 82 | } 83 | 84 | @Override 85 | @Transactional 86 | public Long updatePlace(long id, PlaceParam param) { 87 | PlaceEntity placeEntity = paramAndEntityBuilder.buildPlaceEntity(param); 88 | PlaceEntity entity = placeRepository.save(placeEntity); 89 | return entity.getId(); 90 | } 91 | 92 | @Override 93 | public List getAllNearestPlaces(Double latitude,Double longitude,Double distance) { 94 | List locationWithinParams = new ArrayList<>(); 95 | List locationsWithinDistance = placeRepository.findLocationWithin(latitude,longitude,distance); 96 | locationWithinParams.addAll(locationsWithinDistance.stream().map(paramAndEntityBuilder::buildPlaceParam).collect(Collectors.toList())); 97 | return locationWithinParams; 98 | } 99 | 100 | @Override 101 | public List getAllPlaces() { 102 | List placeEntities = placeRepository.findAll(); 103 | List placeParams = new ArrayList<>(); 104 | placeParams.addAll(placeEntities.stream().map(paramAndEntityBuilder::buildPlaceParam).collect(Collectors.toList())); 105 | return placeParams; 106 | } 107 | 108 | @Override 109 | public PlaceParam findOne(long id) { 110 | PlaceParam param = null; 111 | PlaceEntity placeEntity = placeRepository.findOne(id); 112 | if(placeEntity != null) { 113 | param = paramAndEntityBuilder.buildPlaceParam(placeEntity); 114 | } 115 | return param; 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/main/java/com/fm/assignment/core/service/PathServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.fm.assignment.core.service; 2 | 3 | import com.fm.assignment.core.dao.PathRepository; 4 | import com.fm.assignment.core.entity.PathEntity; 5 | import com.fm.assignment.core.dao.PlaceRepository; 6 | import com.fm.assignment.core.entity.PlaceEntity; 7 | import com.fm.assignment.core.enums.AttachmentYnEnum; 8 | import com.fm.assignment.core.enums.MailStatusEnum; 9 | import com.fm.assignment.core.enums.TransportTypeEnum; 10 | import com.fm.assignment.core.params.*; 11 | import com.fm.assignment.core.util.ParamAndEntityBuilder; 12 | import com.fm.assignment.core.validator.FindPathValidator; 13 | import com.fm.assignment.errorhandler.RemoteApiException; 14 | import com.fm.assignment.mail.EmailService; 15 | import com.fm.assignment.remote.LatLongService; 16 | import com.fm.assignment.remote.LatLongModel; 17 | import com.fm.assignment.errorhandler.DatabaseException; 18 | import com.fm.assignment.errorhandler.ErrorCodes; 19 | import com.fm.assignment.errorhandler.ResourceNotFoundException; 20 | import com.fm.assignment.core.util.GraphBuilder; 21 | import com.fm.assignment.core.util.PathFinder; 22 | import com.fm.assignment.util.Constants; 23 | import lombok.extern.slf4j.Slf4j; 24 | import org.springframework.beans.factory.annotation.Autowired; 25 | import org.springframework.stereotype.Service; 26 | import org.springframework.transaction.annotation.Transactional; 27 | 28 | import java.util.ArrayList; 29 | import java.util.List; 30 | import java.util.stream.Stream; 31 | 32 | /** 33 | * This class use for path related task. 34 | * 35 | * @author Sanjoy Kumer Deb 36 | * @since 06/10/2017. 37 | */ 38 | @Service 39 | @Slf4j 40 | public class PathServiceImpl implements PathService { 41 | @Autowired 42 | private PathRepository pathRepository; 43 | 44 | @Autowired 45 | private PlaceRepository placeRepository; 46 | 47 | @Autowired 48 | private PlaceService placeService; 49 | 50 | @Autowired 51 | private LatLongService latLongService; 52 | 53 | @Autowired 54 | private MailBoxService mailBoxService; 55 | 56 | @Autowired 57 | private ParamAndEntityBuilder paramAndEntityBuilder; 58 | 59 | /** 60 | * This will use to add path to path table. 61 | * 62 | * @param pathParam 63 | * @return 64 | * @throws ResourceNotFoundException 65 | * @throws DatabaseException 66 | */ 67 | @Override 68 | @Transactional 69 | public long addPath(PathParam pathParam) throws ResourceNotFoundException, DatabaseException { 70 | log.info("Adding Path : {}", pathParam); 71 | PathEntity pathEntity = paramAndEntityBuilder.buildPathEntity(pathParam); 72 | PathEntity savedEntity; 73 | try { 74 | savedEntity = pathRepository.save(pathEntity); 75 | addMailBox(savedEntity); 76 | } catch (Exception exp) { 77 | throw new DatabaseException(ErrorCodes.Feature.PATH_ADD, 78 | ErrorCodes.CODE.PATH_SAVE_FAIL, ErrorCodes.REASON_MAP.get(ErrorCodes.CODE.PATH_SAVE_FAIL)); 79 | } 80 | log.info("Path Added : {}", savedEntity); 81 | return savedEntity.getId(); 82 | } 83 | 84 | /** 85 | * This method will use to add all possible path from source to destination. 86 | * If destination not found then use nearest point to find Path from source to destination. 87 | * 88 | * @param param 89 | * @return 90 | * @throws ResourceNotFoundException 91 | * @TODO need to introduce a layer like params layer. for example FindPathParam instead of FindPathRequest. 92 | * Its not recommended to use request object to service layer. 93 | */ 94 | @Override 95 | public List getAllPaths(FindPathParam param) throws ResourceNotFoundException, RemoteApiException { 96 | PlaceEntity sourceObj = placeRepository.findByName(param.getSource()); 97 | PlaceEntity destinationObj = placeRepository.findByName(param.getDestination()); 98 | String sourceCode = sourceObj == null ? null : sourceObj.getCode(); 99 | String destinationCode = destinationObj == null ? null : destinationObj.getCode(); 100 | if (sourceCode == null) { 101 | throw new ResourceNotFoundException(ErrorCodes.Feature.PATH_FIND, 102 | ErrorCodes.CODE.SOURCE_NOT_FOUND, ErrorCodes.REASON_MAP.get(ErrorCodes.CODE.SOURCE_NOT_FOUND)); 103 | } 104 | 105 | List> allPaths = new ArrayList<>(); 106 | if (destinationCode != null) { 107 | /*Find All posible path from source to destination*/ 108 | allPaths = getPaths(param, sourceCode, destinationCode); 109 | } 110 | /*If destination not found then find near location for destination within 50KM.*/ 111 | else if (destinationCode == null) { 112 | List derivedLocationAsDestination = findDerivedLocationAsDestination(param); 113 | for (PlaceParam placeParam : derivedLocationAsDestination) { 114 | List> paths = getPaths(param, sourceCode, placeParam.getCode()); 115 | allPaths.addAll(paths); 116 | } 117 | } 118 | 119 | 120 | if (allPaths == null || allPaths.size() == 0) { 121 | throw new ResourceNotFoundException(ErrorCodes.Feature.PATH_FIND, 122 | ErrorCodes.CODE.PATH_NOT_FOUND, ErrorCodes.REASON_MAP.get(ErrorCodes.CODE.PATH_NOT_FOUND)); 123 | } 124 | 125 | /**/ 126 | List resultsResources = calculateTotalCostAndDurationOfRoutes(allPaths, param); 127 | return resultsResources; 128 | } 129 | 130 | /** 131 | * If destination not available in our DB 132 | * then get nearest location 133 | * Here I use google map api to find latitude and longitude for which destination searched for. 134 | * Then find nearest locations available within 50KM from our predefined location. 135 | * 136 | * @param param 137 | * @return 138 | * @throws Exception 139 | * @TODO if nearest location not match then next nearest location to be calculated. 140 | * @TODO if two nearest location find with same distance then both will pick. 141 | */ 142 | private List findDerivedLocationAsDestination(FindPathParam param) throws RemoteApiException, ResourceNotFoundException { 143 | LatLongModel latLongModel = latLongService.getLatLongPositions(param.getDestination()); 144 | List allDerivedLocations = placeService.getAllNearestPlaces( 145 | latLongModel.getLatitude(), 146 | latLongModel.getLongitude(), 147 | Constants.MINIMUM_DISTANCE_TO_NEAR_LOCATION); 148 | if (allDerivedLocations.size() == 0) { 149 | throw new ResourceNotFoundException(ErrorCodes.Feature.PATH_FIND, 150 | ErrorCodes.CODE.DESTINATION_NOT_FOUND, 151 | ErrorCodes.REASON_MAP.get(ErrorCodes.CODE.DESTINATION_NOT_FOUND)); 152 | } 153 | return allDerivedLocations; 154 | } 155 | 156 | /** 157 | * This method used to find all paths from DB Using transportationMode and container size. 158 | * 159 | * @param param 160 | * @param sourceCode 161 | * @param destinationCode 162 | * @return 163 | * @throws ResourceNotFoundException 164 | * @TODO here we can use factory pattern. I will implement it in future 165 | */ 166 | private List> getPaths(FindPathParam param, String sourceCode, String destinationCode) throws ResourceNotFoundException { 167 | GraphBuilder paths = new GraphBuilder(); 168 | List pathEntityList = new ArrayList<>(); 169 | for (TransportTypeEnum transportTypeEnum : param.getModeOfTransports()) { 170 | switch (transportTypeEnum) { 171 | case All: 172 | pathEntityList.addAll(pathRepository.findByContainerSize(param.getContainerSize())); 173 | buildGraph(paths, pathEntityList); 174 | break; 175 | default: 176 | pathEntityList.addAll(pathRepository.findByRouteTypeAndContainerSize(transportTypeEnum, param.getContainerSize())); 177 | buildGraph(paths, pathEntityList); 178 | break; 179 | } 180 | 181 | } 182 | /*If pathEntityList size is zero that means its contain no path for requested routType and containerSize*/ 183 | if (pathEntityList.size() <= 0) { 184 | throw new ResourceNotFoundException(ErrorCodes.Feature.PATH_FIND, 185 | ErrorCodes.CODE.ROUTE_TYPE_OR_CONTAINER_NOT_MATCHED, ErrorCodes.REASON_MAP.get(ErrorCodes.CODE.ROUTE_TYPE_OR_CONTAINER_NOT_MATCHED)); 186 | } 187 | PathFinder findAllPaths = new PathFinder(paths); 188 | return findAllPaths.getAllPaths(sourceCode, destinationCode); 189 | } 190 | 191 | /** 192 | * @param paths //pass to build graph for different combination of routes 193 | * @param pathEntityList 194 | */ 195 | private void buildGraph(GraphBuilder paths, List pathEntityList) throws ResourceNotFoundException { 196 | for (PathEntity entity : pathEntityList) { 197 | PlaceEntity fromPlace = entity.getFromCode(); 198 | PlaceEntity toPlace = entity.getToCode(); 199 | FindPathValidator.isPlaceNull(fromPlace, toPlace); 200 | paths.addLocation(fromPlace.getCode()); 201 | paths.addLocation(toPlace.getCode()); 202 | paths.addPath(entity.getFromCode().getCode(), entity.getToCode().getCode(), entity); 203 | } 204 | } 205 | 206 | /** 207 | * @param allPaths 208 | * @param param 209 | * @return 210 | * @throws ResourceNotFoundException 211 | */ 212 | private List calculateTotalCostAndDurationOfRoutes(List> allPaths, FindPathParam param) throws ResourceNotFoundException { 213 | List results = new ArrayList<>(); 214 | 215 | for (List pathEntityList : allPaths) { 216 | List routes = new ArrayList<>(); 217 | Double totalCost = 0.0; 218 | Long totalDuration = 0L; 219 | for (PathEntity entity : pathEntityList) { 220 | PathParam route = paramAndEntityBuilder.buildPathParam(entity); 221 | routes.add(route); 222 | totalCost += entity.getCost(); 223 | totalDuration += entity.getDuration(); 224 | } 225 | /*Filter path against cost/duration range*/ 226 | FindPathValidator.filterPaths(param, results, routes, totalCost, totalDuration); 227 | 228 | } 229 | if (results.size() == 0) { 230 | throw new ResourceNotFoundException(ErrorCodes.Feature.PATH_FIND, 231 | ErrorCodes.CODE.COST_DURATION_NOT_MATCHED, ErrorCodes.REASON_MAP.get(ErrorCodes.CODE.COST_DURATION_NOT_MATCHED)); 232 | } 233 | return results; 234 | } 235 | 236 | /** 237 | * This is for temporary when message template feature add then this code will remove 238 | * 239 | * @param pathEntity 240 | */ 241 | private void addMailBox(PathEntity pathEntity) { 242 | MailBoxParam param = new MailBoxParam(); 243 | param.setToEmail("sanjoyd.cse@gmail.com"); 244 | param.setSubject("Freight Management Path Added."); 245 | param.setText("

Path added From " + pathEntity.getFromCode().getName() + " to " + pathEntity.getToCode().getName() + "


" + 246 | "

" + 247 | "Route Type : " + pathEntity.getRouteType().name() + "
" + 248 | "Container Size : " + pathEntity.getContainerSize() + "
" + 249 | "

" + 250 | ""); 251 | param.setAttachmentYN(AttachmentYnEnum.YES); 252 | param.setAttachmentName("teddy.jpeg"); 253 | param.setStatus(MailStatusEnum.PENDING); 254 | mailBoxService.addMailBox(param); 255 | } 256 | 257 | @Override 258 | public PathParam findById(long id) { 259 | PathParam pathParam = null; 260 | PathEntity pathEntity = pathRepository.findOne(id); 261 | if(pathEntity != null) 262 | pathParam = paramAndEntityBuilder.buildPathParam(pathEntity); 263 | return pathParam; 264 | } 265 | 266 | @Override 267 | public List findAll() { 268 | List pathEntityList = pathRepository.findAll(); 269 | List pathParams = new ArrayList<>(); 270 | for (PathEntity pathEntity:pathEntityList){ 271 | PathParam param = null; 272 | if(pathEntity != null) 273 | param = paramAndEntityBuilder.buildPathParam(pathEntity); 274 | pathParams.add(param); 275 | } 276 | return pathParams; 277 | } 278 | 279 | @Override 280 | public Long updateOne(long id, PathParam pathParam) throws ResourceNotFoundException { 281 | 282 | PathEntity entity = paramAndEntityBuilder.buildPathEntity(pathParam); 283 | PathEntity pathEntity = pathRepository.save(entity); 284 | return pathEntity.getId(); 285 | } 286 | } 287 | --------------------------------------------------------------------------------