├── .gitignore ├── LICENSE ├── README.md ├── docs └── xstate.js ├── pom.xml └── src ├── main └── java │ └── io │ └── legacyfighter │ └── cabs │ ├── CabsApplication.java │ ├── agreements │ ├── Contract.java │ ├── ContractAttachment.java │ ├── ContractAttachmentDTO.java │ ├── ContractAttachmentData.java │ ├── ContractAttachmentDataRepository.java │ ├── ContractAttachmentStatus.java │ ├── ContractController.java │ ├── ContractDTO.java │ ├── ContractRepository.java │ ├── ContractService.java │ └── ContractStatus.java │ ├── assignment │ ├── AssignmentStatus.java │ ├── DriverAssignment.java │ ├── DriverAssignmentFacade.java │ ├── DriverAssignmentRepository.java │ └── InvolvedDriversSummary.java │ ├── carfleet │ ├── CarClass.java │ ├── CarType.java │ ├── CarTypeActiveCounter.java │ ├── CarTypeController.java │ ├── CarTypeDTO.java │ ├── CarTypeRepository.java │ └── CarTypeService.java │ ├── common │ ├── BaseEntity.java │ ├── Event.java │ ├── EventsPublisher.java │ └── JsonToCollectionMapper.java │ ├── config │ ├── AppProperties.java │ ├── ClockConfig.java │ └── EventPublisherConfiguration.java │ ├── contracts │ ├── application │ │ ├── acme │ │ │ ├── dynamic │ │ │ │ ├── DocumentOperationResult.java │ │ │ │ └── DocumentResourceManager.java │ │ │ └── straigthforward │ │ │ │ ├── AcmeContractProcessBasedOnStraightforwardDocumentModel.java │ │ │ │ ├── AcmeStateFactory.java │ │ │ │ └── ContractResult.java │ │ └── editor │ │ │ ├── CommitResult.java │ │ │ ├── DocumentDTO.java │ │ │ └── DocumentEditor.java │ ├── infra │ │ └── JpaDocumentHeaderRepository.java │ ├── legacy │ │ ├── BaseAggregateRoot.java │ │ ├── Contract2.java │ │ ├── Document.java │ │ ├── DocumentStatus.java │ │ ├── OOParadigm.java │ │ ├── Printable.java │ │ ├── UnsupportedTransitionException.java │ │ ├── User.java │ │ ├── UserRepository.java │ │ └── Versionable.java │ └── model │ │ ├── ContentId.java │ │ ├── DocumentHeader.java │ │ ├── DocumentHeaderRepository.java │ │ ├── content │ │ ├── ContentVersion.java │ │ ├── DocumentContent.java │ │ ├── DocumentContentRepository.java │ │ └── DocumentNumber.java │ │ └── state │ │ ├── dynamic │ │ ├── ChangeCommand.java │ │ ├── State.java │ │ ├── StateBuilder.java │ │ ├── StateConfig.java │ │ ├── acme │ │ │ └── AcmeContractStateAssembler.java │ │ └── config │ │ │ ├── actions │ │ │ ├── ChangeVerifier.java │ │ │ └── PublishEvent.java │ │ │ ├── changes │ │ │ └── FixedState.java │ │ │ ├── events │ │ │ ├── DocumentEvent.java │ │ │ ├── DocumentPublished.java │ │ │ └── DocumentUnpublished.java │ │ │ └── predicates │ │ │ ├── contentchange │ │ │ ├── NegativePredicate.java │ │ │ └── PositivePredicate.java │ │ │ └── statechange │ │ │ ├── AuthorIsNotAVerifier.java │ │ │ ├── ContentNotEmptyVerifier.java │ │ │ ├── PositiveVerifier.java │ │ │ └── PreviousStateVerifier.java │ │ └── straightforward │ │ ├── BaseState.java │ │ └── acme │ │ ├── ArchivedState.java │ │ ├── DraftState.java │ │ ├── PublishedState.java │ │ └── VerifiedState.java │ ├── crm │ ├── Client.java │ ├── ClientController.java │ ├── ClientDTO.java │ ├── ClientRepository.java │ ├── ClientService.java │ ├── claims │ │ ├── Claim.java │ │ ├── ClaimAttachment.java │ │ ├── ClaimAttachmentRepository.java │ │ ├── ClaimController.java │ │ ├── ClaimDTO.java │ │ ├── ClaimNumberGenerator.java │ │ ├── ClaimRepository.java │ │ ├── ClaimService.java │ │ ├── ClaimsResolver.java │ │ ├── ClaimsResolverRepository.java │ │ └── Status.java │ └── transitanalyzer │ │ ├── AnalyzedAddressesDTO.java │ │ ├── GraphTransitAnalyzer.java │ │ ├── Neo4jConfig.java │ │ ├── PopulateGraphService.java │ │ └── TransitAnalyzerController.java │ ├── driverfleet │ ├── Driver.java │ ├── DriverAttribute.java │ ├── DriverAttributeDTO.java │ ├── DriverAttributeName.java │ ├── DriverAttributeRepository.java │ ├── DriverController.java │ ├── DriverDTO.java │ ├── DriverFee.java │ ├── DriverFeeRepository.java │ ├── DriverFeeService.java │ ├── DriverLicense.java │ ├── DriverRepository.java │ ├── DriverService.java │ └── driverreport │ │ ├── DriverReport.java │ │ ├── DriverReportController.java │ │ ├── SqlBasedDriverReportCreator.java │ │ └── travelleddistance │ │ ├── TravelledDistance.java │ │ ├── TravelledDistanceRepository.java │ │ └── TravelledDistanceService.java │ ├── geolocation │ ├── Distance.java │ ├── DistanceCalculator.java │ ├── GeocodingService.java │ └── address │ │ ├── Address.java │ │ ├── AddressDTO.java │ │ ├── AddressRepository.java │ │ └── AddressRepositoryInterface.java │ ├── invocing │ ├── Invoice.java │ ├── InvoiceGenerator.java │ └── InvoiceRepository.java │ ├── loyalty │ ├── AwardedMiles.java │ ├── AwardsAccount.java │ ├── AwardsAccountController.java │ ├── AwardsAccountDTO.java │ ├── AwardsAccountRepository.java │ ├── AwardsService.java │ ├── AwardsServiceImpl.java │ ├── ConstantUntil.java │ ├── Miles.java │ ├── MilesJsonMapper.java │ └── TwoStepExpiringMiles.java │ ├── money │ └── Money.java │ ├── notification │ ├── ClientNotificationService.java │ └── DriverNotificationService.java │ ├── party │ ├── api │ │ ├── PartyId.java │ │ ├── PartyMapper.java │ │ └── RoleObjectFactory.java │ ├── infra │ │ ├── JpaPartyRelationshipRepository.java │ │ └── JpaPartyRepository.java │ ├── model │ │ ├── party │ │ │ ├── Party.java │ │ │ ├── PartyRelationship.java │ │ │ ├── PartyRelationshipRepository.java │ │ │ ├── PartyRepository.java │ │ │ └── PartyRole.java │ │ └── role │ │ │ └── PartyBasedRole.java │ └── utils │ │ └── PolymorphicHashMap.java │ ├── pricing │ ├── Tariff.java │ └── Tariffs.java │ ├── repair │ ├── api │ │ ├── AssistanceRequest.java │ │ ├── ContractManager.java │ │ ├── RepairProcess.java │ │ ├── RepairRequest.java │ │ └── ResolveResult.java │ ├── legacy │ │ ├── dao │ │ │ └── UserDAO.java │ │ ├── job │ │ │ ├── CommonBaseAbstractJob.java │ │ │ ├── JobResult.java │ │ │ ├── MaintenanceJob.java │ │ │ └── RepairJob.java │ │ ├── parts │ │ │ └── Parts.java │ │ ├── service │ │ │ └── JobDoer.java │ │ └── user │ │ │ ├── CommonBaseAbstractUser.java │ │ │ ├── EmployeeDriver.java │ │ │ ├── EmployeeDriverWithLeasedCar.java │ │ │ ├── EmployeeDriverWithOwnCar.java │ │ │ ├── SignedContract.java │ │ │ ├── SubcontractorDriver.java │ │ │ ├── SubcontractorDriverWithOwnCar.java │ │ │ └── SubcontractorWithRentedCar.java │ └── model │ │ ├── dict │ │ ├── PartyRelationshipsDictionary.java │ │ └── PartyRolesDictionary.java │ │ └── roles │ │ ├── assistance │ │ └── RoleForAssistance.java │ │ ├── empty │ │ ├── Customer.java │ │ └── Insured.java │ │ └── repair │ │ ├── ExtendedInsurance.java │ │ ├── RepairingResult.java │ │ ├── RoleForRepairer.java │ │ └── Warranty.java │ ├── ride │ ├── ChangeDestinationService.java │ ├── ChangePickupService.java │ ├── CompleteTransitService.java │ ├── DemandService.java │ ├── RequestForTransit.java │ ├── RequestForTransitRepository.java │ ├── RequestTransitService.java │ ├── RideService.java │ ├── StartTransitService.java │ ├── Transit.java │ ├── TransitController.java │ ├── TransitDTO.java │ ├── TransitDemand.java │ ├── TransitDemandRepository.java │ ├── TransitRepository.java │ ├── details │ │ ├── Status.java │ │ ├── TransitDetails.java │ │ ├── TransitDetailsDTO.java │ │ ├── TransitDetailsFacade.java │ │ └── TransitDetailsRepository.java │ └── events │ │ └── TransitCompleted.java │ └── tracking │ ├── DriverPosition.java │ ├── DriverPositionDTO.java │ ├── DriverPositionDTOV2.java │ ├── DriverPositionRepository.java │ ├── DriverSession.java │ ├── DriverSessionController.java │ ├── DriverSessionDTO.java │ ├── DriverSessionRepository.java │ ├── DriverSessionService.java │ ├── DriverTrackingController.java │ └── DriverTrackingService.java └── test ├── java └── io │ └── legacyfighter │ └── cabs │ ├── agreements │ └── ContractLifecycleTest.java │ ├── assignment │ └── DriverAssignmentTest.java │ ├── common │ ├── AddressFixture.java │ ├── AddressMatcher.java │ ├── AwardsAccountFixture.java │ ├── CarTypeFixture.java │ ├── ClaimFixture.java │ ├── ClientFixture.java │ ├── DriverFixture.java │ ├── Fixtures.java │ ├── RideFixture.java │ ├── StubbedTransitPrice.java │ ├── TestWithGraphDB.java │ └── TransitFixture.java │ ├── contracts │ ├── application │ │ ├── dynamic │ │ │ ├── AcmeContractManagerBasedOnDynamicStateModelTest.java │ │ │ └── DocumentOperationResultAssert.java │ │ └── straightforward │ │ │ └── acme │ │ │ ├── AcmeContractProcessBasedOnStraightforwardStateModelTest.java │ │ │ └── ContractResultAssert.java │ ├── legacy │ │ └── DocumentTest.java │ └── model │ │ └── state │ │ ├── dynamic │ │ ├── AcmeContractTest.java │ │ └── FakeDocumentPublisher.java │ │ └── straightforward │ │ └── AcmeContractTest.java │ ├── crm │ └── claims │ │ └── ClaimAutomaticResolvingTest.java │ ├── distance │ └── DistanceTest.java │ ├── driverfleet │ ├── DriverLicenseTest.java │ └── driverreport │ │ └── travelleddistance │ │ └── SlotTest.java │ ├── integration │ ├── AnalyzeNearbyTransitsIntegrationTest.java │ ├── AwardMilesManagementIntegrationTest.java │ ├── CalculateDriverFeeIntegrationTest.java │ ├── CalculateDriverPeriodicPaymentsIntegrationTest.java │ ├── CalculateDriverTravelledDistanceIntegrationTest.java │ ├── CarTypeUpdateIntegrationTest.java │ ├── ClaimAutomaticResolvingIntegrationTest.java │ ├── ContractLifecycleIntegrationTest.java │ ├── CreateDriverReportIntegrationTest.java │ ├── DriverTrackingServiceIntegrationTest.java │ ├── ExpiringMilesIntegrationTest.java │ ├── GraphTransitAnalyzerIntegrationTest.java │ ├── PopulateGraphServiceIntegrationTest.java │ ├── RemovingAwardMilesIntegrationTest.java │ ├── TariffRecognizingIntegrationTest.java │ ├── TransitLifeCycleIntegrationTest.java │ └── ValidateDriverLicenseIntegrationTest.java │ ├── loyalty │ └── MilesTest.java │ ├── money │ └── MoneyTest.java │ ├── pricing │ ├── CalculateTransitPriceTest.java │ └── TariffTest.java │ ├── repair │ ├── api │ │ ├── RepairProcessTest.java │ │ └── VehicleRepairAssert.java │ └── legacy │ │ ├── job │ │ └── RepairTest.java │ │ └── service │ │ └── JobDoerTest.java │ └── ride │ ├── CalculateTransitDistanceTest.java │ ├── RequestForTransitTest.java │ ├── TransitDemandTest.java │ └── TransitTest.java └── resources └── application.properties /README.md: -------------------------------------------------------------------------------- 1 | # Rozwój kodu 2 | 3 | Kod będzie rozwijać się wraz z cotygodniową narracją szkoleniową. 4 | Zarówno pojawiać się w nim będą kolejne poprawki jak i odziedziczone po firmach partnerskich nowe moduły ;-) Jak to w prawdziwym legacy. 5 | 6 | # Przeglądanie kodu 7 | 8 | Poszczególne kroki refaktoryzacyjne najlepiej przeglądać używająć tagów. Każdy krok szkoleniowy, który opisany jest w odcinku Legacy Fighter posiada na końcu planszę z nazwą odpowiedniego taga. Porównać zmiany można robiąc diffa w stosunku do poprzedniego taga z narracji. 9 | -------------------------------------------------------------------------------- /docs/xstate.js: -------------------------------------------------------------------------------- 1 | // https://xstate.js.org/viz/ 2 | 3 | // Available variables: 4 | // - Machine 5 | // - interpret 6 | // - assign 7 | // - send 8 | // - sendParent 9 | // - spawn 10 | // - raise 11 | // - actions 12 | // - XState (all XState exports) 13 | 14 | const machine = Machine({ 15 | id: 'transit', 16 | initial: 'draft', 17 | context: { 18 | retries: 0 19 | }, 20 | states: { 21 | draft: { 22 | on: { 23 | PUBLISH: 'waiting_for_driver_acceptance' 24 | } 25 | }, 26 | waiting_for_driver_acceptance: { 27 | on: { 28 | CANCEL: 'cancelled', 29 | ACCEPT: 'transit_to_passenger', 30 | FAILED: 'driver_assignment_failed' 31 | } 32 | }, 33 | transit_to_passenger: { 34 | on: { 35 | CANCEL: 'cancelled', 36 | START_TRANSIT: 'in_transit' 37 | } 38 | }, 39 | in_transit: { 40 | on: { 41 | COMPLETE: 'completed' 42 | } 43 | }, 44 | cancelled: { 45 | type: 'final' 46 | }, 47 | completed: { 48 | type: 'final' 49 | }, 50 | driver_assignment_failed: { 51 | type: 'final' 52 | } 53 | } 54 | }); 55 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/CabsApplication.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs; 2 | 3 | import io.legacyfighter.cabs.config.AppProperties; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 7 | 8 | @SpringBootApplication 9 | @EnableConfigurationProperties(AppProperties.class) 10 | public class CabsApplication { 11 | 12 | public static void main(String[] args) { 13 | SpringApplication.run(CabsApplication.class, args); 14 | } 15 | } -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/agreements/ContractAttachment.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.agreements; 2 | 3 | import io.legacyfighter.cabs.common.BaseEntity; 4 | 5 | import javax.persistence.*; 6 | import java.time.Instant; 7 | import java.util.UUID; 8 | 9 | @Entity 10 | class ContractAttachment extends BaseEntity { 11 | 12 | @Column(nullable = false) 13 | private UUID contractAttachmentNo = UUID.randomUUID(); 14 | 15 | private Instant acceptedAt; 16 | 17 | private Instant rejectedAt; 18 | 19 | private Instant changeDate; 20 | 21 | @Enumerated(EnumType.STRING) 22 | private ContractAttachmentStatus status = ContractAttachmentStatus.PROPOSED; 23 | 24 | @ManyToOne 25 | private Contract contract; 26 | 27 | Instant getAcceptedAt() { 28 | return acceptedAt; 29 | } 30 | 31 | void setAcceptedAt(Instant acceptedAt) { 32 | this.acceptedAt = acceptedAt; 33 | } 34 | 35 | Instant getRejectedAt() { 36 | return rejectedAt; 37 | } 38 | 39 | void setRejectedAt(Instant rejectedAt) { 40 | this.rejectedAt = rejectedAt; 41 | } 42 | 43 | Instant getChangeDate() { 44 | return changeDate; 45 | } 46 | 47 | void setChangeDate(Instant changeDate) { 48 | this.changeDate = changeDate; 49 | } 50 | 51 | ContractAttachmentStatus getStatus() { 52 | return status; 53 | } 54 | 55 | void setStatus(ContractAttachmentStatus status) { 56 | this.status = status; 57 | } 58 | 59 | Contract getContract() { 60 | return contract; 61 | } 62 | 63 | void setContract(Contract contract) { 64 | this.contract = contract; 65 | } 66 | 67 | UUID getContractAttachmentNo() { 68 | return contractAttachmentNo; 69 | } 70 | 71 | @Override 72 | public boolean equals(Object o) { 73 | if (this == o) return true; 74 | 75 | if (!(o instanceof ContractAttachment)) 76 | return false; 77 | 78 | ContractAttachment other = (ContractAttachment) o; 79 | 80 | return this.getId() != null && 81 | this.getId().equals(other.getId()); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/agreements/ContractAttachmentData.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.agreements; 2 | 3 | import io.legacyfighter.cabs.common.BaseEntity; 4 | 5 | import javax.persistence.*; 6 | import java.time.Instant; 7 | import java.util.UUID; 8 | 9 | @Entity 10 | class ContractAttachmentData extends BaseEntity { 11 | 12 | @Column(nullable = false) 13 | private UUID contractAttachmentNo; 14 | 15 | @Lob 16 | @Column(name = "data", columnDefinition="BLOB") 17 | private byte[] data; 18 | 19 | @Column(nullable = false) 20 | private Instant creationDate = Instant.now(); 21 | 22 | ContractAttachmentData() { 23 | } 24 | 25 | ContractAttachmentData(UUID contractAttachmentId, byte[] data) { 26 | this.contractAttachmentNo = contractAttachmentId; 27 | this.data = data; 28 | } 29 | 30 | byte[] getData() { 31 | return data; 32 | } 33 | 34 | Instant getCreationDate() { 35 | return creationDate; 36 | } 37 | 38 | UUID getContractAttachmentNo() { 39 | return contractAttachmentNo; 40 | } 41 | 42 | @Override 43 | public boolean equals(Object o) { 44 | if (this == o) return true; 45 | 46 | if (!(o instanceof ContractAttachmentData)) 47 | return false; 48 | 49 | ContractAttachmentData other = (ContractAttachmentData) o; 50 | 51 | return this.getId() != null && 52 | this.getId().equals(other.getId()); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/agreements/ContractAttachmentDataRepository.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.agreements; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | import org.springframework.data.jpa.repository.Modifying; 5 | import org.springframework.data.jpa.repository.Query; 6 | 7 | import java.util.List; 8 | import java.util.Set; 9 | import java.util.UUID; 10 | 11 | interface ContractAttachmentDataRepository extends JpaRepository { 12 | 13 | Set findByContractAttachmentNoIn(List attachmentIds); 14 | 15 | @Modifying 16 | @Query("delete FROM ContractAttachmentData cad WHERE cad.contractAttachmentNo =" + 17 | " (SELECT ca.contractAttachmentNo FROM ContractAttachment ca WHERE ca.id = ?1)") 18 | int deleteByAttachmentId(Long attachmentId); 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/agreements/ContractAttachmentStatus.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.agreements; 2 | 3 | public enum ContractAttachmentStatus { 4 | PROPOSED, ACCEPTED_BY_ONE_SIDE, ACCEPTED_BY_BOTH_SIDES, REJECTED 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/agreements/ContractRepository.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.agreements; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | import org.springframework.data.jpa.repository.Query; 5 | 6 | import java.util.List; 7 | import java.util.UUID; 8 | 9 | interface ContractRepository extends JpaRepository { 10 | 11 | List findByPartnerName(String partnerName); 12 | 13 | @Query("SELECT c FROM Contract c JOIN ContractAttachment ca ON ca.contract.id = c.id WHERE ca.id = ?1") 14 | Contract findByAttachmentId(Long attachmentId); 15 | 16 | @Query("SELECT c.contractAttachmentNo FROM ContractAttachment c WHERE c.id = ?1") 17 | UUID findContractAttachmentNoById(Long attachmentId); 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/agreements/ContractStatus.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.agreements; 2 | 3 | public enum ContractStatus { 4 | NEGOTIATIONS_IN_PROGRESS, REJECTED, ACCEPTED 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/assignment/AssignmentStatus.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.assignment; 2 | 3 | public enum AssignmentStatus { 4 | CANCELLED, 5 | WAITING_FOR_DRIVER_ASSIGNMENT, 6 | DRIVER_ASSIGNMENT_FAILED, 7 | ON_THE_WAY, 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/assignment/DriverAssignmentRepository.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.assignment; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | 5 | import java.util.UUID; 6 | 7 | interface DriverAssignmentRepository extends JpaRepository { 8 | 9 | DriverAssignment findByRequestUUID(UUID transitRequestUUID); 10 | 11 | DriverAssignment findByRequestUUIDAndStatus(UUID transitRequestUUID, AssignmentStatus status); 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/assignment/InvolvedDriversSummary.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.assignment; 2 | 3 | 4 | import java.util.HashSet; 5 | import java.util.Set; 6 | 7 | import static io.legacyfighter.cabs.assignment.AssignmentStatus.DRIVER_ASSIGNMENT_FAILED; 8 | 9 | public class InvolvedDriversSummary { 10 | 11 | public Set proposedDrivers = new HashSet<>(); 12 | public Set driverRejections = new HashSet<>(); 13 | public Long assignedDriver; 14 | public AssignmentStatus status; 15 | 16 | public InvolvedDriversSummary() { 17 | } 18 | 19 | public InvolvedDriversSummary(Set proposedDrivers, Set driverRejections, Long assignedDriverId, AssignmentStatus status) { 20 | this.proposedDrivers = proposedDrivers; 21 | this.driverRejections = driverRejections; 22 | this.status = status; 23 | 24 | } 25 | 26 | public static InvolvedDriversSummary noneFound() { 27 | return new InvolvedDriversSummary(new HashSet<>(), new HashSet<>(), null, DRIVER_ASSIGNMENT_FAILED); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/carfleet/CarClass.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.carfleet; 2 | 3 | public enum CarClass { 4 | ECO, REGULAR, VAN, PREMIUM 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/carfleet/CarTypeActiveCounter.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.carfleet; 2 | 3 | import javax.persistence.*; 4 | 5 | @Entity 6 | class CarTypeActiveCounter { 7 | 8 | @Enumerated(EnumType.STRING) 9 | @Column(nullable = false) 10 | @Id 11 | private CarClass carClass; 12 | 13 | @Column(nullable = false) 14 | private int activeCarsCounter; 15 | 16 | CarTypeActiveCounter(CarClass carClass) { 17 | this.carClass = carClass; 18 | } 19 | 20 | CarTypeActiveCounter() { 21 | } 22 | 23 | int getActiveCarsCounter() { 24 | return activeCarsCounter; 25 | } 26 | 27 | @Override 28 | public boolean equals(Object o) { 29 | if (this == o) return true; 30 | 31 | if (!(o instanceof CarTypeActiveCounter)) 32 | return false; 33 | 34 | CarTypeActiveCounter other = (CarTypeActiveCounter) o; 35 | 36 | return this.carClass != null && 37 | this.carClass.equals(other.carClass); 38 | } 39 | 40 | @Override 41 | public int hashCode() { 42 | return getClass().hashCode(); 43 | } 44 | 45 | } -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/carfleet/CarTypeController.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.carfleet; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.http.ResponseEntity; 5 | import org.springframework.web.bind.annotation.*; 6 | 7 | @RestController 8 | public class CarTypeController { 9 | 10 | @Autowired 11 | private CarTypeService carTypeService; 12 | 13 | 14 | @PostMapping("/cartypes") 15 | ResponseEntity create(@RequestBody CarTypeDTO carTypeDTO) { 16 | CarTypeDTO created = carTypeService.create(carTypeDTO); 17 | return ResponseEntity.ok(created); 18 | } 19 | 20 | @PostMapping("/cartypes/{carClass}/registerCar") 21 | ResponseEntity registerCar(@PathVariable CarClass carClass) { 22 | carTypeService.registerCar(carClass); 23 | return ResponseEntity.ok().build(); 24 | } 25 | 26 | @PostMapping("/cartypes/{carClass}/unregisterCar") 27 | ResponseEntity unregisterCar(@PathVariable CarClass carClass) { 28 | carTypeService.unregisterCar(carClass); 29 | return ResponseEntity.ok().build(); 30 | } 31 | 32 | @PostMapping("/cartypes/{id}/activate") 33 | ResponseEntity activate(@PathVariable Long id) { 34 | carTypeService.activate(id); 35 | return ResponseEntity.ok().build(); 36 | } 37 | 38 | @PostMapping("/cartypes/{id}/deactivate") 39 | ResponseEntity deactivate(@PathVariable Long id) { 40 | carTypeService.deactivate(id); 41 | return ResponseEntity.ok().build(); 42 | } 43 | 44 | @GetMapping("/cartypes/{id}") 45 | ResponseEntity find(@PathVariable Long id) { 46 | CarTypeDTO carType = carTypeService.loadDto(id); 47 | return ResponseEntity.ok(carType); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/carfleet/CarTypeDTO.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.carfleet; 2 | 3 | public class CarTypeDTO { 4 | 5 | private Long id; 6 | 7 | private CarClass carClass; 8 | 9 | private CarType.Status status; 10 | 11 | private int carsCounter; 12 | 13 | private String description; 14 | 15 | private int activeCarsCounter; 16 | 17 | private int minNoOfCarsToActivateClass; 18 | 19 | public CarTypeDTO(CarType carType, int activeCarsCounter) { 20 | this.id = carType.getId(); 21 | this.carClass = carType.getCarClass(); 22 | this.status = carType.getStatus(); 23 | this.carsCounter = carType.getCarsCounter(); 24 | this.description = carType.getDescription(); 25 | this.activeCarsCounter = activeCarsCounter; 26 | this.minNoOfCarsToActivateClass = carType.getMinNoOfCarsToActivateClass(); 27 | } 28 | 29 | public CarTypeDTO() { 30 | 31 | } 32 | 33 | public Long getId() { 34 | return id; 35 | } 36 | 37 | public CarClass getCarClass() { 38 | return carClass; 39 | } 40 | 41 | public void setCarClass(CarClass carClass) { 42 | this.carClass = carClass; 43 | } 44 | 45 | public CarType.Status getStatus() { 46 | return status; 47 | } 48 | 49 | public void setStatus(CarType.Status status) { 50 | this.status = status; 51 | } 52 | 53 | public int getCarsCounter() { 54 | return carsCounter; 55 | } 56 | 57 | public void setCarsCounter(int carsCounter) { 58 | this.carsCounter = carsCounter; 59 | } 60 | 61 | public String getDescription() { 62 | return description; 63 | } 64 | 65 | public void setDescription(String description) { 66 | this.description = description; 67 | } 68 | 69 | public int getActiveCarsCounter() { 70 | return activeCarsCounter; 71 | } 72 | 73 | public void setActiveCarsCounter(int activeCarsCounter) { 74 | this.activeCarsCounter = activeCarsCounter; 75 | } 76 | 77 | 78 | public int getMinNoOfCarsToActivateClass() { 79 | return minNoOfCarsToActivateClass; 80 | } 81 | 82 | public void setMinNoOfCarsToActivateClass(int minNoOfCarsToActivateClass) { 83 | this.minNoOfCarsToActivateClass = minNoOfCarsToActivateClass; 84 | } 85 | } 86 | 87 | 88 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/common/BaseEntity.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.common; 2 | 3 | import javax.persistence.GeneratedValue; 4 | import javax.persistence.Id; 5 | import javax.persistence.MappedSuperclass; 6 | import javax.persistence.Version; 7 | 8 | @MappedSuperclass 9 | public class BaseEntity { 10 | 11 | @GeneratedValue 12 | @Id 13 | protected Long id; 14 | 15 | @Version 16 | private Integer version; 17 | 18 | @Override 19 | public int hashCode() { 20 | return getClass().hashCode(); 21 | } 22 | 23 | public Long getId() { 24 | return id; 25 | } 26 | 27 | } -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/common/Event.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.common; 2 | 3 | public interface Event { 4 | } 5 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/common/EventsPublisher.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.common; 2 | 3 | 4 | import org.springframework.context.ApplicationEventPublisher; 5 | 6 | public class EventsPublisher { 7 | 8 | private final ApplicationEventPublisher applicationEventPublisher; 9 | 10 | public EventsPublisher(ApplicationEventPublisher applicationEventPublisher) { 11 | this.applicationEventPublisher = applicationEventPublisher; 12 | } 13 | 14 | public void publish(Event event) { 15 | applicationEventPublisher.publishEvent(event); 16 | } 17 | } 18 | 19 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/common/JsonToCollectionMapper.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.common; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | 6 | import java.util.HashSet; 7 | import java.util.Set; 8 | 9 | public class JsonToCollectionMapper { 10 | 11 | private static ObjectMapper objectMapper; 12 | 13 | static { 14 | objectMapper = new ObjectMapper(); 15 | } 16 | 17 | public static Set deserialize(String json) { 18 | if (json == null) { 19 | return new HashSet<>(); 20 | } 21 | try { 22 | return objectMapper.readValue(json, objectMapper.getTypeFactory().constructCollectionType(Set.class, Long.class)); 23 | } catch (JsonProcessingException e) { 24 | throw new RuntimeException(e); 25 | } 26 | } 27 | 28 | public static String serialize(Set collectionOfIds) { 29 | try { 30 | return objectMapper.writeValueAsString(collectionOfIds); 31 | } catch (JsonProcessingException e) { 32 | throw new RuntimeException(e); 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/config/AppProperties.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.config; 2 | 3 | import org.springframework.boot.context.properties.ConfigurationProperties; 4 | 5 | @ConfigurationProperties(prefix = "app") 6 | public class AppProperties { 7 | 8 | private Integer noOfTransitsForClaimAutomaticRefund; 9 | private Integer automaticRefundForVipThreshold; 10 | private Integer minNoOfCarsForEcoClass; 11 | 12 | private Integer milesExpirationInDays = 365; 13 | private Integer defaultMilesBonus = 10; 14 | 15 | public Integer getAutomaticRefundForVipThreshold() { 16 | return automaticRefundForVipThreshold; 17 | } 18 | 19 | public void setAutomaticRefundForVipThreshold(Integer automaticRefundForVipThreshold) { 20 | this.automaticRefundForVipThreshold = automaticRefundForVipThreshold; 21 | } 22 | 23 | public Integer getNoOfTransitsForClaimAutomaticRefund() { 24 | return noOfTransitsForClaimAutomaticRefund; 25 | } 26 | 27 | public void setNoOfTransitsForClaimAutomaticRefund(Integer noOfTransitsForClaimAutomaticRefund) { 28 | this.noOfTransitsForClaimAutomaticRefund = noOfTransitsForClaimAutomaticRefund; 29 | } 30 | 31 | public Integer getMinNoOfCarsForEcoClass() { 32 | return minNoOfCarsForEcoClass; 33 | } 34 | 35 | public void setMinNoOfCarsForEcoClass(Integer minNoOfCarsForEcoClass) { 36 | this.minNoOfCarsForEcoClass = minNoOfCarsForEcoClass; 37 | } 38 | 39 | public int getMilesExpirationInDays() { 40 | return milesExpirationInDays; 41 | } 42 | 43 | public void setMilesExpirationInDays(int milesExpirationInDays) { 44 | this.milesExpirationInDays = milesExpirationInDays; 45 | } 46 | 47 | public Integer getDefaultMilesBonus() { 48 | return defaultMilesBonus; 49 | } 50 | 51 | public void setDefaultMilesBonus(Integer defaultMilesBonus) { 52 | this.defaultMilesBonus = defaultMilesBonus; 53 | } 54 | } 55 | 56 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/config/ClockConfig.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | 6 | import java.time.Clock; 7 | 8 | @Configuration 9 | public class ClockConfig { 10 | 11 | @Bean 12 | Clock clock() { 13 | return Clock.systemDefaultZone(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/config/EventPublisherConfiguration.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.config; 2 | 3 | 4 | import io.legacyfighter.cabs.common.EventsPublisher; 5 | import org.springframework.context.ApplicationEventPublisher; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | 9 | @Configuration 10 | class EventPublisherConfiguration { 11 | 12 | @Bean 13 | EventsPublisher eventsPublisher(ApplicationEventPublisher applicationEventPublisher) { 14 | return new EventsPublisher(applicationEventPublisher); 15 | } 16 | } 17 | 18 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/contracts/application/acme/dynamic/DocumentOperationResult.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.contracts.application.acme.dynamic; 2 | 3 | import io.legacyfighter.cabs.contracts.model.ContentId; 4 | import io.legacyfighter.cabs.contracts.model.content.DocumentNumber; 5 | 6 | import java.util.List; 7 | import java.util.Map; 8 | 9 | public class DocumentOperationResult { 10 | 11 | public enum Result{ 12 | SUCCESS, ERROR 13 | } 14 | 15 | private Result result; 16 | private String stateName; 17 | private ContentId contentId; 18 | 19 | private Long documentHeaderId; 20 | private DocumentNumber documentNumber; 21 | 22 | private Map> possibleTransitionsAndRules; 23 | private boolean contentChangePossible; 24 | private String contentChangePredicate; 25 | 26 | 27 | public DocumentOperationResult(Result result, Long documentHeaderId, DocumentNumber documentNumber, String stateName, ContentId contentId, Map> possibleTransitionsAndRules, boolean contentChangePossible, String contentChangePredicate) { 28 | this.result = result; 29 | this.documentHeaderId = documentHeaderId; 30 | this.documentNumber = documentNumber; 31 | this.stateName = stateName; 32 | this.contentId = contentId; 33 | this.possibleTransitionsAndRules = possibleTransitionsAndRules; 34 | this.contentChangePossible = contentChangePossible; 35 | this.contentChangePredicate = contentChangePredicate; 36 | } 37 | 38 | public Map> getPossibleTransitionsAndRules() { 39 | return possibleTransitionsAndRules; 40 | } 41 | 42 | public String getContentChangePredicate() { 43 | return contentChangePredicate; 44 | } 45 | 46 | public boolean isContentChangePossible() { 47 | return contentChangePossible; 48 | } 49 | 50 | public Result getResult() { 51 | return result; 52 | } 53 | 54 | public String getStateName() { 55 | return stateName; 56 | } 57 | 58 | public DocumentNumber getDocumentNumber() { 59 | return documentNumber; 60 | } 61 | 62 | public Long getDocumentHeaderId() { 63 | return documentHeaderId; 64 | } 65 | 66 | public ContentId getContentId() { 67 | return contentId; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/contracts/application/acme/straigthforward/AcmeStateFactory.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.contracts.application.acme.straigthforward; 2 | 3 | import io.legacyfighter.cabs.contracts.model.DocumentHeader; 4 | import io.legacyfighter.cabs.contracts.model.state.straightforward.BaseState; 5 | import io.legacyfighter.cabs.contracts.model.state.straightforward.acme.DraftState; 6 | import org.springframework.stereotype.Component; 7 | 8 | @Component 9 | class AcmeStateFactory { 10 | public BaseState create(DocumentHeader header){ 11 | //sample impl is based on class names 12 | //other possibilities: names Dependency Injection Containers, states persisted via ORM Discriminator mechanism, mapper 13 | String className = header.getStateDescriptor(); 14 | 15 | if (className == null) { 16 | DraftState state = new DraftState(); 17 | state.init(header); 18 | return state; 19 | } 20 | 21 | try { 22 | Class clazz = (Class) Class.forName(className); 23 | BaseState state = clazz.getConstructor().newInstance(); 24 | state.init(header); 25 | return state; 26 | } 27 | catch (Exception e){ 28 | throw new RuntimeException(e); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/contracts/application/acme/straigthforward/ContractResult.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.contracts.application.acme.straigthforward; 2 | 3 | import io.legacyfighter.cabs.contracts.model.content.DocumentNumber; 4 | 5 | public class ContractResult { 6 | 7 | public enum Result{ 8 | FAILURE, SUCCESS 9 | } 10 | 11 | private Result result; 12 | private Long documentHeaderId; 13 | private DocumentNumber documentNumber; 14 | private String stateDescriptor; 15 | 16 | public ContractResult(Result result, Long documentHeaderId, DocumentNumber documentNumber, String stateDescriptor) { 17 | this.result = result; 18 | this.documentHeaderId = documentHeaderId; 19 | this.documentNumber = documentNumber; 20 | this.stateDescriptor = stateDescriptor; 21 | } 22 | 23 | public Result getResult() { 24 | return result; 25 | } 26 | 27 | public DocumentNumber getDocumentNumber() { 28 | return documentNumber; 29 | } 30 | 31 | public Long getDocumentHeaderId() { 32 | return documentHeaderId; 33 | } 34 | 35 | public String getStateDescriptor() { 36 | return stateDescriptor; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/contracts/application/editor/CommitResult.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.contracts.application.editor; 2 | 3 | import java.util.UUID; 4 | 5 | public class CommitResult { 6 | 7 | 8 | public enum Result{ 9 | FAILURE, SUCCESS 10 | } 11 | 12 | private UUID contentId; 13 | private Result result; 14 | private String message; 15 | 16 | public CommitResult(UUID contentId, Result result, String message) { 17 | this.contentId = contentId; 18 | this.result = result; 19 | this.message = message; 20 | } 21 | 22 | public CommitResult(UUID documentId, Result result) { 23 | this(documentId, result, null); 24 | } 25 | 26 | public Result getResult() { 27 | return result; 28 | } 29 | 30 | public UUID getContentId() { 31 | return contentId; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/contracts/application/editor/DocumentDTO.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.contracts.application.editor; 2 | 3 | import io.legacyfighter.cabs.contracts.model.content.ContentVersion; 4 | 5 | import java.util.UUID; 6 | 7 | public class DocumentDTO { 8 | private final UUID contentId; 9 | private final String physicalContent; 10 | private final ContentVersion contentVersion; 11 | 12 | public DocumentDTO(UUID contentId, String physicalContent, ContentVersion contentVersion) { 13 | this.contentId = contentId; 14 | this.physicalContent = physicalContent; 15 | this.contentVersion = contentVersion; 16 | } 17 | 18 | public UUID getContentId() { 19 | return contentId; 20 | } 21 | 22 | public ContentVersion getDocumentVersion() { 23 | return contentVersion; 24 | } 25 | 26 | public String getPhysicalContent() { 27 | return physicalContent; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/contracts/application/editor/DocumentEditor.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.contracts.application.editor; 2 | 3 | import io.legacyfighter.cabs.contracts.model.content.DocumentContent; 4 | import io.legacyfighter.cabs.contracts.model.content.DocumentContentRepository; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.stereotype.Service; 7 | import org.springframework.transaction.annotation.Transactional; 8 | 9 | import java.util.UUID; 10 | 11 | @Service 12 | @Transactional 13 | public class DocumentEditor { 14 | @Autowired 15 | private DocumentContentRepository documentContentRepository; 16 | 17 | public CommitResult commit(DocumentDTO document){ 18 | UUID previousID = document.getContentId(); 19 | DocumentContent content = new DocumentContent(previousID, document.getDocumentVersion(), document.getPhysicalContent()); 20 | documentContentRepository.save(content); 21 | return new CommitResult(content.getId(), CommitResult.Result.SUCCESS); 22 | } 23 | 24 | 25 | public DocumentDTO get(UUID contentId){ 26 | DocumentContent content = documentContentRepository.getOne(contentId); 27 | return new DocumentDTO(contentId, content.getPhysicalContent(), content.getDocumentVersion()); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/contracts/infra/JpaDocumentHeaderRepository.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.contracts.infra; 2 | 3 | import io.legacyfighter.cabs.contracts.model.DocumentHeader; 4 | import io.legacyfighter.cabs.contracts.model.DocumentHeaderRepository; 5 | import org.springframework.stereotype.Repository; 6 | 7 | import javax.persistence.EntityManager; 8 | import javax.persistence.LockModeType; 9 | import javax.persistence.PersistenceContext; 10 | 11 | //LockModeType surprised you? MUST see: https://youtu.be/uj25PbkHb94?t=499 12 | 13 | @Repository 14 | public class JpaDocumentHeaderRepository implements DocumentHeaderRepository { 15 | @PersistenceContext 16 | private EntityManager entityManager; 17 | 18 | @Override 19 | public DocumentHeader getOne(Long id){ 20 | return entityManager.find(DocumentHeader.class, id, LockModeType.OPTIMISTIC); 21 | } 22 | 23 | @Override 24 | public void save(DocumentHeader header) { 25 | if (entityManager.contains(header)) 26 | entityManager.lock(header, LockModeType.OPTIMISTIC_FORCE_INCREMENT); 27 | else 28 | entityManager.persist(header); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/contracts/legacy/BaseAggregateRoot.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.contracts.legacy; 2 | 3 | import io.legacyfighter.cabs.common.BaseEntity; 4 | 5 | public abstract class BaseAggregateRoot extends BaseEntity { 6 | //just to be DDD compliant 7 | 8 | //if you have added a DDD skill to your linkedin profile just because extending this class add + below: 9 | //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/contracts/legacy/Contract2.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.contracts.legacy; 2 | 3 | import java.util.Random; 4 | 5 | public class Contract2 extends Document implements Versionable{ 6 | 7 | public Contract2(String number, User creator) { 8 | super(number, creator); 9 | } 10 | 11 | @Override 12 | public void publish() throws UnsupportedTransitionException { 13 | throw new UnsupportedTransitionException(status, DocumentStatus.PUBLISHED); 14 | } 15 | 16 | public void accept(){ 17 | if (status == DocumentStatus.VERIFIED){ 18 | status = DocumentStatus.PUBLISHED; //reusing unused enum to provide data model for new status 19 | } 20 | } 21 | 22 | //Contracts just don't have a title, it's just a part of the content 23 | @Override 24 | public void changeTitle(String title) { 25 | super.changeContent(title + getContent()); 26 | } 27 | 28 | //NOT @Override 29 | public void changeContent(String content, String userStatus){ 30 | if (userStatus == "ChiefSalesOfficerStatus" || misterVladimirIsLoggedIn(userStatus)){ 31 | overridePublished = true; 32 | changeContent(content); 33 | } 34 | } 35 | 36 | private boolean misterVladimirIsLoggedIn(String userStatus) { 37 | return userStatus.toLowerCase().trim().equals("!!!id=" + NUMBER_OF_THE_BEAST); 38 | } 39 | 40 | private static final String NUMBER_OF_THE_BEAST = "616"; 41 | 42 | @Override 43 | public void recreateTo(long version) { 44 | //TODO need to learn Kafka 45 | } 46 | 47 | @Override 48 | public long getLastVersion() { 49 | return new Random().nextLong();//FIXME, don't know how to return a null 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/contracts/legacy/DocumentStatus.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.contracts.legacy; 2 | 3 | public enum DocumentStatus { 4 | DRAFT, VERIFIED, PUBLISHED, ARCHIVED 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/contracts/legacy/OOParadigm.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.contracts.legacy; 2 | 3 | abstract class OOParadigm { 4 | //2. enkapsulacja - ukrycie impl 5 | private Object filed; 6 | 7 | //1. abstrakcja - agent odbierający sygnały 8 | public void method(){ 9 | //do sth 10 | } 11 | 12 | //3. polimorfizm - zmienne zachowania 13 | protected abstract void abstractStep(); 14 | } 15 | 16 | //4. dziedziczenie - technika wspierająca polimorizm 17 | class ConcreteType extends OOParadigm{ 18 | 19 | @Override 20 | protected void abstractStep() { 21 | 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/contracts/legacy/Printable.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.contracts.legacy; 2 | 3 | /** 4 | * Marker interface. Corporate standard. 5 | */ 6 | public interface Printable { 7 | 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/contracts/legacy/UnsupportedTransitionException.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.contracts.legacy; 2 | 3 | public class UnsupportedTransitionException extends RuntimeException{ 4 | private DocumentStatus current; 5 | private DocumentStatus desired; 6 | 7 | public UnsupportedTransitionException(DocumentStatus current, DocumentStatus desired) { 8 | super("can not transit form " + current + " to " + desired); 9 | this.current = current; 10 | this.desired = desired; 11 | } 12 | 13 | public DocumentStatus getCurrent() { 14 | return current; 15 | } 16 | 17 | public DocumentStatus getDesired() { 18 | return desired; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/contracts/legacy/User.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.contracts.legacy; 2 | 3 | import io.legacyfighter.cabs.common.BaseEntity; 4 | 5 | import javax.persistence.Entity; 6 | 7 | @Entity 8 | public class User extends BaseEntity { 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/contracts/legacy/UserRepository.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.contracts.legacy; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | 5 | public interface UserRepository extends JpaRepository { 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/contracts/legacy/Versionable.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.contracts.legacy; 2 | 3 | public interface Versionable { 4 | void recreateTo(long version); 5 | long getLastVersion(); 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/contracts/model/ContentId.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.contracts.model; 2 | 3 | import javax.persistence.Embeddable; 4 | import java.util.Objects; 5 | import java.util.UUID; 6 | 7 | @Embeddable 8 | public class ContentId { 9 | private UUID contentId; 10 | 11 | protected ContentId(){} 12 | 13 | public ContentId(UUID contentId){ 14 | this.contentId = contentId; 15 | } 16 | 17 | @Override 18 | public String toString() { 19 | return "ContentId{" + 20 | "contentId=" + contentId + 21 | '}'; 22 | } 23 | 24 | @Override 25 | public boolean equals(Object o) { 26 | if (this == o) return true; 27 | if (o == null || getClass() != o.getClass()) return false; 28 | ContentId contentId1 = (ContentId) o; 29 | return contentId.equals(contentId1.contentId); 30 | } 31 | 32 | @Override 33 | public int hashCode() { 34 | return Objects.hash(contentId); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/contracts/model/DocumentHeader.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.contracts.model; 2 | 3 | import io.legacyfighter.cabs.common.BaseEntity; 4 | import io.legacyfighter.cabs.contracts.model.content.DocumentNumber; 5 | 6 | import javax.persistence.Embedded; 7 | import javax.persistence.Entity; 8 | 9 | @Entity 10 | public class DocumentHeader extends BaseEntity { 11 | @Embedded 12 | private DocumentNumber number; 13 | 14 | private Long authorId; 15 | 16 | private Long verifierId; 17 | 18 | private String stateDescriptor; 19 | 20 | @Embedded 21 | private ContentId contentId; 22 | 23 | protected DocumentHeader(){ 24 | 25 | } 26 | 27 | public DocumentHeader(Long authorId, DocumentNumber number){ 28 | this.authorId = authorId; 29 | this.number = number; 30 | } 31 | 32 | public void changeCurrentContent(ContentId contentId){ 33 | this.contentId = contentId; 34 | } 35 | 36 | public boolean notEmpty() { 37 | return contentId != null; 38 | } 39 | 40 | 41 | public Long getVerifier() { 42 | return verifierId; 43 | } 44 | 45 | public Long getAuthorId() { 46 | return authorId; 47 | } 48 | 49 | public void setVerifierId(Long verifierId) { 50 | this.verifierId = verifierId; 51 | } 52 | 53 | public void setAuthorId(Long authorId) { 54 | this.authorId = authorId; 55 | } 56 | 57 | public String getStateDescriptor() { 58 | return stateDescriptor; 59 | } 60 | 61 | public void setStateDescriptor(String stateDescriptor) { 62 | this.stateDescriptor = stateDescriptor; 63 | } 64 | 65 | public DocumentNumber getDocumentNumber() { 66 | return number; 67 | } 68 | 69 | public ContentId getContentId() { 70 | return contentId; 71 | } 72 | 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/contracts/model/DocumentHeaderRepository.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.contracts.model; 2 | 3 | 4 | public interface DocumentHeaderRepository { 5 | DocumentHeader getOne(Long id); 6 | 7 | void save(DocumentHeader header); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/contracts/model/content/ContentVersion.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.contracts.model.content; 2 | 3 | import javax.persistence.Embeddable; 4 | 5 | @Embeddable 6 | public class ContentVersion { 7 | private String contentVersion; 8 | 9 | protected ContentVersion(){} 10 | 11 | public ContentVersion(String contentVersion){ 12 | this.contentVersion = contentVersion; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/contracts/model/content/DocumentContent.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.contracts.model.content; 2 | 3 | import javax.persistence.Embedded; 4 | import javax.persistence.Entity; 5 | import javax.persistence.GeneratedValue; 6 | import javax.persistence.Id; 7 | import java.util.UUID; 8 | 9 | @Entity 10 | public class DocumentContent { 11 | @Id 12 | @GeneratedValue 13 | private UUID id; 14 | 15 | private UUID previousId; 16 | 17 | private String physicalContent; //some kind of reference to file, version control. In sour sample i will be a blob stored in DB:) 18 | 19 | @Embedded 20 | private ContentVersion contentVersion;//just a human readable descriptor 21 | 22 | protected DocumentContent() { 23 | 24 | } 25 | 26 | public DocumentContent(UUID previousId, ContentVersion contentVersion, String physicalContent) { 27 | this.previousId = previousId; 28 | this.contentVersion = contentVersion; 29 | this.physicalContent = physicalContent; 30 | } 31 | 32 | public DocumentContent(ContentVersion version, String physicalContent) { 33 | this(null, version, physicalContent); 34 | } 35 | 36 | public UUID getId() { 37 | return id; 38 | } 39 | 40 | public String getPhysicalContent() { 41 | return physicalContent; 42 | } 43 | 44 | public ContentVersion getDocumentVersion() { 45 | return contentVersion; 46 | } 47 | } -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/contracts/model/content/DocumentContentRepository.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.contracts.model.content; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | 5 | import java.util.UUID; 6 | 7 | public interface DocumentContentRepository extends JpaRepository { 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/contracts/model/content/DocumentNumber.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.contracts.model.content; 2 | 3 | import javax.persistence.Embeddable; 4 | 5 | @Embeddable 6 | public class DocumentNumber { 7 | private String number; 8 | 9 | protected DocumentNumber(){ 10 | 11 | } 12 | 13 | public DocumentNumber(String number){ 14 | this.number = number; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/contracts/model/state/dynamic/ChangeCommand.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.contracts.model.state.dynamic; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | public class ChangeCommand { 7 | private String desiredState; 8 | private Map params; 9 | 10 | public ChangeCommand(String desiredState, Map params){ 11 | this.desiredState = desiredState; 12 | this.params = params; 13 | } 14 | 15 | public ChangeCommand(String desiredState){ 16 | this(desiredState, new HashMap<>()); 17 | } 18 | 19 | public ChangeCommand withParam(String name, Object value){ 20 | params.put(name, value); 21 | return this; 22 | } 23 | 24 | public String getDesiredState() { 25 | return desiredState; 26 | } 27 | 28 | public T getParam(String name, Class type){ 29 | return (T) params.get(name); 30 | } 31 | 32 | @Override 33 | public String toString() { 34 | return "ChangeCommand{" + 35 | "desiredState='" + desiredState + '\'' + 36 | '}'; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/contracts/model/state/dynamic/StateConfig.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.contracts.model.state.dynamic; 2 | 3 | import io.legacyfighter.cabs.contracts.model.DocumentHeader; 4 | 5 | public interface StateConfig { 6 | State begin(DocumentHeader documentHeader); 7 | 8 | State recreate(DocumentHeader documentHeader); 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/contracts/model/state/dynamic/config/actions/ChangeVerifier.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.contracts.model.state.dynamic.config.actions; 2 | 3 | import io.legacyfighter.cabs.contracts.model.DocumentHeader; 4 | import io.legacyfighter.cabs.contracts.model.state.dynamic.ChangeCommand; 5 | 6 | import java.util.function.BiFunction; 7 | 8 | public class ChangeVerifier implements BiFunction { 9 | 10 | public static final String PARAM_VERIFIER = "verifier"; 11 | 12 | @Override 13 | public Void apply(DocumentHeader documentHeader, ChangeCommand command) { 14 | documentHeader.setVerifierId(command.getParam(PARAM_VERIFIER, Long.class)); 15 | return null; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/contracts/model/state/dynamic/config/actions/PublishEvent.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.contracts.model.state.dynamic.config.actions; 2 | 3 | import io.legacyfighter.cabs.contracts.model.ContentId; 4 | import io.legacyfighter.cabs.contracts.model.DocumentHeader; 5 | import io.legacyfighter.cabs.contracts.model.content.DocumentNumber; 6 | import io.legacyfighter.cabs.contracts.model.state.dynamic.ChangeCommand; 7 | import io.legacyfighter.cabs.contracts.model.state.dynamic.config.events.DocumentEvent; 8 | import org.springframework.context.ApplicationEventPublisher; 9 | 10 | import java.lang.reflect.Constructor; 11 | import java.util.function.BiFunction; 12 | 13 | public class PublishEvent implements BiFunction { 14 | 15 | private Class eventClass; 16 | 17 | private ApplicationEventPublisher publisher; 18 | 19 | public PublishEvent(Class eventClass, ApplicationEventPublisher publisher) { 20 | this.eventClass = eventClass; 21 | this.publisher = publisher; 22 | } 23 | 24 | @Override 25 | public Void apply(DocumentHeader documentHeader, ChangeCommand command) { 26 | DocumentEvent event; 27 | try { 28 | Constructor constructor = eventClass.getDeclaredConstructor(Long.class, String.class, ContentId.class, DocumentNumber.class); 29 | event = constructor.newInstance(documentHeader.getId(), documentHeader.getStateDescriptor(), documentHeader.getContentId(), documentHeader.getDocumentNumber()); 30 | } catch (Exception e) { 31 | throw new RuntimeException(e); 32 | } 33 | 34 | publisher.publishEvent(event); 35 | 36 | return null; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/contracts/model/state/dynamic/config/changes/FixedState.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.contracts.model.state.dynamic.config.changes; 2 | 3 | import io.legacyfighter.cabs.contracts.model.state.dynamic.State; 4 | 5 | import java.util.function.Function; 6 | 7 | public class FixedState implements Function { 8 | private String stateName; 9 | public FixedState(String stateName) { 10 | this.stateName = stateName; 11 | } 12 | 13 | @Override 14 | public State apply(State state) { 15 | return new State(stateName); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/contracts/model/state/dynamic/config/events/DocumentEvent.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.contracts.model.state.dynamic.config.events; 2 | 3 | import io.legacyfighter.cabs.contracts.model.ContentId; 4 | import io.legacyfighter.cabs.contracts.model.content.DocumentNumber; 5 | import org.springframework.context.ApplicationEvent; 6 | 7 | public abstract class DocumentEvent extends ApplicationEvent { 8 | private final Long documentId; 9 | private final String currentSate; 10 | private final ContentId contentId; 11 | private final DocumentNumber number; 12 | 13 | public DocumentEvent(Long documentId, String currentSate, ContentId contentId, DocumentNumber number) { 14 | super(number); 15 | this.documentId = documentId; 16 | this.currentSate = currentSate; 17 | this.contentId = contentId; 18 | this.number = number; 19 | } 20 | 21 | public Long getDocumentId() { 22 | return documentId; 23 | } 24 | 25 | public String getCurrentSate() { 26 | return currentSate; 27 | } 28 | 29 | public ContentId getContentId() { 30 | return contentId; 31 | } 32 | 33 | public DocumentNumber getNumber() { 34 | return number; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/contracts/model/state/dynamic/config/events/DocumentPublished.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.contracts.model.state.dynamic.config.events; 2 | 3 | import io.legacyfighter.cabs.contracts.model.ContentId; 4 | import io.legacyfighter.cabs.contracts.model.content.DocumentNumber; 5 | 6 | public class DocumentPublished extends DocumentEvent{ 7 | public DocumentPublished(Long documentId, String currentSate, ContentId contentId, DocumentNumber number) { 8 | super(documentId, currentSate, contentId, number); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/contracts/model/state/dynamic/config/events/DocumentUnpublished.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.contracts.model.state.dynamic.config.events; 2 | 3 | import io.legacyfighter.cabs.contracts.model.ContentId; 4 | import io.legacyfighter.cabs.contracts.model.content.DocumentNumber; 5 | 6 | public class DocumentUnpublished extends DocumentEvent{ 7 | public DocumentUnpublished(Long documentId, String currentSate, ContentId contentId, DocumentNumber number) { 8 | super(documentId, currentSate, contentId, number); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/contracts/model/state/dynamic/config/predicates/contentchange/NegativePredicate.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.contracts.model.state.dynamic.config.predicates.contentchange; 2 | 3 | import io.legacyfighter.cabs.contracts.model.state.dynamic.State; 4 | 5 | import java.util.function.Predicate; 6 | 7 | public class NegativePredicate implements Predicate { 8 | @Override 9 | public boolean test(State state) { 10 | return false; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/contracts/model/state/dynamic/config/predicates/contentchange/PositivePredicate.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.contracts.model.state.dynamic.config.predicates.contentchange; 2 | 3 | import io.legacyfighter.cabs.contracts.model.state.dynamic.State; 4 | 5 | import java.util.function.Predicate; 6 | 7 | public class PositivePredicate implements Predicate { 8 | @Override 9 | public boolean test(State state) { 10 | return true; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/contracts/model/state/dynamic/config/predicates/statechange/AuthorIsNotAVerifier.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.contracts.model.state.dynamic.config.predicates.statechange; 2 | 3 | import io.legacyfighter.cabs.contracts.model.state.dynamic.ChangeCommand; 4 | import io.legacyfighter.cabs.contracts.model.state.dynamic.State; 5 | import io.legacyfighter.cabs.contracts.model.state.dynamic.config.actions.ChangeVerifier; 6 | 7 | import java.util.function.BiFunction; 8 | 9 | public class AuthorIsNotAVerifier implements BiFunction { 10 | 11 | public static final String PARAM_VERIFIER = ChangeVerifier.PARAM_VERIFIER; 12 | 13 | @Override 14 | public Boolean apply(State state, ChangeCommand command) { 15 | return ! command.getParam(PARAM_VERIFIER, Long.class).equals(state.getDocumentHeader().getAuthorId()); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/contracts/model/state/dynamic/config/predicates/statechange/ContentNotEmptyVerifier.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.contracts.model.state.dynamic.config.predicates.statechange; 2 | 3 | import io.legacyfighter.cabs.contracts.model.state.dynamic.ChangeCommand; 4 | import io.legacyfighter.cabs.contracts.model.state.dynamic.State; 5 | 6 | import java.util.function.BiFunction; 7 | 8 | public class ContentNotEmptyVerifier implements BiFunction { 9 | 10 | @Override 11 | public Boolean apply(State state, ChangeCommand command) { 12 | return state.getDocumentHeader().getContentId() != null; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/contracts/model/state/dynamic/config/predicates/statechange/PositiveVerifier.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.contracts.model.state.dynamic.config.predicates.statechange; 2 | 3 | import io.legacyfighter.cabs.contracts.model.state.dynamic.ChangeCommand; 4 | import io.legacyfighter.cabs.contracts.model.state.dynamic.State; 5 | 6 | import java.util.function.BiFunction; 7 | 8 | public class PositiveVerifier implements BiFunction { 9 | 10 | @Override 11 | public Boolean apply(State state, ChangeCommand command) { 12 | return true; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/contracts/model/state/dynamic/config/predicates/statechange/PreviousStateVerifier.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.contracts.model.state.dynamic.config.predicates.statechange; 2 | 3 | import io.legacyfighter.cabs.contracts.model.state.dynamic.ChangeCommand; 4 | import io.legacyfighter.cabs.contracts.model.state.dynamic.State; 5 | 6 | import java.util.function.BiFunction; 7 | 8 | public class PreviousStateVerifier implements BiFunction { 9 | private final String stateDescriptor; 10 | 11 | public PreviousStateVerifier(String stateDescriptor) { 12 | this.stateDescriptor = stateDescriptor; 13 | } 14 | 15 | @Override 16 | public Boolean apply(State state, ChangeCommand command) { 17 | return state.getStateDescriptor().equals(stateDescriptor); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/contracts/model/state/straightforward/BaseState.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.contracts.model.state.straightforward; 2 | 3 | import io.legacyfighter.cabs.contracts.model.ContentId; 4 | import io.legacyfighter.cabs.contracts.model.DocumentHeader; 5 | 6 | //TODO introduce an interface 7 | 8 | public abstract class BaseState { 9 | protected DocumentHeader documentHeader; 10 | 11 | public void init(DocumentHeader documentHeader){ 12 | this.documentHeader = documentHeader; 13 | documentHeader.setStateDescriptor(getStateDescriptor()); 14 | } 15 | 16 | public BaseState changeContent(ContentId currentContent){ 17 | if (canChangeContent()){ 18 | BaseState newState = stateAfterContentChange(); 19 | newState.init(documentHeader); 20 | this.documentHeader.changeCurrentContent(currentContent); 21 | newState.acquire(documentHeader); 22 | return newState; 23 | } 24 | return this; 25 | } 26 | 27 | protected abstract boolean canChangeContent(); 28 | 29 | protected abstract BaseState stateAfterContentChange(); 30 | 31 | public BaseState changeState(BaseState newState){ 32 | if (newState.canChangeFrom(this)) { 33 | newState.init(documentHeader); 34 | documentHeader.setStateDescriptor(newState.getStateDescriptor()); 35 | newState.acquire(documentHeader); 36 | return newState; 37 | } 38 | return this; 39 | } 40 | 41 | public String getStateDescriptor(){ 42 | return getClass().getName(); 43 | } 44 | 45 | /** 46 | * template method that allows to perform addition actions during state change 47 | */ 48 | protected abstract void acquire(DocumentHeader documentHeader); 49 | 50 | protected abstract boolean canChangeFrom(BaseState previousState); 51 | 52 | public DocumentHeader getDocumentHeader(){ 53 | return documentHeader; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/contracts/model/state/straightforward/acme/ArchivedState.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.contracts.model.state.straightforward.acme; 2 | 3 | import io.legacyfighter.cabs.contracts.model.DocumentHeader; 4 | import io.legacyfighter.cabs.contracts.model.state.straightforward.BaseState; 5 | 6 | public class ArchivedState extends BaseState { 7 | @Override 8 | protected boolean canChangeContent() { 9 | return false; 10 | } 11 | 12 | @Override 13 | protected BaseState stateAfterContentChange() { 14 | return this; 15 | } 16 | 17 | @Override 18 | protected boolean canChangeFrom(BaseState previousState) { 19 | return true; 20 | } 21 | 22 | @Override 23 | protected void acquire(DocumentHeader documentHeader) { 24 | 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/contracts/model/state/straightforward/acme/DraftState.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.contracts.model.state.straightforward.acme; 2 | 3 | import io.legacyfighter.cabs.contracts.model.DocumentHeader; 4 | import io.legacyfighter.cabs.contracts.model.state.straightforward.BaseState; 5 | 6 | public class DraftState extends BaseState { 7 | 8 | //BAD IDEA! 9 | //public BaseState publish(){ 10 | //if some validation 11 | // return new PublishedState(); 12 | //} 13 | 14 | @Override 15 | protected boolean canChangeContent() { 16 | return true; 17 | } 18 | 19 | @Override 20 | protected BaseState stateAfterContentChange() { 21 | return this; 22 | } 23 | 24 | @Override 25 | protected boolean canChangeFrom(BaseState previousState) { 26 | return true; 27 | } 28 | 29 | @Override 30 | protected void acquire(DocumentHeader documentHeader) { 31 | 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/contracts/model/state/straightforward/acme/PublishedState.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.contracts.model.state.straightforward.acme; 2 | 3 | import io.legacyfighter.cabs.contracts.model.DocumentHeader; 4 | import io.legacyfighter.cabs.contracts.model.state.straightforward.BaseState; 5 | 6 | public class PublishedState extends BaseState { 7 | 8 | @Override 9 | protected boolean canChangeContent() { 10 | return false; 11 | } 12 | 13 | @Override 14 | protected BaseState stateAfterContentChange() { 15 | return this; 16 | } 17 | 18 | @Override 19 | protected boolean canChangeFrom(BaseState previousState) { 20 | return previousState instanceof VerifiedState 21 | && previousState.getDocumentHeader().notEmpty(); 22 | } 23 | 24 | @Override 25 | protected void acquire(DocumentHeader documentHeader) { 26 | 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/contracts/model/state/straightforward/acme/VerifiedState.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.contracts.model.state.straightforward.acme; 2 | 3 | import io.legacyfighter.cabs.contracts.model.DocumentHeader; 4 | import io.legacyfighter.cabs.contracts.model.state.straightforward.BaseState; 5 | 6 | public class VerifiedState extends BaseState { 7 | private Long verifierId; 8 | 9 | public VerifiedState(Long verifierId) { 10 | this.verifierId = verifierId; 11 | } 12 | 13 | public VerifiedState(){ 14 | 15 | } 16 | 17 | 18 | @Override 19 | protected boolean canChangeContent() { 20 | return true; 21 | } 22 | 23 | @Override 24 | protected BaseState stateAfterContentChange() { 25 | return new DraftState(); 26 | } 27 | 28 | @Override 29 | protected boolean canChangeFrom(BaseState previousState) { 30 | return previousState instanceof DraftState 31 | && !previousState.getDocumentHeader().getAuthorId().equals(verifierId) 32 | && previousState.getDocumentHeader().notEmpty(); 33 | } 34 | 35 | @Override 36 | protected void acquire(DocumentHeader documentHeader) { 37 | documentHeader.setVerifierId(verifierId); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/crm/Client.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.crm; 2 | 3 | import io.legacyfighter.cabs.common.BaseEntity; 4 | 5 | import javax.persistence.Entity; 6 | import javax.persistence.EnumType; 7 | import javax.persistence.Enumerated; 8 | 9 | @Entity 10 | public class Client extends BaseEntity { 11 | 12 | public enum Type { 13 | NORMAL, VIP 14 | } 15 | 16 | public enum ClientType { 17 | INDIVIDUAL, COMPANY 18 | } 19 | 20 | public enum PaymentType { 21 | PRE_PAID, POST_PAID, MONTHLY_INVOICE 22 | } 23 | 24 | private Type type; 25 | 26 | private String name; 27 | 28 | private String lastName; 29 | 30 | @Enumerated(EnumType.STRING) 31 | private PaymentType defaultPaymentType; 32 | 33 | @Enumerated(EnumType.STRING) 34 | private ClientType clientType; 35 | 36 | public Client() { 37 | 38 | } 39 | 40 | public String getName() { 41 | return name; 42 | } 43 | 44 | public void setName(String name) { 45 | this.name = name; 46 | } 47 | 48 | public String getLastName() { 49 | return lastName; 50 | } 51 | 52 | public void setLastName(String lastName) { 53 | this.lastName = lastName; 54 | } 55 | 56 | public ClientType getClientType() { 57 | return clientType; 58 | } 59 | 60 | public void setClientType(ClientType clientType) { 61 | this.clientType = clientType; 62 | } 63 | 64 | public Type getType() { 65 | return type; 66 | } 67 | 68 | public void setType(Type type) { 69 | this.type = type; 70 | } 71 | 72 | public PaymentType getDefaultPaymentType() { 73 | return defaultPaymentType; 74 | } 75 | 76 | public void setDefaultPaymentType(PaymentType defaultPaymentType) { 77 | this.defaultPaymentType = defaultPaymentType; 78 | } 79 | 80 | @Override 81 | public boolean equals(Object o) { 82 | if (this == o) return true; 83 | 84 | if (!(o instanceof Client)) 85 | return false; 86 | 87 | Client other = (Client) o; 88 | 89 | return this.getId() != null && 90 | this.getId().equals(other.getId()); 91 | } 92 | } -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/crm/ClientController.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.crm; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.http.ResponseEntity; 5 | import org.springframework.web.bind.annotation.*; 6 | 7 | @RestController 8 | public class ClientController { 9 | 10 | @Autowired 11 | ClientService clientService; 12 | 13 | @PostMapping("/clients") 14 | public ResponseEntity register(@RequestBody ClientDTO dto) { 15 | Client c = clientService.registerClient(dto.getName(), dto.getLastName(), dto.getType(), dto.getDefaultPaymentType()); 16 | return ResponseEntity.ok(clientService.load(c.getId())); 17 | } 18 | 19 | @GetMapping("/clients/{clientId}") 20 | public ResponseEntity find(@PathVariable Long clientId) { 21 | return ResponseEntity.ok(clientService.load(clientId)); 22 | } 23 | 24 | @PostMapping("/clients/{clientId}/upgrade") 25 | public ResponseEntity upgradeToVIP(@PathVariable Long clientId) { 26 | clientService.upgradeToVIP(clientId); 27 | return ResponseEntity.ok(clientService.load(clientId)); 28 | } 29 | 30 | @PostMapping("/clients/{clientId}/downgrade") 31 | public ResponseEntity downgrade(@PathVariable Long clientId) { 32 | clientService.downgradeToRegular(clientId); 33 | return ResponseEntity.ok(clientService.load(clientId)); 34 | } 35 | 36 | @PostMapping("/clients/{clientId}/changeDefaultPaymentType") 37 | public ResponseEntity changeDefaultPaymentType(@PathVariable Long clientId, @RequestBody ClientDTO dto) { 38 | clientService.changeDefaultPaymentType(clientId, dto.getDefaultPaymentType()); 39 | return ResponseEntity.ok(clientService.load(clientId)); 40 | } 41 | 42 | } 43 | 44 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/crm/ClientDTO.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.crm; 2 | 3 | public class ClientDTO { 4 | 5 | private Long id; 6 | 7 | private Client.Type type; 8 | 9 | private String name; 10 | 11 | private String lastName; 12 | 13 | private Client.PaymentType defaultPaymentType; 14 | 15 | private Client.ClientType clientType; 16 | 17 | private Integer numberOfClaims; 18 | 19 | public ClientDTO() { 20 | 21 | } 22 | 23 | public ClientDTO(Client client) { 24 | this.id = client.getId(); 25 | this.type = client.getType(); 26 | this.name = client.getName(); 27 | this.lastName = client.getLastName(); 28 | this.defaultPaymentType = client.getDefaultPaymentType(); 29 | this.clientType = client.getClientType(); 30 | } 31 | 32 | public String getName() { 33 | return name; 34 | } 35 | 36 | public void setName(String name) { 37 | this.name = name; 38 | } 39 | 40 | public String getLastName() { 41 | return lastName; 42 | } 43 | 44 | public void setLastName(String lastName) { 45 | this.lastName = lastName; 46 | } 47 | 48 | public Client.ClientType getClientType() { 49 | return clientType; 50 | } 51 | 52 | public void setClientType(Client.ClientType clientType) { 53 | this.clientType = clientType; 54 | } 55 | 56 | public Client.Type getType() { 57 | return type; 58 | } 59 | 60 | public void setType(Client.Type type) { 61 | this.type = type; 62 | } 63 | 64 | public Client.PaymentType getDefaultPaymentType() { 65 | return defaultPaymentType; 66 | } 67 | 68 | public void setDefaultPaymentType(Client.PaymentType defaultPaymentType) { 69 | this.defaultPaymentType = defaultPaymentType; 70 | } 71 | 72 | public Long getId() { 73 | return id; 74 | } 75 | 76 | public void setId(Long id) { 77 | this.id = id; 78 | } 79 | 80 | } -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/crm/ClientRepository.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.crm; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | 5 | public interface ClientRepository extends JpaRepository { 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/crm/ClientService.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.crm; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.stereotype.Service; 5 | import org.springframework.transaction.annotation.Transactional; 6 | 7 | @Service 8 | public class ClientService { 9 | 10 | @Autowired 11 | private ClientRepository clientRepository; 12 | 13 | @Transactional 14 | public Client registerClient(String name, String lastName, Client.Type type, Client.PaymentType paymentType) { 15 | Client client = new Client(); 16 | client.setName(name); 17 | client.setLastName(lastName); 18 | client.setType(type); 19 | client.setDefaultPaymentType(paymentType); 20 | return clientRepository.save(client); 21 | } 22 | 23 | @Transactional 24 | public void changeDefaultPaymentType(Long clientId, Client.PaymentType paymentType) { 25 | Client client = clientRepository.getOne(clientId); 26 | if (client == null) { 27 | throw new IllegalArgumentException("Client does not exists, id = " + clientId); 28 | } 29 | client.setDefaultPaymentType(paymentType); 30 | clientRepository.save(client); 31 | } 32 | 33 | @Transactional 34 | public void upgradeToVIP(Long clientId) { 35 | Client client = clientRepository.getOne(clientId); 36 | if (client == null) { 37 | throw new IllegalArgumentException("Client does not exists, id = " + clientId); 38 | } 39 | client.setType(Client.Type.VIP); 40 | clientRepository.save(client); 41 | } 42 | 43 | @Transactional 44 | public void downgradeToRegular(Long clientId) { 45 | Client client = clientRepository.getOne(clientId); 46 | if (client == null) { 47 | throw new IllegalArgumentException("Client does not exists, id = " + clientId); 48 | } 49 | client.setType(Client.Type.NORMAL); 50 | clientRepository.save(client); 51 | } 52 | 53 | @Transactional 54 | public ClientDTO load(Long id) { 55 | return new ClientDTO(clientRepository.getOne(id)); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/crm/claims/ClaimAttachment.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.crm.claims; 2 | 3 | import io.legacyfighter.cabs.common.BaseEntity; 4 | 5 | import javax.persistence.Column; 6 | import javax.persistence.Entity; 7 | import javax.persistence.Lob; 8 | import javax.persistence.ManyToOne; 9 | import java.time.Instant; 10 | 11 | @Entity 12 | class ClaimAttachment extends BaseEntity { 13 | 14 | public ClaimAttachment() { 15 | 16 | } 17 | 18 | @ManyToOne 19 | private Claim claim; 20 | 21 | @Column(nullable = false) 22 | private Instant creationDate; 23 | 24 | private String description; 25 | 26 | @Lob 27 | @Column(name = "data", columnDefinition="BLOB") 28 | private byte[] data; 29 | 30 | Long getClientId() { 31 | return claim.getOwnerId(); 32 | } 33 | 34 | Claim getClaim() { 35 | return claim; 36 | } 37 | 38 | void setClaim(Claim claim) { 39 | this.claim = claim; 40 | } 41 | 42 | Instant getCreationDate() { 43 | return creationDate; 44 | } 45 | 46 | void setCreationDate(Instant creationDate) { 47 | this.creationDate = creationDate; 48 | } 49 | 50 | String getDescription() { 51 | return description; 52 | } 53 | 54 | void setDescription(String description) { 55 | this.description = description; 56 | } 57 | 58 | byte[] getData() { 59 | return data; 60 | } 61 | 62 | void setData(byte[] data) { 63 | this.data = data; 64 | } 65 | 66 | @Override 67 | public boolean equals(Object o) { 68 | if (this == o) return true; 69 | 70 | if (!(o instanceof ClaimAttachment)) 71 | return false; 72 | 73 | ClaimAttachment other = (ClaimAttachment) o; 74 | 75 | return this.getId() != null && 76 | this.getId().equals(other.getId()); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/crm/claims/ClaimAttachmentRepository.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.crm.claims; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | 5 | interface ClaimAttachmentRepository extends JpaRepository { 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/crm/claims/ClaimController.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.crm.claims; 2 | 3 | import org.springframework.http.ResponseEntity; 4 | import org.springframework.transaction.annotation.Transactional; 5 | import org.springframework.web.bind.annotation.*; 6 | 7 | @RestController 8 | public class ClaimController { 9 | 10 | private final ClaimService claimService; 11 | 12 | public ClaimController(ClaimService claimService) { 13 | this.claimService = claimService; 14 | } 15 | 16 | @PostMapping("/claims/createDraft") 17 | public ResponseEntity create(@RequestBody ClaimDTO claimDTO) { 18 | Claim created = claimService.create(claimDTO); 19 | return ResponseEntity.ok(toDto(created)); 20 | } 21 | 22 | @PostMapping("/claims/send") 23 | ResponseEntity sendNew(@RequestBody ClaimDTO claimDTO) { 24 | claimDTO.setDraft(false); 25 | Claim claim = claimService.create(claimDTO); 26 | return ResponseEntity.ok(toDto(claim)); 27 | } 28 | 29 | @PostMapping("/claims/{id}/markInProcess") 30 | ResponseEntity markAsInProcess(@PathVariable Long id) { 31 | Claim claim = claimService.setStatus(Status.IN_PROCESS, id); 32 | return ResponseEntity.ok(toDto(claim)); 33 | } 34 | 35 | @GetMapping("/claims/{id}}") 36 | @Transactional 37 | public ResponseEntity find(@PathVariable Long id) { 38 | Claim claim = claimService.find(id); 39 | ClaimDTO dto = toDto(claim); 40 | return ResponseEntity.ok(dto); 41 | } 42 | 43 | @PostMapping("/claims/{id}}") 44 | ResponseEntity tryToAutomaticallyResolve(@PathVariable Long id) { 45 | Claim claim = claimService.tryToResolveAutomatically(id); 46 | return ResponseEntity.ok(toDto(claim)); 47 | } 48 | 49 | private ClaimDTO toDto(Claim claim) { 50 | return new ClaimDTO(claim); 51 | 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/crm/claims/ClaimNumberGenerator.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.crm.claims; 2 | 3 | import org.springframework.stereotype.Service; 4 | 5 | import java.time.ZoneId; 6 | import java.time.format.DateTimeFormatter; 7 | 8 | @Service 9 | class ClaimNumberGenerator { 10 | 11 | private final ClaimRepository claimRepository; 12 | 13 | ClaimNumberGenerator(ClaimRepository claimRepository) { 14 | this.claimRepository = claimRepository; 15 | } 16 | 17 | String generate(Claim claim) { 18 | Long count = claimRepository.count(); 19 | Long prefix = count; 20 | if (count == 0) { 21 | prefix = 1L; 22 | } 23 | DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("dd/MM/yyyy") 24 | .withZone(ZoneId.systemDefault()); 25 | return count + "---" + DATE_TIME_FORMATTER.format(claim.getCreationDate()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/crm/claims/ClaimRepository.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.crm.claims; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | 5 | import java.util.List; 6 | 7 | interface ClaimRepository extends JpaRepository { 8 | 9 | List findAllByOwnerId(Long ownerId); 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/crm/claims/ClaimsResolverRepository.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.crm.claims; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | 5 | interface ClaimsResolverRepository extends JpaRepository { 6 | 7 | ClaimsResolver findByClientId(Long clientId); 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/crm/claims/Status.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.crm.claims; 2 | 3 | public enum Status { 4 | DRAFT, NEW, IN_PROCESS, REFUNDED, ESCALATED, REJECTED 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/crm/transitanalyzer/AnalyzedAddressesDTO.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.crm.transitanalyzer; 2 | 3 | import io.legacyfighter.cabs.geolocation.address.AddressDTO; 4 | 5 | import java.util.List; 6 | 7 | public class AnalyzedAddressesDTO { 8 | private List addresses; 9 | 10 | public AnalyzedAddressesDTO() { 11 | } 12 | 13 | public AnalyzedAddressesDTO(List addresses) { 14 | this.addresses = addresses; 15 | } 16 | 17 | public List getAddresses() { 18 | return addresses; 19 | } 20 | 21 | public void setAddresses(List addresses) { 22 | this.addresses = addresses; 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/crm/transitanalyzer/Neo4jConfig.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.crm.transitanalyzer; 2 | 3 | import io.legacyfighter.cabs.ride.details.TransitDetailsFacade; 4 | import org.neo4j.graphdb.GraphDatabaseService; 5 | import org.springframework.beans.factory.annotation.Value; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | 9 | 10 | @Configuration 11 | class Neo4jConfig { 12 | 13 | @Value("${neo4j.db.file:testPath}") 14 | private String dbPath; 15 | 16 | @Bean(destroyMethod = "onClose") 17 | GraphTransitAnalyzer graphTransitAnalyzer() { 18 | return new GraphTransitAnalyzer(notConnectedOnProdYet(dbPath)); 19 | } 20 | 21 | @Bean 22 | GraphDatabaseService notConnectedOnProdYet(String dbPath) { 23 | return null; 24 | } 25 | 26 | @Bean 27 | PopulateGraphService populateGraphService(GraphTransitAnalyzer graphTransitAnalyzer, TransitDetailsFacade transitDetailsFacade) { 28 | return new PopulateGraphService(graphTransitAnalyzer, transitDetailsFacade); 29 | } 30 | } 31 | 32 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/crm/transitanalyzer/PopulateGraphService.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.crm.transitanalyzer; 2 | 3 | 4 | import io.legacyfighter.cabs.ride.details.TransitDetailsDTO; 5 | import io.legacyfighter.cabs.ride.details.TransitDetailsFacade; 6 | import org.springframework.transaction.annotation.Transactional; 7 | 8 | public class PopulateGraphService { 9 | 10 | private final GraphTransitAnalyzer graphTransitAnalyzer; 11 | private final TransitDetailsFacade transitDetailsFacade; 12 | 13 | public PopulateGraphService(GraphTransitAnalyzer graphTransitAnalyzer, TransitDetailsFacade transitDetailsFacade) { 14 | this.graphTransitAnalyzer = graphTransitAnalyzer; 15 | this.transitDetailsFacade = transitDetailsFacade; 16 | } 17 | 18 | @Transactional 19 | public void populate() { 20 | transitDetailsFacade 21 | .findCompleted() 22 | .forEach(this::addToGraph); 23 | } 24 | 25 | private void addToGraph(TransitDetailsDTO transitDetails) { 26 | Long clientId = transitDetails.client.getId(); 27 | graphTransitAnalyzer.addTransitBetweenAddresses( 28 | clientId, 29 | transitDetails.transitId, 30 | transitDetails.from.getHash(), 31 | transitDetails.to.getHash(), 32 | transitDetails.started, 33 | transitDetails.completedAt); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/crm/transitanalyzer/TransitAnalyzerController.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.crm.transitanalyzer; 2 | 3 | import io.legacyfighter.cabs.geolocation.address.AddressDTO; 4 | import io.legacyfighter.cabs.geolocation.address.AddressRepository; 5 | import org.springframework.web.bind.annotation.GetMapping; 6 | import org.springframework.web.bind.annotation.PathVariable; 7 | import org.springframework.web.bind.annotation.RestController; 8 | 9 | import java.util.List; 10 | import java.util.stream.Collectors; 11 | 12 | @RestController 13 | public class TransitAnalyzerController { 14 | 15 | private final GraphTransitAnalyzer graphTransitAnalyzer; 16 | private final AddressRepository addressRepository; 17 | 18 | public TransitAnalyzerController(GraphTransitAnalyzer graphTransitAnalyzer, AddressRepository addressRepository) { 19 | this.graphTransitAnalyzer = graphTransitAnalyzer; 20 | this.addressRepository = addressRepository; 21 | } 22 | 23 | @GetMapping("/transitAnalyze/{clientId}/{addressId}") 24 | public AnalyzedAddressesDTO analyze(@PathVariable Long clientId, @PathVariable Long addressId) { 25 | List hashes = graphTransitAnalyzer.analyze(clientId, addressRepository.findHashById(addressId)); 26 | List addressDTOs = hashes 27 | .stream() 28 | .map(this::mapToAddressDTO) 29 | .collect(Collectors.toList()); 30 | 31 | return new AnalyzedAddressesDTO(addressDTOs); 32 | } 33 | 34 | private AddressDTO mapToAddressDTO(Long hash) { 35 | return new AddressDTO(addressRepository.getByHash(hash.intValue())); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/driverfleet/DriverAttribute.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.driverfleet; 2 | 3 | import javax.persistence.*; 4 | 5 | @Entity 6 | public 7 | class DriverAttribute { 8 | 9 | 10 | @Id 11 | @GeneratedValue 12 | private long id; 13 | 14 | @Enumerated(EnumType.STRING) 15 | @Column(nullable = false) 16 | private DriverAttributeName name; 17 | 18 | @Column(nullable = false) 19 | private String value; 20 | 21 | @ManyToOne 22 | @JoinColumn(name = "DRIVER_ID") 23 | private Driver driver; 24 | 25 | DriverAttribute() { 26 | 27 | } 28 | 29 | public DriverAttribute(Driver driver, DriverAttributeName attr, String value) { 30 | this.driver = driver; 31 | this.value = value; 32 | this.name = attr; 33 | } 34 | 35 | DriverAttributeName getName() { 36 | return name; 37 | } 38 | 39 | void setName(DriverAttributeName name) { 40 | this.name = name; 41 | } 42 | 43 | String getValue() { 44 | return value; 45 | } 46 | 47 | void setValue(String value) { 48 | this.value = value; 49 | } 50 | 51 | Driver getDriver() { 52 | return driver; 53 | } 54 | 55 | void setDriver(Driver driver) { 56 | this.driver = driver; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/driverfleet/DriverAttributeDTO.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.driverfleet; 2 | 3 | import java.util.Objects; 4 | 5 | public class DriverAttributeDTO { 6 | 7 | private DriverAttributeName name; 8 | 9 | private String value; 10 | 11 | public DriverAttributeDTO(DriverAttribute driverAttribute) { 12 | this.name = driverAttribute.getName(); 13 | this.value = driverAttribute.getValue(); 14 | } 15 | 16 | public DriverAttributeDTO(DriverAttributeName name, String value) { 17 | this.name = name; 18 | this.value = value; 19 | } 20 | 21 | DriverAttributeDTO() { 22 | 23 | } 24 | 25 | public DriverAttributeName getName() { 26 | return name; 27 | } 28 | 29 | public void setName(DriverAttributeName name) { 30 | this.name = name; 31 | } 32 | 33 | public String getValue() { 34 | return value; 35 | } 36 | 37 | public void setValue(String value) { 38 | this.value = value; 39 | } 40 | 41 | @Override 42 | public boolean equals(Object o) { 43 | if (this == o) return true; 44 | if (o == null || getClass() != o.getClass()) return false; 45 | final DriverAttributeDTO that = (DriverAttributeDTO) o; 46 | return name == that.name && Objects.equals(value, that.value); 47 | } 48 | 49 | @Override 50 | public int hashCode() { 51 | return Objects.hash(name, value); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/driverfleet/DriverAttributeName.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.driverfleet; 2 | 3 | public enum DriverAttributeName { 4 | PENALTY_POINTS, NATIONALITY, YEARS_OF_EXPERIENCE, MEDICAL_EXAMINATION_EXPIRATION_DATE, MEDICAL_EXAMINATION_REMARKS, EMAIL, BIRTHPLACE, COMPANY_NAME 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/driverfleet/DriverAttributeRepository.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.driverfleet; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | 5 | public interface DriverAttributeRepository extends JpaRepository { 6 | 7 | 8 | } 9 | 10 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/driverfleet/DriverController.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.driverfleet; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.web.bind.annotation.*; 5 | 6 | @RestController 7 | public class DriverController { 8 | 9 | @Autowired 10 | private DriverService driverService; 11 | 12 | @Autowired 13 | private DriverRepository driverRepository; 14 | 15 | @PostMapping("/drivers") 16 | DriverDTO createDriver(@RequestParam String license, @RequestParam String firstName, @RequestParam String lastName, @RequestParam String photo) { 17 | Driver driver = driverService.createDriver(license, lastName, firstName, Driver.Type.CANDIDATE, Driver.Status.INACTIVE, photo); 18 | 19 | return driverService.loadDriver(driver.getId()); 20 | } 21 | 22 | @GetMapping("/drivers/{id}") 23 | DriverDTO getDriver(@PathVariable Long id) { 24 | return driverService.loadDriver(id); 25 | } 26 | 27 | @PostMapping("/drivers/{id}") 28 | DriverDTO updateDriver(@PathVariable Long id) { 29 | 30 | return driverService.loadDriver(id); 31 | } 32 | 33 | @PostMapping("/drivers/{id}/deactivate") 34 | DriverDTO deactivateDriver(@PathVariable Long id) { 35 | driverService.changeDriverStatus(id, Driver.Status.INACTIVE); 36 | 37 | return driverService.loadDriver(id); 38 | } 39 | 40 | @PostMapping("/drivers/{id}/activate") 41 | DriverDTO activateDriver(@PathVariable Long id) { 42 | driverService.changeDriverStatus(id, Driver.Status.ACTIVE); 43 | 44 | return driverService.loadDriver(id); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/driverfleet/DriverFee.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.driverfleet; 2 | 3 | import io.legacyfighter.cabs.common.BaseEntity; 4 | import io.legacyfighter.cabs.money.Money; 5 | 6 | import javax.persistence.*; 7 | 8 | @Entity 9 | public class DriverFee extends BaseEntity { 10 | 11 | public enum FeeType { 12 | FLAT, PERCENTAGE 13 | } 14 | 15 | public DriverFee() { 16 | 17 | } 18 | 19 | public DriverFee(FeeType feeType, Driver driver, Integer amount, Integer min) { 20 | this.feeType = feeType; 21 | this.driver = driver; 22 | this.amount = amount; 23 | this.min = new Money(min); 24 | } 25 | 26 | @Column(nullable = false) 27 | private FeeType feeType; 28 | 29 | @OneToOne 30 | private Driver driver; 31 | 32 | @Column(nullable = false) 33 | private Integer amount; 34 | 35 | @Embedded 36 | @AttributeOverrides({ 37 | @AttributeOverride(name="value", column=@Column(name="min")), 38 | }) 39 | private Money min; 40 | 41 | public FeeType getFeeType() { 42 | return feeType; 43 | } 44 | 45 | public void setFeeType(FeeType feeType) { 46 | this.feeType = feeType; 47 | } 48 | 49 | Driver getDriver() { 50 | return driver; 51 | } 52 | 53 | public void setDriver(Driver driver) { 54 | this.driver = driver; 55 | } 56 | 57 | public Integer getAmount() { 58 | return amount; 59 | } 60 | 61 | public void setAmount(Integer amount) { 62 | this.amount = amount; 63 | } 64 | 65 | public Money getMin() { 66 | return min; 67 | } 68 | 69 | public void setMin(Money min) { 70 | this.min = min; 71 | } 72 | 73 | @Override 74 | public boolean equals(Object o) { 75 | if (this == o) return true; 76 | 77 | if (!(o instanceof DriverFee)) 78 | return false; 79 | 80 | DriverFee other = (DriverFee) o; 81 | 82 | return this.getId() != null && 83 | this.getId().equals(other.getId()); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/driverfleet/DriverFeeRepository.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.driverfleet; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | 5 | public interface DriverFeeRepository extends JpaRepository { 6 | 7 | DriverFee findByDriverId(Long driverId); 8 | 9 | } 10 | 11 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/driverfleet/DriverFeeService.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.driverfleet; 2 | 3 | import io.legacyfighter.cabs.money.Money; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.stereotype.Service; 6 | import org.springframework.transaction.annotation.Transactional; 7 | 8 | @Service 9 | public class DriverFeeService { 10 | 11 | @Autowired 12 | private DriverFeeRepository driverFeeRepository; 13 | 14 | @Transactional 15 | public Money calculateDriverFee(Money transitPrice, Long driverId) { 16 | DriverFee driverFee = driverFeeRepository.findByDriverId(driverId); 17 | if (driverFee == null) { 18 | throw new IllegalArgumentException("driver Fees not defined for driver, driver id = " + driverId); 19 | } 20 | Money finalFee; 21 | if (driverFee.getFeeType().equals(DriverFee.FeeType.FLAT)) { 22 | finalFee = transitPrice.subtract(new Money(driverFee.getAmount())); 23 | } else { 24 | finalFee = transitPrice.percentage(driverFee.getAmount()); 25 | } 26 | 27 | return new Money(Math.max(finalFee.toInt(), driverFee.getMin() == null ? 0 : driverFee.getMin().toInt())); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/driverfleet/DriverLicense.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.driverfleet; 2 | 3 | import javax.persistence.Embeddable; 4 | 5 | @Embeddable 6 | class DriverLicense { 7 | 8 | static final String DRIVER_LICENSE_REGEX = "^[A-Z9]{5}\\d{6}[A-Z9]{2}\\d[A-Z]{2}$"; 9 | 10 | private String driverLicense; 11 | 12 | DriverLicense() { 13 | } 14 | 15 | private DriverLicense(String driverLicense) { 16 | this.driverLicense = driverLicense; 17 | } 18 | 19 | static DriverLicense withLicense(String driverLicense) { 20 | if (driverLicense == null || driverLicense.isEmpty() || !driverLicense.matches(DRIVER_LICENSE_REGEX)) { 21 | throw new IllegalArgumentException("Illegal license no = " + driverLicense); 22 | } 23 | return new DriverLicense(driverLicense); 24 | } 25 | 26 | static DriverLicense withoutValidation(String driverLicense) { 27 | return new DriverLicense(driverLicense); 28 | } 29 | 30 | @Override 31 | public String toString() { 32 | return "DriverLicense{" + 33 | "driverLicense='" + driverLicense + '\'' + 34 | '}'; 35 | } 36 | 37 | String asString() { 38 | return driverLicense; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/driverfleet/DriverRepository.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.driverfleet; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | 5 | public interface DriverRepository extends JpaRepository { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/driverfleet/driverreport/DriverReport.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.driverfleet.driverreport; 2 | 3 | 4 | import io.legacyfighter.cabs.driverfleet.DriverAttributeDTO; 5 | import io.legacyfighter.cabs.driverfleet.DriverDTO; 6 | import io.legacyfighter.cabs.tracking.DriverSessionDTO; 7 | import io.legacyfighter.cabs.ride.TransitDTO; 8 | import io.legacyfighter.cabs.driverfleet.DriverAttributeName; 9 | 10 | import java.util.ArrayList; 11 | import java.util.HashMap; 12 | import java.util.List; 13 | import java.util.Map; 14 | 15 | public class DriverReport { 16 | 17 | private DriverDTO driverDTO; 18 | 19 | private List attributes = new ArrayList<>(); 20 | 21 | private Map> sessions = new HashMap<>(); 22 | 23 | public DriverDTO getDriverDTO() { 24 | return driverDTO; 25 | } 26 | 27 | public void setDriverDTO(DriverDTO driverDTO) { 28 | this.driverDTO = driverDTO; 29 | } 30 | 31 | public List getAttributes() { 32 | return attributes; 33 | } 34 | 35 | public void setAttributes(List attributes) { 36 | this.attributes = attributes; 37 | } 38 | 39 | public Map> getSessions() { 40 | return sessions; 41 | } 42 | 43 | public void setSessions(Map> sessions) { 44 | this.sessions = sessions; 45 | } 46 | 47 | public void addAttr(DriverAttributeName name, String value) { 48 | attributes.add(new DriverAttributeDTO(name, value)); 49 | } 50 | } 51 | 52 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/driverfleet/driverreport/DriverReportController.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.driverfleet.driverreport; 2 | 3 | import org.springframework.transaction.annotation.Transactional; 4 | import org.springframework.web.bind.annotation.GetMapping; 5 | import org.springframework.web.bind.annotation.PathVariable; 6 | import org.springframework.web.bind.annotation.RequestParam; 7 | import org.springframework.web.bind.annotation.RestController; 8 | 9 | 10 | @RestController 11 | public class DriverReportController { 12 | 13 | private final SqlBasedDriverReportCreator driverReportCreator; 14 | 15 | DriverReportController(SqlBasedDriverReportCreator driverReportCreator) { 16 | this.driverReportCreator = driverReportCreator; 17 | } 18 | 19 | @GetMapping("/driverreport/{driverId}") 20 | @Transactional 21 | public DriverReport loadReportForDriver(@PathVariable Long driverId, @RequestParam int lastDays) { 22 | return driverReportCreator.createReport(driverId, lastDays); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/driverfleet/driverreport/travelleddistance/TravelledDistanceRepository.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.driverfleet.driverreport.travelleddistance; 2 | 3 | import org.springframework.data.jpa.repository.Query; 4 | import org.springframework.data.repository.CrudRepository; 5 | 6 | import java.time.Instant; 7 | import java.util.UUID; 8 | 9 | interface TravelledDistanceRepository extends CrudRepository { 10 | 11 | @Query("select td from TravelledDistance td where td.timeSlot.beginning <= :when and :when < td.timeSlot.end and td.driverId = :driverId") 12 | TravelledDistance findTravelledDistanceTimeSlotByTime(Instant when, Long driverId); 13 | 14 | TravelledDistance findTravelledDistanceByTimeSlotAndDriverId(TimeSlot timeSlot, Long driverId); 15 | 16 | @Query(value = "SELECT COALESCE(SUM(_inner.km), 0) FROM " + 17 | "( (SELECT * FROM travelled_distance td WHERE td.beginning >= :beginning AND td.driver_id = :driverId)) " + 18 | "AS _inner WHERE end <= :to ", nativeQuery = true) 19 | double calculateDistance(Instant beginning, Instant to, Long driverId); 20 | 21 | } 22 | 23 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/geolocation/Distance.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.geolocation; 2 | 3 | import javax.persistence.Embeddable; 4 | import java.util.Locale; 5 | import java.util.Objects; 6 | 7 | @Embeddable 8 | public final class Distance { 9 | 10 | public static final Distance ZERO = ofKm(0); 11 | 12 | private static final double MILES_TO_KILOMETERS_RATIO = 1.609344f; 13 | 14 | private double km; 15 | 16 | public static Distance ofKm(float km) { 17 | return new Distance(km); 18 | } 19 | 20 | public static Distance ofKm(double km) { 21 | return new Distance(km); 22 | } 23 | 24 | private Distance(double km) { 25 | this.km = km; 26 | } 27 | 28 | public float toKmInFloat() { 29 | return (float) km; 30 | } 31 | 32 | private Distance() { 33 | 34 | } 35 | 36 | public String printIn(String unit) { 37 | if (unit.equals("km")) { 38 | if (km == Math.ceil(km)) { 39 | return String.format(Locale.US, "%d", Math.round(km)) + "km"; 40 | 41 | } 42 | return String.format(Locale.US, "%.3f", km) + "km"; 43 | } 44 | if (unit.equals("miles")) { 45 | double km = this.km / MILES_TO_KILOMETERS_RATIO; 46 | if (km == Math.ceil(km)) { 47 | return String.format(Locale.US, "%d", Math.round(km)) + "miles"; 48 | } 49 | return String.format(Locale.US, "%.3f", km) + "miles"; 50 | 51 | } 52 | if (unit.equals("m")) { 53 | return String.format(Locale.US, "%d", Math.round(km * 1000)) + "m"; 54 | } 55 | throw new IllegalArgumentException("Invalid unit " + unit); 56 | } 57 | 58 | @Override 59 | public boolean equals(Object o) { 60 | if (o == null || getClass() != o.getClass()) return false; 61 | Distance that = (Distance) o; 62 | return Objects.equals(km, that.km); 63 | } 64 | 65 | @Override 66 | public int hashCode() { 67 | return Objects.hash(km); 68 | } 69 | 70 | public double toKmInDouble() { 71 | return km; 72 | } 73 | 74 | @Override 75 | public String toString() { 76 | return "Distance{" + 77 | "km=" + km + 78 | '}'; 79 | } 80 | 81 | public Distance add(Distance travelled) { 82 | return Distance.ofKm(this.km + travelled.km); 83 | } 84 | } 85 | 86 | 87 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/geolocation/DistanceCalculator.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.geolocation; 2 | 3 | import org.springframework.stereotype.Service; 4 | 5 | @Service 6 | public class DistanceCalculator { 7 | public double calculateByMap(double latitudeFrom, double longitudeFrom, double latitudeTo, double longitudeTo) { 8 | // ... 9 | 10 | return 42; 11 | } 12 | 13 | public double calculateByGeo(double latitudeFrom, double longitudeFrom, double latitudeTo, double longitudeTo) { 14 | // https://www.geeksforgeeks.org/program-distance-two-points-earth/ 15 | // The math module contains a function 16 | // named toRadians which converts from 17 | // degrees to radians. 18 | double lon1 = Math.toRadians(longitudeFrom); 19 | double lon2 = Math.toRadians(longitudeTo); 20 | double lat1 = Math.toRadians(latitudeFrom); 21 | double lat2 = Math.toRadians(latitudeTo); 22 | 23 | // Haversine formula 24 | double dlon = lon2 - lon1; 25 | double dlat = lat2 - lat1; 26 | double a = Math.pow(Math.sin(dlat / 2), 2) 27 | + Math.cos(lat1) * Math.cos(lat2) 28 | * Math.pow(Math.sin(dlon / 2),2); 29 | 30 | double c = 2 * Math.asin(Math.sqrt(a)); 31 | 32 | // Radius of earth in kilometers. Use 3956 for miles 33 | double r = 6371; 34 | 35 | // calculate the result 36 | double distanceInKMeters = c * r; 37 | 38 | return distanceInKMeters; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/geolocation/GeocodingService.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.geolocation; 2 | 3 | import io.legacyfighter.cabs.geolocation.address.Address; 4 | import org.springframework.stereotype.Service; 5 | 6 | @Service 7 | public class GeocodingService { 8 | public double[] geocodeAddress(Address address) { 9 | //TODO ... call do zewnętrznego serwisu 10 | 11 | double geocoded[] = new double[2]; 12 | 13 | geocoded[0] = 1f; //latitude 14 | geocoded[1] = 1f; //longitude 15 | 16 | return geocoded; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/geolocation/address/AddressRepository.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.geolocation.address; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.stereotype.Repository; 5 | import org.springframework.transaction.annotation.Transactional; 6 | 7 | @Repository 8 | public class AddressRepository { 9 | @Autowired 10 | AddressRepositoryInterface addressRepositoryInterface; 11 | 12 | // FIX ME: To replace with getOrCreate method instead of that? 13 | // Actual workaround for address uniqueness problem: assign result from repo.save to variable for later usage 14 | public Address save(Address address) { 15 | address.hash(); 16 | 17 | if (address.getId() == null) { 18 | Address existingAddress = addressRepositoryInterface.findByHash(address.getHash()); 19 | 20 | if (existingAddress != null) { 21 | return existingAddress; 22 | } 23 | } 24 | 25 | return addressRepositoryInterface.save(address); 26 | } 27 | 28 | public Address getOne(Long id) { 29 | return addressRepositoryInterface.getOne(id); 30 | } 31 | 32 | @Transactional 33 | public Integer findHashById(Long addressId) { 34 | return addressRepositoryInterface.findHashById(addressId); 35 | } 36 | 37 | public Address getByHash(int hash) { 38 | return addressRepositoryInterface.findByHash(hash); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/geolocation/address/AddressRepositoryInterface.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.geolocation.address; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | 5 | interface AddressRepositoryInterface extends JpaRepository { 6 | Address findByHash(Integer hash); 7 | 8 | default Integer findHashById(Long id) { 9 | return getOne(id).getHash(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/invocing/Invoice.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.invocing; 2 | 3 | import io.legacyfighter.cabs.common.BaseEntity; 4 | 5 | import javax.persistence.Entity; 6 | import java.math.BigDecimal; 7 | 8 | @Entity 9 | class Invoice extends BaseEntity { 10 | 11 | private BigDecimal amount; 12 | 13 | private String subjectName; 14 | 15 | Invoice(BigDecimal amount, String subjectName) { 16 | this.amount = amount; 17 | this.subjectName = subjectName; 18 | } 19 | 20 | Invoice() { 21 | 22 | } 23 | 24 | @Override 25 | public boolean equals(Object o) { 26 | if (this == o) return true; 27 | 28 | if (!(o instanceof Invoice)) 29 | return false; 30 | 31 | Invoice other = (Invoice) o; 32 | 33 | return this.getId() != null && 34 | this.getId().equals(other.getId()); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/invocing/InvoiceGenerator.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.invocing; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.stereotype.Service; 5 | 6 | import java.math.BigDecimal; 7 | 8 | @Service 9 | public class InvoiceGenerator { 10 | 11 | @Autowired 12 | InvoiceRepository invoiceRepository; 13 | 14 | public Invoice generate(Integer amount, String subjectName) { 15 | return invoiceRepository.save(new Invoice(new BigDecimal(amount), subjectName)); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/invocing/InvoiceRepository.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.invocing; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | 5 | interface InvoiceRepository extends JpaRepository { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/loyalty/AwardsAccountController.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.loyalty; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.http.ResponseEntity; 5 | import org.springframework.web.bind.annotation.GetMapping; 6 | import org.springframework.web.bind.annotation.PathVariable; 7 | import org.springframework.web.bind.annotation.PostMapping; 8 | import org.springframework.web.bind.annotation.RestController; 9 | 10 | @RestController 11 | public class AwardsAccountController { 12 | 13 | @Autowired 14 | private AwardsService awardsService; 15 | 16 | @PostMapping("/clients/{clientId}/awards") 17 | ResponseEntity register(@PathVariable Long clientId) { 18 | awardsService.registerToProgram(clientId); 19 | return ResponseEntity.ok(awardsService.findBy(clientId)); 20 | } 21 | 22 | @PostMapping("/clients/{clientId}/awards/activate") 23 | ResponseEntity activate(@PathVariable Long clientId) { 24 | awardsService.activateAccount(clientId); 25 | return ResponseEntity.ok(awardsService.findBy(clientId)); 26 | } 27 | 28 | @PostMapping("/clients/{clientId}/awards/deactivate") 29 | ResponseEntity deactivate(@PathVariable Long clientId) { 30 | awardsService.deactivateAccount(clientId); 31 | return ResponseEntity.ok(awardsService.findBy(clientId)); 32 | } 33 | 34 | @GetMapping("/clients/{clientId}/awards/balance") 35 | ResponseEntity calculateBalance(@PathVariable Long clientId) { 36 | return ResponseEntity.ok(awardsService.calculateBalance(clientId)); 37 | } 38 | 39 | @PostMapping("/clients/{clientId}/awards/transfer/{toClientId}/{howMuch}") 40 | ResponseEntity transferMiles(@PathVariable Long clientId, @PathVariable Long toClientId, @PathVariable Integer howMuch) { 41 | awardsService.transferMiles(clientId, toClientId, howMuch); 42 | return ResponseEntity.ok(awardsService.findBy(clientId)); 43 | } 44 | 45 | @GetMapping("/clients/{clientId}/awards/") 46 | ResponseEntity findBy(@PathVariable Long clientId) { 47 | return ResponseEntity.ok(awardsService.findBy(clientId)); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/loyalty/AwardsAccountDTO.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.loyalty; 2 | 3 | import io.legacyfighter.cabs.crm.ClientDTO; 4 | 5 | import java.time.Instant; 6 | 7 | public class AwardsAccountDTO { 8 | 9 | 10 | private ClientDTO client; 11 | 12 | private Instant date; 13 | 14 | private Boolean isActive; 15 | 16 | private Integer transactions; 17 | 18 | public AwardsAccountDTO() { 19 | } 20 | 21 | public AwardsAccountDTO(AwardsAccount account, ClientDTO clientDTO) { 22 | this.isActive = account.isActive(); 23 | this.client = clientDTO; 24 | this.transactions = account.getTransactions(); 25 | this.date = account.getDate(); 26 | } 27 | 28 | public void setClient(ClientDTO client) { 29 | this.client = client; 30 | } 31 | 32 | public ClientDTO getClient() { 33 | return client; 34 | } 35 | 36 | public void setDate(Instant date) { 37 | this.date = date; 38 | } 39 | 40 | public void setActive(Boolean active) { 41 | isActive = active; 42 | } 43 | 44 | public Boolean isActive() { 45 | return isActive; 46 | } 47 | 48 | public Integer getTransactions() { 49 | return transactions; 50 | } 51 | 52 | public void setTransactions(int transactions) { 53 | this. transactions = transactions; 54 | } 55 | 56 | public Instant getDate() { 57 | return date; 58 | } 59 | 60 | public Boolean getActive() { 61 | return isActive; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/loyalty/AwardsAccountRepository.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.loyalty; 2 | 3 | import io.legacyfighter.cabs.crm.Client; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | import java.util.List; 7 | 8 | public interface AwardsAccountRepository extends JpaRepository { 9 | 10 | AwardsAccount findByClientId(Long clientId); 11 | 12 | default List findAllMilesBy(Client client) { 13 | return findByClientId(client.getId()).getMiles(); 14 | } 15 | 16 | } -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/loyalty/AwardsService.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.loyalty; 2 | 3 | public interface AwardsService { 4 | 5 | AwardsAccountDTO findBy(Long clientId); 6 | 7 | void registerToProgram(Long clientId); 8 | 9 | void activateAccount(Long clientId); 10 | 11 | void deactivateAccount(Long clientId); 12 | 13 | AwardedMiles registerMiles(Long clientId, Long transitId); 14 | 15 | AwardedMiles registerNonExpiringMiles(Long clientId, Integer miles); 16 | 17 | void removeMiles(Long clientId, Integer miles); 18 | 19 | Integer calculateBalance(Long clientId); 20 | 21 | void transferMiles(Long fromClientId, Long toClientId, Integer miles); 22 | } -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/loyalty/ConstantUntil.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.loyalty; 2 | 3 | import javax.persistence.Embeddable; 4 | import java.time.Instant; 5 | import java.util.Objects; 6 | 7 | @Embeddable 8 | class ConstantUntil implements Miles { 9 | 10 | public static ConstantUntil constantUntilForever(int amount) { 11 | return new ConstantUntil(amount, Instant.MAX); 12 | } 13 | 14 | public static ConstantUntil constantUntil(int amount, Instant when) { 15 | return new ConstantUntil(amount, when); 16 | } 17 | 18 | private Integer amount; 19 | 20 | private Instant whenExpires; 21 | 22 | public ConstantUntil() { 23 | } 24 | 25 | public ConstantUntil(Integer amount, Instant whenExpires) { 26 | this.amount = amount; 27 | this.whenExpires = whenExpires; 28 | } 29 | 30 | @Override 31 | public Integer getAmountFor(Instant moment) { 32 | return !whenExpires.isBefore(moment) ? amount : 0; 33 | } 34 | 35 | @Override 36 | public ConstantUntil subtract(Integer amount, Instant moment) { 37 | if (getAmountFor(moment) < amount) { 38 | throw new IllegalArgumentException("Insufficient amount of miles"); 39 | } 40 | return new ConstantUntil(this.amount - amount, whenExpires); 41 | } 42 | 43 | @Override 44 | public Instant expiresAt() { 45 | return whenExpires; 46 | } 47 | 48 | @Override 49 | public boolean equals(Object o) { 50 | if (this == o) return true; 51 | if (o == null || getClass() != o.getClass()) return false; 52 | final ConstantUntil that = (ConstantUntil) o; 53 | return Objects.equals(amount, that.amount) && Objects.equals(whenExpires, that.whenExpires); 54 | } 55 | 56 | @Override 57 | public int hashCode() { 58 | return Objects.hash(amount, whenExpires); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/loyalty/Miles.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.loyalty; 2 | 3 | import com.fasterxml.jackson.annotation.JsonSubTypes; 4 | import com.fasterxml.jackson.annotation.JsonTypeInfo; 5 | 6 | import java.time.Instant; 7 | 8 | import static com.fasterxml.jackson.annotation.JsonTypeInfo.As.PROPERTY; 9 | import static com.fasterxml.jackson.annotation.JsonTypeInfo.Id.NAME; 10 | 11 | 12 | @JsonTypeInfo(use = NAME, include = PROPERTY) 13 | @JsonSubTypes({ 14 | @JsonSubTypes.Type(value = ConstantUntil.class, name = "Expiring"), 15 | @JsonSubTypes.Type(value = TwoStepExpiringMiles.class, name = "TwoStep") 16 | }) 17 | interface Miles { 18 | Integer getAmountFor(Instant moment); 19 | 20 | Miles subtract(Integer amount, Instant moment); 21 | 22 | Instant expiresAt(); 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/loyalty/MilesJsonMapper.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.loyalty; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; 6 | 7 | import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.ANY; 8 | import static com.fasterxml.jackson.annotation.PropertyAccessor.FIELD; 9 | 10 | class MilesJsonMapper { 11 | 12 | private static ObjectMapper objectMapper; 13 | 14 | static { 15 | objectMapper = new ObjectMapper(); 16 | objectMapper.setVisibility(FIELD, ANY); 17 | objectMapper.registerModule(new JavaTimeModule()); 18 | } 19 | 20 | 21 | static Miles deserialize(String json) { 22 | try { 23 | return objectMapper.readValue(json, Miles.class); 24 | } catch (JsonProcessingException e) { 25 | throw new RuntimeException(e); 26 | } 27 | } 28 | 29 | static String serialize(Miles miles) { 30 | try { 31 | return objectMapper.writeValueAsString(miles); 32 | } catch (JsonProcessingException e) { 33 | throw new RuntimeException(e); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/money/Money.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.money; 2 | 3 | import javax.persistence.Embeddable; 4 | import java.util.Locale; 5 | import java.util.Objects; 6 | 7 | @Embeddable 8 | public class Money { 9 | 10 | public static final Money ZERO = new Money(0); 11 | private Integer value; 12 | 13 | public Money() { 14 | } 15 | 16 | public Money(Integer value) { 17 | this.value = value; 18 | } 19 | 20 | public Money add(Money other) { 21 | return new Money(value + other.value); 22 | } 23 | 24 | public Money subtract(Money other) { 25 | return new Money(value - other.value); 26 | } 27 | 28 | public Money percentage(int percentage) { 29 | return new Money((int) Math.round(percentage * value/100.0)); 30 | } 31 | 32 | public Money percentage(Double percentage) { 33 | return new Money((int) Math.round(percentage * value/100)); 34 | } 35 | 36 | public Integer toInt() { 37 | return value; 38 | } 39 | 40 | @Override 41 | public boolean equals(Object o) { 42 | if (this == o) return true; 43 | if (o == null || getClass() != o.getClass()) return false; 44 | Money money = (Money) o; 45 | return value.equals(money.value); 46 | } 47 | 48 | @Override 49 | public int hashCode() { 50 | return Objects.hash(value); 51 | } 52 | 53 | @Override 54 | public String toString() { 55 | double value = Double.valueOf(this.value) / 100; 56 | return String.format(Locale.US, "%.2f", value); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/notification/ClientNotificationService.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.notification; 2 | 3 | import org.springframework.stereotype.Service; 4 | 5 | @Service 6 | public class ClientNotificationService { 7 | 8 | public void notifyClientAboutRefund(String claimNo, Long clientId) { 9 | 10 | } 11 | 12 | public void askForMoreInformation(String claimNo, Long clientId) { 13 | 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/notification/DriverNotificationService.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.notification; 2 | 3 | import org.springframework.stereotype.Service; 4 | 5 | import java.util.UUID; 6 | 7 | @Service 8 | public class DriverNotificationService { 9 | 10 | public void notifyAboutPossibleTransit(Long driverId, Long transitId) { 11 | // ... 12 | } 13 | 14 | public void notifyAboutChangedTransitAddress(Long driverId, Long transitId) { 15 | // ... 16 | } 17 | 18 | public void notifyAboutCancelledTransit(Long driverId, Long transitId) { 19 | // ... 20 | } 21 | 22 | public void notifyAboutPossibleTransit(Long driverId, UUID requestId) { 23 | // find transit and delegate to notifyAboutPossibleTransit(long, long) 24 | } 25 | 26 | public void notifyAboutChangedTransitAddress(Long driverId, UUID requestId) { 27 | // find transit and delegate to notifyAboutChangedTransitAddress(long, long) 28 | } 29 | 30 | public void notifyAboutCancelledTransit(Long driverId, UUID requestId) { 31 | // find transit and delegate to notifyAboutCancelledTransit(long, long) 32 | } 33 | 34 | public void askDriverForDetailsAboutClaim(String claimNo, Long driverId) { 35 | // ... 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/party/api/PartyId.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.party.api; 2 | 3 | import java.util.Objects; 4 | import java.util.UUID; 5 | 6 | public class PartyId { 7 | private UUID id; 8 | 9 | public PartyId(){ 10 | this.id = UUID.randomUUID(); 11 | } 12 | 13 | public PartyId(UUID id){ 14 | this.id = id; 15 | } 16 | 17 | @Override 18 | public boolean equals(Object o) { 19 | if (this == o) return true; 20 | if (!(o instanceof PartyId)) return false; 21 | PartyId baseId = (PartyId) o; 22 | return id.equals(baseId.id); 23 | } 24 | 25 | @Override 26 | public int hashCode() { 27 | return Objects.hash(id); 28 | } 29 | 30 | public UUID toUUID() { 31 | return id; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/party/api/PartyMapper.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.party.api; 2 | 3 | import io.legacyfighter.cabs.party.model.party.PartyRelationship; 4 | import io.legacyfighter.cabs.party.model.party.PartyRelationshipRepository; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.stereotype.Service; 7 | 8 | import java.util.Optional; 9 | 10 | @Service 11 | public class PartyMapper { 12 | 13 | @Autowired 14 | private PartyRelationshipRepository partyRelationshipRepository; 15 | 16 | public Optional mapRelation(PartyId id, String relationshipName) { 17 | return partyRelationshipRepository.findRelationshipFor(id, relationshipName); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/party/api/RoleObjectFactory.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.party.api; 2 | 3 | import io.legacyfighter.cabs.party.model.party.Party; 4 | import io.legacyfighter.cabs.party.model.party.PartyRelationship; 5 | import io.legacyfighter.cabs.party.model.role.PartyBasedRole; 6 | import io.legacyfighter.cabs.party.utils.PolymorphicHashMap; 7 | 8 | import java.util.Map; 9 | import java.util.Optional; 10 | 11 | /** 12 | * Sample impl based on Class-Instance map. 13 | * More advanced impls can be case on a DI container: getRole can obtain role instance from the container. 14 | * 15 | * TODO introduce an interface to convert to Abstract Factory Pattern to be able to choose factory impl 16 | */ 17 | public class RoleObjectFactory { 18 | 19 | Map, PartyBasedRole> roles = new PolymorphicHashMap<>(); 20 | 21 | public boolean hasRole(Class role){ 22 | return roles.containsKey(role); 23 | } 24 | 25 | public static RoleObjectFactory from(PartyRelationship partyRelationship){ 26 | RoleObjectFactory roleObject = new RoleObjectFactory(); 27 | roleObject.add(partyRelationship); 28 | return roleObject; 29 | } 30 | 31 | private void add(PartyRelationship partyRelationship){ 32 | add(partyRelationship.getRoleA(), partyRelationship.getPartyA()); 33 | add(partyRelationship.getRoleB(), partyRelationship.getPartyB()); 34 | } 35 | 36 | private void add(String role, Party party) { 37 | try { 38 | //in sake of simplicity: a role name is same as a class name with no mapping between them 39 | Class clazz = (Class) Class.forName(role); 40 | PartyBasedRole instance = clazz.getConstructor(Party.class).newInstance(party); 41 | roles.put(clazz, instance); 42 | } catch (Exception e) { 43 | throw new IllegalArgumentException(e); 44 | } 45 | } 46 | 47 | 48 | public Optional getRole(Class role){ 49 | return (Optional) Optional.ofNullable(roles.get(role)); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/party/infra/JpaPartyRepository.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.party.infra; 2 | 3 | import io.legacyfighter.cabs.party.model.party.Party; 4 | import io.legacyfighter.cabs.party.model.party.PartyRepository; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.stereotype.Repository; 7 | 8 | import javax.persistence.EntityManager; 9 | import java.util.UUID; 10 | 11 | @Repository 12 | public class JpaPartyRepository implements PartyRepository { 13 | 14 | @Autowired 15 | private EntityManager entityManager; 16 | 17 | @Override 18 | public Party put(UUID id) { 19 | Party party = entityManager.find(Party.class, id); 20 | if (party == null){ 21 | party = new Party(); 22 | party.setId(id); 23 | entityManager.persist(party); 24 | } 25 | return party; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/party/model/party/Party.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.party.model.party; 2 | 3 | import javax.persistence.Entity; 4 | import javax.persistence.Id; 5 | import java.util.UUID; 6 | 7 | @Entity 8 | public class Party { 9 | @Id 10 | UUID id; 11 | 12 | public UUID getId() { 13 | return id; 14 | } 15 | 16 | public void setId(UUID id) { 17 | this.id = id; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/party/model/party/PartyRelationship.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.party.model.party; 2 | 3 | import javax.persistence.*; 4 | import java.util.Objects; 5 | import java.util.UUID; 6 | 7 | @Entity 8 | public class PartyRelationship { 9 | @Id 10 | @GeneratedValue(strategy = GenerationType.AUTO) 11 | private UUID id; 12 | 13 | private String name; 14 | 15 | private String roleA;//String in sake of simplicity, each domain will use own ENUM 16 | 17 | private String roleB;//String in sake of simplicity, each domain will use own ENUM 18 | 19 | @ManyToOne 20 | private Party partyA; 21 | 22 | @ManyToOne 23 | private Party partyB; 24 | 25 | public UUID getId() { 26 | return id; 27 | } 28 | 29 | public void setId(UUID id) { 30 | this.id = id; 31 | } 32 | 33 | public String getName() { 34 | return name; 35 | } 36 | 37 | public void setName(String name) { 38 | this.name = name; 39 | } 40 | 41 | public String getRoleA() { 42 | return roleA; 43 | } 44 | 45 | public void setRoleA(String roleA) { 46 | this.roleA = roleA; 47 | } 48 | 49 | public String getRoleB() { 50 | return roleB; 51 | } 52 | 53 | public void setRoleB(String roleB) { 54 | this.roleB = roleB; 55 | } 56 | 57 | public Party getPartyA() { 58 | return partyA; 59 | } 60 | 61 | public void setPartyA(Party partyA) { 62 | this.partyA = partyA; 63 | } 64 | 65 | public Party getPartyB() { 66 | return partyB; 67 | } 68 | 69 | public void setPartyB(Party partyB) { 70 | this.partyB = partyB; 71 | } 72 | 73 | @Override 74 | public boolean equals(Object o) { 75 | if (this == o) return true; 76 | if (o == null || getClass() != o.getClass()) return false; 77 | PartyRelationship that = (PartyRelationship) o; 78 | return Objects.equals(id, that.id); 79 | } 80 | 81 | @Override 82 | public int hashCode() { 83 | return Objects.hash(id); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/party/model/party/PartyRelationshipRepository.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.party.model.party; 2 | 3 | import io.legacyfighter.cabs.party.api.PartyId; 4 | 5 | import java.util.Optional; 6 | 7 | public interface PartyRelationshipRepository { 8 | PartyRelationship put(String partyRelationship, String partyARole, Party partyA, String partyBRole, Party partyB); 9 | Optional findRelationshipFor(PartyId id, String relationshipName); 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/party/model/party/PartyRepository.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.party.model.party; 2 | 3 | import java.util.UUID; 4 | 5 | public interface PartyRepository { 6 | Party put(UUID id); 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/party/model/party/PartyRole.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.party.model.party; 2 | 3 | import javax.persistence.Entity; 4 | import javax.persistence.GeneratedValue; 5 | import javax.persistence.GenerationType; 6 | import javax.persistence.Id; 7 | import java.util.UUID; 8 | 9 | @Entity 10 | public class PartyRole { 11 | @Id 12 | @GeneratedValue(strategy = GenerationType.AUTO) 13 | private UUID id; 14 | 15 | private String name; 16 | 17 | public UUID getId() { 18 | return id; 19 | } 20 | 21 | public void setId(UUID id) { 22 | this.id = id; 23 | } 24 | 25 | public String getName() { 26 | return name; 27 | } 28 | 29 | public void setName(String name) { 30 | this.name = name; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/party/model/role/PartyBasedRole.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.party.model.role; 2 | 3 | import io.legacyfighter.cabs.party.model.party.Party; 4 | 5 | /** 6 | * TODO introduce interface for an abstract class 7 | */ 8 | public abstract class PartyBasedRole { 9 | protected Party party; 10 | 11 | public PartyBasedRole(Party party){ 12 | this.party = party; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/party/utils/PolymorphicHashMap.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.party.utils; 2 | 3 | import java.util.HashMap; 4 | import java.util.Optional; 5 | 6 | public class PolymorphicHashMap,V> extends HashMap { 7 | 8 | @Override 9 | public boolean containsKey(Object key) { 10 | return findEntry((K)key).isPresent(); 11 | } 12 | 13 | @Override 14 | public V get(Object key) { 15 | Optional> entry = findEntry((K)key); 16 | return entry.map(Entry::getValue).orElse(null); 17 | } 18 | 19 | private Optional> findEntry(K key) { 20 | return entrySet().stream() 21 | .filter(e -> key.isAssignableFrom(e.getKey())) 22 | .findFirst(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/pricing/Tariffs.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.pricing; 2 | 3 | import org.springframework.stereotype.Service; 4 | 5 | import java.time.Instant; 6 | import java.time.ZoneId; 7 | 8 | @Service 9 | public class Tariffs { 10 | 11 | public Tariff choose(Instant when) { 12 | if (when == null) { 13 | when = Instant.now(); 14 | } 15 | return Tariff.ofTime(when.atZone(ZoneId.systemDefault()).toLocalDateTime()); 16 | } 17 | } -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/repair/api/AssistanceRequest.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.repair.api; 2 | 3 | public class AssistanceRequest { 4 | } 5 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/repair/api/ContractManager.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.repair.api; 2 | 3 | import io.legacyfighter.cabs.party.api.PartyId; 4 | import io.legacyfighter.cabs.party.model.party.Party; 5 | import io.legacyfighter.cabs.party.model.party.PartyRelationshipRepository; 6 | import io.legacyfighter.cabs.party.model.party.PartyRepository; 7 | import io.legacyfighter.cabs.repair.model.dict.PartyRelationshipsDictionary; 8 | import io.legacyfighter.cabs.repair.model.dict.PartyRolesDictionary; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.stereotype.Service; 11 | import org.springframework.transaction.annotation.Transactional; 12 | 13 | @Service 14 | @Transactional 15 | public class ContractManager { 16 | 17 | @Autowired 18 | private PartyRepository partyRepository; 19 | 20 | @Autowired 21 | private PartyRelationshipRepository partyRelationshipRepository; 22 | 23 | public void extendedWarrantyContractSigned(PartyId insurerId, PartyId vehicleId){ 24 | Party insurer = partyRepository.put(insurerId.toUUID()); 25 | Party vehicle = partyRepository.put(vehicleId.toUUID()); 26 | 27 | partyRelationshipRepository.put(PartyRelationshipsDictionary.REPAIR.toString(), 28 | PartyRolesDictionary.INSURER.getRoleName(), insurer, 29 | PartyRolesDictionary.INSURED.getRoleName(), vehicle); 30 | } 31 | 32 | public void manufacturerWarrantyRegistered(PartyId distributorId, PartyId vehicleId) { 33 | Party distributor = partyRepository.put(distributorId.toUUID()); 34 | Party vehicle = partyRepository.put(vehicleId.toUUID()); 35 | 36 | partyRelationshipRepository.put(PartyRelationshipsDictionary.REPAIR.toString(), 37 | PartyRolesDictionary.GUARANTOR.getRoleName(), distributor, 38 | PartyRolesDictionary.CUSTOMER.getRoleName(), vehicle); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/repair/api/RepairRequest.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.repair.api; 2 | 3 | import io.legacyfighter.cabs.party.api.PartyId; 4 | import io.legacyfighter.cabs.repair.legacy.parts.Parts; 5 | 6 | import java.util.Set; 7 | 8 | public class RepairRequest { 9 | 10 | private PartyId vehicle; 11 | private Set partsToRepair; 12 | 13 | public RepairRequest(PartyId vehicle, Set parts) { 14 | this.vehicle = vehicle; 15 | this.partsToRepair = parts; 16 | } 17 | 18 | public Set getPartsToRepair() { 19 | return partsToRepair; 20 | } 21 | 22 | public PartyId getVehicle() { 23 | return vehicle; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/repair/api/ResolveResult.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.repair.api; 2 | 3 | import io.legacyfighter.cabs.money.Money; 4 | import io.legacyfighter.cabs.repair.legacy.parts.Parts; 5 | 6 | import java.util.Set; 7 | import java.util.UUID; 8 | 9 | public class ResolveResult { 10 | 11 | 12 | public enum Status { 13 | SUCCESS, ERROR; 14 | } 15 | 16 | private UUID handlingParty; 17 | private Money totalCost; 18 | private Set acceptedParts; 19 | private Status status; 20 | 21 | 22 | public ResolveResult(Status status, UUID handlingParty, Money totalCost, Set acceptedParts) { 23 | this.status = status; 24 | this.handlingParty = handlingParty; 25 | this.totalCost = totalCost; 26 | this.acceptedParts = acceptedParts; 27 | } 28 | 29 | public ResolveResult(Status status) { 30 | this.status = status; 31 | } 32 | 33 | public UUID getHandlingParty() { 34 | return handlingParty; 35 | } 36 | 37 | public Money getTotalCost() { 38 | return totalCost; 39 | } 40 | 41 | public Status getStatus() { 42 | return status; 43 | } 44 | 45 | public Set getAcceptedParts() { 46 | return acceptedParts; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/repair/legacy/dao/UserDAO.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.repair.legacy.dao; 2 | 3 | import io.legacyfighter.cabs.repair.legacy.parts.Parts; 4 | import io.legacyfighter.cabs.repair.legacy.user.CommonBaseAbstractUser; 5 | import io.legacyfighter.cabs.repair.legacy.user.EmployeeDriverWithOwnCar; 6 | import io.legacyfighter.cabs.repair.legacy.user.SignedContract; 7 | import org.springframework.stereotype.Repository; 8 | 9 | import java.util.Set; 10 | 11 | /* 12 | Fake impl that fakes graph query and determining CommonBaseAbstractUser type 13 | */ 14 | @Repository 15 | public class UserDAO { 16 | public CommonBaseAbstractUser getOne(Long userId) { 17 | SignedContract contract = new SignedContract(); 18 | contract.setCoveredParts(Set.of(Parts.values())); 19 | contract.setCoverageRatio(100.0); 20 | 21 | EmployeeDriverWithOwnCar user = new EmployeeDriverWithOwnCar(); 22 | user.setContract(contract); 23 | return user; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/repair/legacy/job/CommonBaseAbstractJob.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.repair.legacy.job; 2 | 3 | public abstract class CommonBaseAbstractJob { 4 | } 5 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/repair/legacy/job/JobResult.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.repair.legacy.job; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | public class JobResult { 7 | public enum Decision{REDIRECTION, ACCEPTED, ERROR;} 8 | 9 | private Decision decision; 10 | 11 | private Map params = new HashMap<>(); 12 | 13 | public JobResult(Decision decision) { 14 | this.decision = decision; 15 | } 16 | 17 | public JobResult addParam(String name, Object value){ 18 | params.put(name, value); 19 | return this; 20 | } 21 | 22 | public Object getParam(String name) { 23 | return params.get(name); 24 | } 25 | 26 | public Decision getDecision() { 27 | return decision; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/repair/legacy/job/MaintenanceJob.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.repair.legacy.job; 2 | 3 | public class MaintenanceJob extends CommonBaseAbstractJob{ 4 | } 5 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/repair/legacy/job/RepairJob.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.repair.legacy.job; 2 | 3 | import io.legacyfighter.cabs.money.Money; 4 | import io.legacyfighter.cabs.repair.legacy.parts.Parts; 5 | 6 | import java.util.Set; 7 | 8 | public class RepairJob extends CommonBaseAbstractJob{ 9 | private Set partsToRepair; 10 | private Money estimatedValue; 11 | 12 | public Money getEstimatedValue() { 13 | return estimatedValue; 14 | } 15 | 16 | public void setEstimatedValue(Money estimatedValue) { 17 | this.estimatedValue = estimatedValue; 18 | } 19 | 20 | public Set getPartsToRepair() { 21 | return partsToRepair; 22 | } 23 | 24 | public void setPartsToRepair(Set partsToRepair) { 25 | this.partsToRepair = partsToRepair; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/repair/legacy/parts/Parts.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.repair.legacy.parts; 2 | 3 | public enum Parts{ 4 | ENGINE, GEARBOX, SUSPENSION, PAINT 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/repair/legacy/service/JobDoer.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.repair.legacy.service; 2 | 3 | import io.legacyfighter.cabs.repair.legacy.dao.UserDAO; 4 | import io.legacyfighter.cabs.repair.legacy.job.CommonBaseAbstractJob; 5 | import io.legacyfighter.cabs.repair.legacy.job.JobResult; 6 | import io.legacyfighter.cabs.repair.legacy.user.CommonBaseAbstractUser; 7 | import org.springframework.stereotype.Service; 8 | import org.springframework.transaction.annotation.Transactional; 9 | 10 | @Service 11 | @Transactional 12 | public class JobDoer { 13 | 14 | private UserDAO userDAO; 15 | 16 | public JobDoer(UserDAO userDAO){ 17 | this.userDAO = userDAO; //I'll inject test double some day because it makes total sense to me 18 | } 19 | 20 | public JobResult repair(Long userId, CommonBaseAbstractJob job){ 21 | CommonBaseAbstractUser user = userDAO.getOne(userId); 22 | return user.doJob(job); 23 | } 24 | 25 | public JobResult repair2parallelModels(Long userId, CommonBaseAbstractJob job){ 26 | CommonBaseAbstractUser user = userDAO.getOne(userId); 27 | return user.doJob(job); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/repair/legacy/user/CommonBaseAbstractUser.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.repair.legacy.user; 2 | 3 | import io.legacyfighter.cabs.common.BaseEntity; 4 | import io.legacyfighter.cabs.repair.legacy.job.CommonBaseAbstractJob; 5 | import io.legacyfighter.cabs.repair.legacy.job.JobResult; 6 | import io.legacyfighter.cabs.repair.legacy.job.MaintenanceJob; 7 | import io.legacyfighter.cabs.repair.legacy.job.RepairJob; 8 | 9 | public abstract class CommonBaseAbstractUser extends BaseEntity { 10 | public JobResult doJob(CommonBaseAbstractJob job){ 11 | //poor man's pattern matching 12 | if (job instanceof RepairJob){ 13 | return handle((RepairJob) job); 14 | } 15 | if (job instanceof MaintenanceJob){ 16 | return handle((MaintenanceJob) job); 17 | } 18 | return defaultHandler(job); 19 | } 20 | 21 | protected JobResult handle(RepairJob job) { 22 | return defaultHandler(job); 23 | } 24 | 25 | protected JobResult handle(MaintenanceJob job) { 26 | return defaultHandler(job); 27 | } 28 | 29 | protected JobResult defaultHandler(CommonBaseAbstractJob job){ 30 | throw new IllegalArgumentException(getClass().getName() + " can not handle " + job.getClass().getName()); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/repair/legacy/user/EmployeeDriver.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.repair.legacy.user; 2 | 3 | public class EmployeeDriver extends CommonBaseAbstractUser{ 4 | } 5 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/repair/legacy/user/EmployeeDriverWithLeasedCar.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.repair.legacy.user; 2 | 3 | import io.legacyfighter.cabs.repair.legacy.job.JobResult; 4 | import io.legacyfighter.cabs.repair.legacy.job.RepairJob; 5 | 6 | public class EmployeeDriverWithLeasedCar extends EmployeeDriver{ 7 | 8 | private Long lasingCompanyId; 9 | 10 | @Override 11 | protected JobResult handle(RepairJob job) { 12 | return new JobResult(JobResult.Decision.REDIRECTION).addParam("shouldHandleBy", lasingCompanyId); 13 | } 14 | 15 | public Long getLasingCompanyId() { 16 | return lasingCompanyId; 17 | } 18 | 19 | public void setLasingCompanyId(Long lasingCompanyId) { 20 | this.lasingCompanyId = lasingCompanyId; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/repair/legacy/user/EmployeeDriverWithOwnCar.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.repair.legacy.user; 2 | 3 | import io.legacyfighter.cabs.money.Money; 4 | import io.legacyfighter.cabs.repair.legacy.job.JobResult; 5 | import io.legacyfighter.cabs.repair.legacy.job.RepairJob; 6 | import io.legacyfighter.cabs.repair.legacy.parts.Parts; 7 | 8 | import javax.persistence.Entity; 9 | import javax.persistence.OneToOne; 10 | import java.util.HashSet; 11 | import java.util.Set; 12 | 13 | @Entity 14 | public class EmployeeDriverWithOwnCar extends EmployeeDriver{ 15 | 16 | @OneToOne 17 | private SignedContract contract; 18 | 19 | @Override 20 | protected JobResult handle(RepairJob job) { 21 | Set acceptedParts = new HashSet<>(job.getPartsToRepair()); 22 | acceptedParts.retainAll(contract.getCoveredParts()); 23 | 24 | Money coveredCost = job.getEstimatedValue().percentage(contract.getCoverageRatio()); 25 | Money totalCost = job.getEstimatedValue().subtract(coveredCost); 26 | 27 | return new JobResult(JobResult.Decision.ACCEPTED).addParam("totalCost", totalCost).addParam("acceptedParts", acceptedParts); 28 | } 29 | 30 | public void setContract(SignedContract contract) { 31 | this.contract = contract; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/repair/legacy/user/SignedContract.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.repair.legacy.user; 2 | 3 | import io.legacyfighter.cabs.common.BaseEntity; 4 | import io.legacyfighter.cabs.repair.legacy.parts.Parts; 5 | 6 | import javax.persistence.ElementCollection; 7 | import javax.persistence.Entity; 8 | import javax.persistence.FetchType; 9 | import java.util.Set; 10 | 11 | @Entity 12 | public class SignedContract extends BaseEntity { 13 | @ElementCollection(fetch = FetchType.EAGER) 14 | private Set coveredParts; 15 | 16 | private Double coverageRatio; 17 | 18 | public Double getCoverageRatio() { 19 | return coverageRatio; 20 | } 21 | 22 | public void setCoverageRatio(Double coverageRatio) { 23 | this.coverageRatio = coverageRatio; 24 | } 25 | 26 | public Set getCoveredParts() { 27 | return coveredParts; 28 | } 29 | 30 | public void setCoveredParts(Set coveredParts) { 31 | this.coveredParts = coveredParts; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/repair/legacy/user/SubcontractorDriver.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.repair.legacy.user; 2 | 3 | public class SubcontractorDriver extends CommonBaseAbstractUser{ 4 | } 5 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/repair/legacy/user/SubcontractorDriverWithOwnCar.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.repair.legacy.user; 2 | 3 | public class SubcontractorDriverWithOwnCar extends SubcontractorDriver{ 4 | } 5 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/repair/legacy/user/SubcontractorWithRentedCar.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.repair.legacy.user; 2 | 3 | public class SubcontractorWithRentedCar { 4 | } 5 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/repair/model/dict/PartyRelationshipsDictionary.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.repair.model.dict; 2 | 3 | /* 4 | Enum that emulates database dictionary 5 | */ 6 | public enum PartyRelationshipsDictionary{ 7 | REPAIR, SERVICE, CLEANING 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/repair/model/dict/PartyRolesDictionary.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.repair.model.dict; 2 | 3 | import io.legacyfighter.cabs.party.model.role.PartyBasedRole; 4 | import io.legacyfighter.cabs.repair.model.roles.empty.Customer; 5 | import io.legacyfighter.cabs.repair.model.roles.empty.Insured; 6 | import io.legacyfighter.cabs.repair.model.roles.repair.ExtendedInsurance; 7 | import io.legacyfighter.cabs.repair.model.roles.repair.Warranty; 8 | 9 | /* 10 | Enum that emulates database dictionary 11 | */ 12 | public enum PartyRolesDictionary { 13 | INSURER(ExtendedInsurance.class), INSURED(Insured.class), GUARANTOR(Warranty.class), CUSTOMER(Customer.class); 14 | 15 | private final String name; 16 | 17 | PartyRolesDictionary(Class clazz){ 18 | name = clazz.getName(); 19 | } 20 | 21 | public String getRoleName(){ 22 | return name; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/repair/model/roles/assistance/RoleForAssistance.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.repair.model.roles.assistance; 2 | 3 | import io.legacyfighter.cabs.party.model.party.Party; 4 | import io.legacyfighter.cabs.party.model.role.PartyBasedRole; 5 | import io.legacyfighter.cabs.repair.api.AssistanceRequest; 6 | 7 | /** 8 | * Base class for all commands that are able to handle @{@link AssistanceRequest} 9 | */ 10 | public abstract class RoleForAssistance extends PartyBasedRole { 11 | public RoleForAssistance(Party party) { 12 | super(party); 13 | } 14 | 15 | public abstract void handle(AssistanceRequest request); 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/repair/model/roles/empty/Customer.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.repair.model.roles.empty; 2 | 3 | import io.legacyfighter.cabs.party.model.party.Party; 4 | import io.legacyfighter.cabs.party.model.role.PartyBasedRole; 5 | 6 | 7 | public class Customer extends PartyBasedRole { 8 | public Customer(Party party) { 9 | super(party); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/repair/model/roles/empty/Insured.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.repair.model.roles.empty; 2 | 3 | import io.legacyfighter.cabs.party.model.party.Party; 4 | import io.legacyfighter.cabs.party.model.role.PartyBasedRole; 5 | 6 | public class Insured extends PartyBasedRole { 7 | public Insured(Party party) { 8 | super(party); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/repair/model/roles/repair/ExtendedInsurance.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.repair.model.roles.repair; 2 | 3 | import io.legacyfighter.cabs.money.Money; 4 | import io.legacyfighter.cabs.party.model.party.Party; 5 | import io.legacyfighter.cabs.repair.api.RepairRequest; 6 | import io.legacyfighter.cabs.repair.legacy.parts.Parts; 7 | 8 | import java.util.HashSet; 9 | import java.util.Set; 10 | 11 | 12 | public class ExtendedInsurance extends RoleForRepairer { 13 | 14 | public ExtendedInsurance(Party party) { 15 | super(party); 16 | } 17 | 18 | public RepairingResult handle(RepairRequest repairRequest) { 19 | Set handledParts = new HashSet<>(repairRequest.getPartsToRepair()); 20 | handledParts.remove(Parts.PAINT); 21 | 22 | return new RepairingResult(party.getId(), Money.ZERO, handledParts); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/repair/model/roles/repair/RepairingResult.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.repair.model.roles.repair; 2 | 3 | import io.legacyfighter.cabs.money.Money; 4 | import io.legacyfighter.cabs.repair.legacy.parts.Parts; 5 | 6 | import java.util.Set; 7 | import java.util.UUID; 8 | 9 | public class RepairingResult { 10 | private final UUID handlingParty; 11 | private final Money totalCost; 12 | private final Set handledParts; 13 | 14 | public RepairingResult(UUID handlingParty, Money totalCost, Set handledParts) { 15 | this.handlingParty = handlingParty; 16 | this.totalCost = totalCost; 17 | this.handledParts = handledParts; 18 | } 19 | 20 | public UUID getHandlingParty() { 21 | return handlingParty; 22 | } 23 | 24 | public Money getTotalCost() { 25 | return totalCost; 26 | } 27 | 28 | public Set getHandledParts() { 29 | return handledParts; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/repair/model/roles/repair/RoleForRepairer.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.repair.model.roles.repair; 2 | 3 | import io.legacyfighter.cabs.party.model.party.Party; 4 | import io.legacyfighter.cabs.party.model.role.PartyBasedRole; 5 | import io.legacyfighter.cabs.repair.api.RepairRequest; 6 | 7 | /** 8 | * Base class for all commands that are able to handle @{@link RepairRequest RepairRequest} 9 | */ 10 | public abstract class RoleForRepairer extends PartyBasedRole { 11 | public RoleForRepairer(Party party) { 12 | super(party); 13 | } 14 | 15 | public abstract RepairingResult handle(RepairRequest repairRequest); 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/repair/model/roles/repair/Warranty.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.repair.model.roles.repair; 2 | 3 | import io.legacyfighter.cabs.money.Money; 4 | import io.legacyfighter.cabs.party.model.party.Party; 5 | import io.legacyfighter.cabs.repair.api.RepairRequest; 6 | import io.legacyfighter.cabs.repair.legacy.parts.Parts; 7 | 8 | import java.util.HashSet; 9 | import java.util.Set; 10 | 11 | public class Warranty extends RoleForRepairer { 12 | 13 | public Warranty(Party party) { 14 | super(party); 15 | } 16 | 17 | public RepairingResult handle(RepairRequest repairRequest) { 18 | Set handledParts = new HashSet<>(repairRequest.getPartsToRepair()); 19 | 20 | return new RepairingResult(party.getId(), Money.ZERO, handledParts); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/ride/ChangeDestinationService.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.ride; 2 | 3 | import io.legacyfighter.cabs.geolocation.Distance; 4 | import io.legacyfighter.cabs.geolocation.DistanceCalculator; 5 | import io.legacyfighter.cabs.geolocation.GeocodingService; 6 | import io.legacyfighter.cabs.geolocation.address.Address; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.stereotype.Service; 9 | import org.springframework.transaction.annotation.Transactional; 10 | 11 | import java.util.UUID; 12 | 13 | @Service 14 | class ChangeDestinationService { 15 | 16 | @Autowired 17 | private TransitRepository transitRepository; 18 | 19 | @Autowired 20 | private DistanceCalculator distanceCalculator; 21 | 22 | @Autowired 23 | private GeocodingService geocodingService; 24 | 25 | @Transactional 26 | public Distance changeTransitAddressTo(UUID requestUUID, Address newAddress, Address from) { 27 | // FIXME later: add some exceptions handling 28 | double[] geoFrom = geocodingService.geocodeAddress(from); 29 | double[] geoTo = geocodingService.geocodeAddress(newAddress); 30 | Distance newDistance = Distance.ofKm((float) distanceCalculator.calculateByMap(geoFrom[0], geoFrom[1], geoTo[0], geoTo[1])); 31 | Transit transit = transitRepository.findByTransitRequestUUID(requestUUID); 32 | if (transit != null) { 33 | transit.changeDestination(newDistance); 34 | } 35 | return newDistance; 36 | 37 | } 38 | 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/ride/CompleteTransitService.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.ride; 2 | 3 | import io.legacyfighter.cabs.geolocation.Distance; 4 | import io.legacyfighter.cabs.geolocation.DistanceCalculator; 5 | import io.legacyfighter.cabs.geolocation.GeocodingService; 6 | import io.legacyfighter.cabs.geolocation.address.Address; 7 | import io.legacyfighter.cabs.money.Money; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.stereotype.Service; 10 | import org.springframework.transaction.annotation.Transactional; 11 | 12 | import java.util.UUID; 13 | 14 | @Service 15 | class CompleteTransitService { 16 | 17 | @Autowired 18 | private TransitRepository transitRepository; 19 | 20 | @Autowired 21 | private DistanceCalculator distanceCalculator; 22 | 23 | @Autowired 24 | private GeocodingService geocodingService; 25 | 26 | @Transactional 27 | public Money completeTransit(Long driverId, UUID requestUUID, Address from, Address destinationAddress) { 28 | Transit transit = transitRepository.findByTransitRequestUUID(requestUUID); 29 | 30 | if (transit == null) { 31 | throw new IllegalArgumentException("Transit does not exist, id = " + requestUUID); 32 | } 33 | 34 | // FIXME later: add some exceptions handling 35 | double[] geoFrom = geocodingService.geocodeAddress(from); 36 | double[] geoTo = geocodingService.geocodeAddress(destinationAddress); 37 | Distance distance = Distance.ofKm((float) distanceCalculator.calculateByMap(geoFrom[0], geoFrom[1], geoTo[0], geoTo[1])); 38 | Money finalPrice = transit.completeAt(distance); 39 | transitRepository.save(transit); 40 | return finalPrice; 41 | } 42 | 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/ride/DemandService.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.ride; 2 | 3 | import org.springframework.stereotype.Service; 4 | import org.springframework.transaction.annotation.Transactional; 5 | 6 | import java.util.UUID; 7 | 8 | @Service 9 | public class DemandService { 10 | 11 | private final TransitDemandRepository transitDemandRepository; 12 | 13 | public DemandService(TransitDemandRepository transitDemandRepository) { 14 | this.transitDemandRepository = transitDemandRepository; 15 | } 16 | 17 | @Transactional 18 | public void publishDemand(UUID requestUUID) { 19 | transitDemandRepository.save(new TransitDemand(requestUUID)); 20 | } 21 | 22 | @Transactional 23 | public void cancelDemand(UUID requestUUID) { 24 | TransitDemand transitDemand = transitDemandRepository.findByTransitRequestUUID(requestUUID); 25 | if (transitDemand != null) { 26 | transitDemand.cancel(); 27 | } 28 | } 29 | 30 | @Transactional 31 | public void acceptDemand(UUID requestUUID) { 32 | TransitDemand transitDemand = transitDemandRepository.findByTransitRequestUUID(requestUUID); 33 | if (transitDemand != null) { 34 | transitDemand.accept(); 35 | } 36 | } 37 | 38 | public boolean existsFor(UUID requestUUID) { 39 | return transitDemandRepository.findByTransitRequestUUID(requestUUID) != null; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/ride/RequestForTransit.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.ride; 2 | 3 | import io.legacyfighter.cabs.common.BaseEntity; 4 | import io.legacyfighter.cabs.geolocation.Distance; 5 | import io.legacyfighter.cabs.money.Money; 6 | import io.legacyfighter.cabs.pricing.Tariff; 7 | 8 | import javax.persistence.Embedded; 9 | import javax.persistence.Entity; 10 | import java.util.UUID; 11 | 12 | @Entity 13 | public class RequestForTransit extends BaseEntity { 14 | 15 | private UUID requestUUID = UUID.randomUUID(); 16 | 17 | @Embedded 18 | private Tariff tariff; 19 | 20 | @Embedded 21 | private Distance distance; 22 | 23 | RequestForTransit() { 24 | } 25 | 26 | public RequestForTransit(Tariff tariff, Distance distance) { 27 | this.tariff = tariff; 28 | this.distance = distance; 29 | } 30 | 31 | public Money getEstimatedPrice() { 32 | return tariff.calculateCost(distance); 33 | } 34 | 35 | public UUID getRequestUUID() { 36 | return requestUUID; 37 | } 38 | 39 | public Tariff getTariff() { 40 | return tariff; 41 | } 42 | 43 | @Override 44 | public boolean equals(Object o) { 45 | if (this == o) return true; 46 | 47 | if (!(o instanceof RequestForTransit)) 48 | return false; 49 | 50 | RequestForTransit other = (RequestForTransit) o; 51 | 52 | return this.getId() != null && 53 | this.getId().equals(other.getId()); 54 | } 55 | 56 | 57 | public Distance getDistance() { 58 | return distance; 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/ride/RequestForTransitRepository.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.ride; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | 5 | import java.util.UUID; 6 | 7 | public interface RequestForTransitRepository extends JpaRepository { 8 | 9 | RequestForTransit findByRequestUUID(UUID requestUUID); 10 | } 11 | 12 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/ride/StartTransitService.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.ride; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.stereotype.Service; 5 | import org.springframework.transaction.annotation.Transactional; 6 | 7 | import java.util.UUID; 8 | 9 | @Service 10 | class StartTransitService { 11 | 12 | @Autowired 13 | private TransitRepository transitRepository; 14 | 15 | @Autowired 16 | private RequestTransitService requestTransitService; 17 | 18 | @Transactional 19 | public Transit start(UUID requestUUID) { 20 | Transit transit = new Transit(requestTransitService.findTariff(requestUUID), requestUUID); 21 | return transitRepository.save(transit); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/ride/TransitDemand.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.ride; 2 | 3 | import io.legacyfighter.cabs.common.BaseEntity; 4 | 5 | import javax.persistence.Entity; 6 | import java.util.UUID; 7 | 8 | 9 | @Entity 10 | public class TransitDemand extends BaseEntity { 11 | 12 | private UUID transitRequestUUID; 13 | 14 | public enum Status { 15 | CANCELLED, 16 | WAITING_FOR_DRIVER_ASSIGNMENT, 17 | TRANSIT_TO_PASSENGER, 18 | } 19 | 20 | private Status status; 21 | 22 | public Integer pickupAddressChangeCounter = 0; 23 | 24 | TransitDemand() { 25 | 26 | } 27 | 28 | public TransitDemand(UUID transitRequestUUID) { 29 | this.transitRequestUUID = transitRequestUUID; 30 | this.status = Status.WAITING_FOR_DRIVER_ASSIGNMENT; 31 | } 32 | 33 | public void changePickup(double distanceFromPreviousPickup) { 34 | if (distanceFromPreviousPickup > 0.25) { 35 | throw new IllegalStateException("Address 'from' cannot be changed, id = " + getId()); 36 | } else if (!(this.status.equals(Status.WAITING_FOR_DRIVER_ASSIGNMENT))) { 37 | throw new IllegalStateException("Address 'from' cannot be changed, id = " + getId()); 38 | } else if (pickupAddressChangeCounter > 2) { 39 | throw new IllegalStateException("Address 'from' cannot be changed, id = " + getId()); 40 | } 41 | this.pickupAddressChangeCounter = pickupAddressChangeCounter + 1; 42 | } 43 | 44 | public void accept() { 45 | status = Status.TRANSIT_TO_PASSENGER; 46 | } 47 | 48 | public void cancel() { 49 | if (this.status != Status.WAITING_FOR_DRIVER_ASSIGNMENT) { 50 | throw new IllegalStateException("Demand cannot be cancelled, id = " + getId()); 51 | } 52 | this.status = Status.CANCELLED; 53 | } 54 | 55 | public Status getStatus() { 56 | return status; 57 | } 58 | 59 | @Override 60 | public boolean equals(Object o) { 61 | if (this == o) return true; 62 | 63 | if (!(o instanceof Transit)) 64 | return false; 65 | 66 | Transit other = (Transit) o; 67 | 68 | return this.getId() != null && 69 | this.getId().equals(other.getId()); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/ride/TransitDemandRepository.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.ride; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | 5 | import java.util.UUID; 6 | 7 | public interface TransitDemandRepository extends JpaRepository { 8 | 9 | TransitDemand findByTransitRequestUUID(UUID requestUUID); 10 | } 11 | 12 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/ride/TransitRepository.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.ride; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | import org.springframework.data.jpa.repository.Query; 5 | 6 | import java.util.List; 7 | import java.util.UUID; 8 | 9 | public interface TransitRepository extends JpaRepository { 10 | 11 | Transit findByTransitRequestUUID(UUID transitRequestUUID); 12 | 13 | @Query("select T from Transit T join TransitDetails TD ON T.transitRequestUUID = TD.requestUUID where TD.client.id = ?1") 14 | List findByClientId(Long clientId); 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/ride/details/Status.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.ride.details; 2 | 3 | public enum Status { 4 | DRAFT, 5 | CANCELLED, 6 | WAITING_FOR_DRIVER_ASSIGNMENT, 7 | DRIVER_ASSIGNMENT_FAILED, 8 | TRANSIT_TO_PASSENGER, 9 | IN_TRANSIT, 10 | COMPLETED 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/ride/details/TransitDetailsRepository.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.ride.details; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | import org.springframework.data.jpa.repository.Query; 5 | 6 | import java.time.Instant; 7 | import java.util.List; 8 | import java.util.UUID; 9 | 10 | interface TransitDetailsRepository extends JpaRepository { 11 | 12 | TransitDetails findByRequestUUID(UUID requestUUID); 13 | 14 | List findByClientId(Long clientId); 15 | 16 | @Query("select TD from TransitDetails TD where TD.driverId = ?1 and TD.dateTime between ?2 and ?3") 17 | List findAllByDriverAndDateTimeBetween(Long driverID, Instant from, Instant to); 18 | 19 | List findByStatus(Status completed); 20 | 21 | TransitDetails findByTransitId(Long transitId); 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/ride/events/TransitCompleted.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.ride.events; 2 | 3 | import io.legacyfighter.cabs.common.Event; 4 | 5 | import java.time.Instant; 6 | 7 | public class TransitCompleted implements Event { 8 | 9 | public final Long clientId; 10 | public final Long transitId; 11 | public final Integer addressFromHash; 12 | public final Integer addressToHash; 13 | public final Instant started; 14 | public final Instant completeAt; 15 | public final Instant eventTimestamp; 16 | 17 | public TransitCompleted(Long clientId, Long transitId, Integer addressFromHash, Integer addressToHash, Instant started, Instant completeAt, Instant eventTimestamp) { 18 | this.clientId = clientId; 19 | this.transitId = transitId; 20 | this.addressFromHash = addressFromHash; 21 | this.addressToHash = addressToHash; 22 | this.started = started; 23 | this.completeAt = completeAt; 24 | this.eventTimestamp = eventTimestamp; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/tracking/DriverPosition.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.tracking; 2 | 3 | import io.legacyfighter.cabs.common.BaseEntity; 4 | 5 | import javax.persistence.*; 6 | import java.time.Instant; 7 | 8 | @Entity 9 | class DriverPosition extends BaseEntity { 10 | 11 | private Long driverId; 12 | 13 | @Column(nullable = false) 14 | private double latitude; 15 | 16 | @Column(nullable = false) 17 | private double longitude; 18 | 19 | @Column(nullable = false) 20 | private Instant seenAt; 21 | 22 | DriverPosition() { 23 | } 24 | 25 | Long getDriverId() { 26 | return driverId; 27 | } 28 | 29 | void setDriverId(Long driverId) { 30 | this.driverId = driverId; 31 | } 32 | 33 | double getLatitude() { 34 | return latitude; 35 | } 36 | 37 | void setLatitude(double latitude) { 38 | this.latitude = latitude; 39 | } 40 | 41 | double getLongitude() { 42 | return longitude; 43 | } 44 | 45 | void setLongitude(double longitude) { 46 | this.longitude = longitude; 47 | } 48 | 49 | Instant getSeenAt() { 50 | return seenAt; 51 | } 52 | 53 | void setSeenAt(Instant seenAt) { 54 | this.seenAt = seenAt; 55 | } 56 | 57 | @Override 58 | public boolean equals(Object o) { 59 | if (this == o) return true; 60 | 61 | if (!(o instanceof DriverPosition)) 62 | return false; 63 | 64 | DriverPosition other = (DriverPosition) o; 65 | 66 | return this.getId() != null && 67 | this.getId().equals(other.getId()); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/tracking/DriverPositionDTO.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.tracking; 2 | 3 | import java.time.Instant; 4 | 5 | public class DriverPositionDTO { 6 | 7 | public DriverPositionDTO() { 8 | 9 | } 10 | 11 | private Long driverId; 12 | 13 | private double latitude; 14 | 15 | private double longitude; 16 | 17 | private Instant seenAt; 18 | 19 | public DriverPositionDTO(Long driverId, double latitude, double longitude, Instant seenAt) { 20 | this.driverId = driverId; 21 | this.latitude = latitude; 22 | this.longitude = longitude; 23 | this.seenAt = seenAt; 24 | } 25 | 26 | public Long getDriverId() { 27 | return driverId; 28 | } 29 | 30 | public void setDriverId(Long driverId) { 31 | this.driverId = driverId; 32 | } 33 | 34 | public double getLatitude() { 35 | return latitude; 36 | } 37 | 38 | public void setLatitude(double latitude) { 39 | this.latitude = latitude; 40 | } 41 | 42 | public double getLongitude() { 43 | return longitude; 44 | } 45 | 46 | public void setLongitude(double longitude) { 47 | this.longitude = longitude; 48 | } 49 | 50 | public Instant getSeenAt() { 51 | return seenAt; 52 | } 53 | 54 | public void setSeenAt(Instant seenAt) { 55 | this.seenAt = seenAt; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/tracking/DriverPositionDTOV2.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.tracking; 2 | 3 | import java.time.Instant; 4 | 5 | public class DriverPositionDTOV2 { 6 | 7 | public DriverPositionDTOV2() { 8 | 9 | } 10 | 11 | private Long driverId; 12 | 13 | private double latitude; 14 | 15 | private double longitude; 16 | 17 | private Instant seenAt; 18 | 19 | public DriverPositionDTOV2(Long driverId, double latitude, double longitude, Instant seenAt) { 20 | this.driverId = driverId; 21 | this.latitude = latitude; 22 | this.longitude = longitude; 23 | this.seenAt = seenAt; 24 | } 25 | 26 | public Long getDriverId() { 27 | return driverId; 28 | } 29 | 30 | public void setDriverId(Long driverId) { 31 | this.driverId = driverId; 32 | } 33 | 34 | public double getLatitude() { 35 | return latitude; 36 | } 37 | 38 | public void setLatitude(double latitude) { 39 | this.latitude = latitude; 40 | } 41 | 42 | public double getLongitude() { 43 | return longitude; 44 | } 45 | 46 | public void setLongitude(double longitude) { 47 | this.longitude = longitude; 48 | } 49 | 50 | public Instant getSeenAt() { 51 | return seenAt; 52 | } 53 | 54 | public void setSeenAt(Instant seenAt) { 55 | this.seenAt = seenAt; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/tracking/DriverPositionRepository.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.tracking; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | import org.springframework.data.jpa.repository.Query; 5 | 6 | import java.time.Instant; 7 | import java.util.List; 8 | 9 | public interface DriverPositionRepository extends JpaRepository { 10 | 11 | @Query(value = "SELECT new io.legacyfighter.cabs.tracking.DriverPositionDTOV2(p.driverId, avg(p.latitude), avg(p.longitude), max(p.seenAt)) FROM DriverPosition p where p.latitude between ?1 and ?2 and p.longitude between ?3 and ?4 and p.seenAt >= ?5 group by p.driverId") 12 | List findAverageDriverPositionSince(double latitudeMin, double latitudeMax, double longitudeMin, double longitudeMax, Instant date); 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/tracking/DriverSession.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.tracking; 2 | 3 | import io.legacyfighter.cabs.carfleet.CarClass; 4 | import io.legacyfighter.cabs.common.BaseEntity; 5 | 6 | import javax.persistence.*; 7 | import java.time.Instant; 8 | 9 | @Entity 10 | public 11 | class DriverSession extends BaseEntity { 12 | 13 | @Column(nullable = false) 14 | private Instant loggedAt; 15 | 16 | private Instant loggedOutAt; 17 | 18 | private Long driverId; 19 | 20 | @Column(nullable = false) 21 | private String platesNumber; 22 | 23 | @Enumerated(EnumType.STRING) 24 | private CarClass carClass; 25 | 26 | private String carBrand; 27 | 28 | String getCarBrand() { 29 | return carBrand; 30 | } 31 | 32 | void setCarBrand(String carBrand) { 33 | this.carBrand = carBrand; 34 | } 35 | 36 | Instant getLoggedAt() { 37 | return loggedAt; 38 | } 39 | 40 | void setLoggedAt(Instant loggedAt) { 41 | this.loggedAt = loggedAt; 42 | } 43 | 44 | Instant getLoggedOutAt() { 45 | return loggedOutAt; 46 | } 47 | 48 | void setLoggedOutAt(Instant loggedOutAt) { 49 | this.loggedOutAt = loggedOutAt; 50 | } 51 | 52 | public Long getDriverId() { 53 | return driverId; 54 | } 55 | 56 | void setDriverId(Long driverId) { 57 | this.driverId = driverId; 58 | } 59 | 60 | String getPlatesNumber() { 61 | return platesNumber; 62 | } 63 | 64 | void setPlatesNumber(String platesNumber) { 65 | this.platesNumber = platesNumber; 66 | } 67 | 68 | CarClass getCarClass() { 69 | return carClass; 70 | } 71 | 72 | void setCarClass(CarClass carClass) { 73 | this.carClass = carClass; 74 | } 75 | 76 | @Override 77 | public boolean equals(Object o) { 78 | if (this == o) return true; 79 | 80 | if (!(o instanceof DriverSession)) 81 | return false; 82 | 83 | DriverSession other = (DriverSession) o; 84 | 85 | return this.getId() != null && 86 | this.getId().equals(other.getId()); 87 | } 88 | } 89 | 90 | 91 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/tracking/DriverSessionController.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.tracking; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.http.ResponseEntity; 5 | import org.springframework.web.bind.annotation.*; 6 | 7 | import java.time.Clock; 8 | import java.util.List; 9 | import java.util.stream.Collectors; 10 | 11 | @RestController 12 | public class DriverSessionController { 13 | 14 | @Autowired 15 | private DriverSessionService driverSessionService; 16 | 17 | @Autowired 18 | private Clock clock; 19 | 20 | @PostMapping("/drivers/{driverId}/driverSessions/login") 21 | public ResponseEntity logIn(@PathVariable Long driverId, @RequestBody DriverSessionDTO dto) { 22 | driverSessionService.logIn(driverId, dto.getPlatesNumber(), dto.getCarClass(), dto.getCarBrand()); 23 | return ResponseEntity.ok().build(); 24 | } 25 | 26 | @DeleteMapping("/drivers/{driverId}/driverSessions/{sessionId}") 27 | public ResponseEntity logOut(@PathVariable Long driverId, @PathVariable Long sessionId) { 28 | driverSessionService.logOut(sessionId); 29 | return ResponseEntity.ok().build(); 30 | } 31 | 32 | @DeleteMapping("/drivers/{driverId}/driverSessions/") 33 | public ResponseEntity logOutCurrent(@PathVariable Long driverId) { 34 | driverSessionService.logOutCurrentSession(driverId); 35 | return ResponseEntity.ok().build(); 36 | } 37 | 38 | @GetMapping("/drivers/{driverId}/driverSessions/") 39 | public ResponseEntity> list(@PathVariable Long driverId) { 40 | return ResponseEntity.ok(driverSessionService.findByDriver(driverId) 41 | .stream() 42 | .map(DriverSessionDTO::new) 43 | .collect(Collectors.toList())); 44 | } 45 | } 46 | 47 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/tracking/DriverSessionDTO.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.tracking; 2 | 3 | import io.legacyfighter.cabs.carfleet.CarClass; 4 | 5 | import java.time.Instant; 6 | 7 | 8 | public class DriverSessionDTO { 9 | 10 | private Instant loggedAt; 11 | 12 | private Instant loggedOutAt; 13 | 14 | private String platesNumber; 15 | 16 | private CarClass carClass; 17 | 18 | private String carBrand; 19 | 20 | public DriverSessionDTO(Instant loggedAt, Instant loggedOutAt, String platesNumber, CarClass carClass, String carBrand) { 21 | this.loggedAt = loggedAt; 22 | this.loggedOutAt = loggedOutAt; 23 | this.platesNumber = platesNumber; 24 | this.carClass = carClass; 25 | this.carBrand = carBrand; 26 | } 27 | 28 | public DriverSessionDTO() { 29 | 30 | } 31 | 32 | public DriverSessionDTO(DriverSession session) { 33 | this.carBrand = session.getCarBrand(); 34 | this.platesNumber = session.getPlatesNumber(); 35 | this.loggedAt = session.getLoggedAt(); 36 | this.loggedOutAt = session.getLoggedOutAt(); 37 | this.carClass = session.getCarClass(); 38 | } 39 | 40 | public String getCarBrand() { 41 | return carBrand; 42 | } 43 | 44 | public void setCarBrand(String carBrand) { 45 | this.carBrand = carBrand; 46 | } 47 | 48 | public Instant getLoggedAt() { 49 | return loggedAt; 50 | } 51 | 52 | public void setLoggedAt(Instant loggedAt) { 53 | this.loggedAt = loggedAt; 54 | } 55 | 56 | public Instant getLoggedOutAt() { 57 | return loggedOutAt; 58 | } 59 | 60 | public void setLoggedOutAt(Instant loggedOutAt) { 61 | this.loggedOutAt = loggedOutAt; 62 | } 63 | 64 | public String getPlatesNumber() { 65 | return platesNumber; 66 | } 67 | 68 | public void setPlatesNumber(String platesNumber) { 69 | this.platesNumber = platesNumber; 70 | } 71 | 72 | public CarClass getCarClass() { 73 | return carClass; 74 | } 75 | 76 | public void setCarClass(CarClass carClass) { 77 | this.carClass = carClass; 78 | } 79 | 80 | } 81 | 82 | 83 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/tracking/DriverSessionRepository.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.tracking; 2 | 3 | import io.legacyfighter.cabs.carfleet.CarClass; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | import java.util.Collection; 7 | import java.util.List; 8 | 9 | public interface DriverSessionRepository extends JpaRepository { 10 | 11 | List findAllByLoggedOutAtNullAndDriverIdInAndCarClassIn(Collection driverIds, Collection carClasses); 12 | 13 | DriverSession findTopByDriverIdAndLoggedOutAtIsNullOrderByLoggedAtDesc(Long driverId); 14 | 15 | List findByDriverId(Long driverId); 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/io/legacyfighter/cabs/tracking/DriverTrackingController.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.tracking; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.http.ResponseEntity; 5 | import org.springframework.web.bind.annotation.*; 6 | 7 | import java.time.Instant; 8 | 9 | @RestController 10 | public class DriverTrackingController { 11 | @Autowired 12 | private DriverTrackingService trackingService; 13 | 14 | @PostMapping("/driverPositions/") 15 | ResponseEntity create(DriverPositionDTO driverPositionDTO) { 16 | DriverPosition driverPosition = trackingService.registerPosition(driverPositionDTO.getDriverId(), driverPositionDTO.getLatitude(), driverPositionDTO.getLongitude(), driverPositionDTO.getSeenAt()); 17 | return ResponseEntity.ok(toDto(driverPosition)); 18 | } 19 | 20 | @GetMapping("/driverPositions/{id}/total") 21 | double calculateTravelledDistance(@PathVariable Long id, @RequestParam Instant from, @RequestParam Instant to) { 22 | return trackingService.calculateTravelledDistance(id, from, to).toKmInDouble(); 23 | } 24 | 25 | private DriverPositionDTO toDto(DriverPosition driverPosition) { 26 | DriverPositionDTO dto = new DriverPositionDTO(); 27 | dto.setDriverId(driverPosition.getDriverId()); 28 | dto.setLatitude(driverPosition.getLatitude()); 29 | dto.setLongitude(driverPosition.getLongitude()); 30 | dto.setSeenAt(driverPosition.getSeenAt()); 31 | return dto; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/test/java/io/legacyfighter/cabs/common/AddressFixture.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.common; 2 | 3 | 4 | import io.legacyfighter.cabs.geolocation.GeocodingService; 5 | import io.legacyfighter.cabs.geolocation.address.Address; 6 | import io.legacyfighter.cabs.geolocation.address.AddressDTO; 7 | import io.legacyfighter.cabs.geolocation.address.AddressRepository; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.stereotype.Component; 10 | 11 | import java.util.Random; 12 | 13 | import static org.mockito.ArgumentMatchers.argThat; 14 | import static org.mockito.Mockito.when; 15 | 16 | @Component 17 | class AddressFixture { 18 | 19 | @Autowired 20 | AddressRepository addressRepository; 21 | 22 | Address anAddress() { 23 | return addressRepository.save(new Address("Polska", "Warszawa", "Młynarska", 20)); 24 | } 25 | 26 | public AddressDTO anAddress(GeocodingService geocodingService, String country, String city, String street, int buildingNumber) { 27 | AddressDTO addressDTO = new AddressDTO(country, city, street, buildingNumber); 28 | Random random = new Random(); 29 | when(geocodingService.geocodeAddress(argThat(new AddressMatcher(addressDTO)))).thenReturn(new double[]{random.nextInt(), random.nextInt()}); 30 | return addressDTO; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/test/java/io/legacyfighter/cabs/common/AddressMatcher.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.common; 2 | 3 | import io.legacyfighter.cabs.geolocation.address.Address; 4 | import io.legacyfighter.cabs.geolocation.address.AddressDTO; 5 | import org.mockito.ArgumentMatcher; 6 | 7 | import java.util.Objects; 8 | 9 | public class AddressMatcher implements ArgumentMatcher
{ 10 | 11 | private String country; 12 | private String city; 13 | private String street; 14 | private Integer buildingNumber; 15 | 16 | public AddressMatcher(Address address) { 17 | this(address.getCountry(), address.getCity(), address.getStreet(), address.getBuildingNumber()); 18 | } 19 | 20 | public AddressMatcher(AddressDTO dto) { 21 | this(dto.toAddressEntity()); 22 | } 23 | 24 | public AddressMatcher(String country, String city, String street, Integer buildingNumber) { 25 | this.country = country; 26 | this.city = city; 27 | this.street = street; 28 | this.buildingNumber = buildingNumber; 29 | } 30 | 31 | @Override 32 | public boolean matches(Address right) { 33 | if (right == null) { 34 | return false; 35 | } 36 | return Objects.equals(country, right.getCountry()) && 37 | Objects.equals(city, right.getCity()) && 38 | Objects.equals(street, right.getStreet()) && 39 | Objects.equals(buildingNumber, right.getBuildingNumber()); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/test/java/io/legacyfighter/cabs/common/AwardsAccountFixture.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.common; 2 | 3 | 4 | import io.legacyfighter.cabs.crm.Client; 5 | import io.legacyfighter.cabs.loyalty.AwardsService; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.stereotype.Component; 8 | 9 | @Component 10 | class AwardsAccountFixture { 11 | 12 | @Autowired 13 | AwardsService awardsService; 14 | 15 | void awardsAccount(Client client) { 16 | awardsService.registerToProgram(client.getId()); 17 | } 18 | 19 | void activeAwardsAccount(Client client) { 20 | awardsAccount(client); 21 | awardsService.activateAccount(client.getId()); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/test/java/io/legacyfighter/cabs/common/CarTypeFixture.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.common; 2 | 3 | import io.legacyfighter.cabs.carfleet.CarClass; 4 | import io.legacyfighter.cabs.carfleet.CarTypeDTO; 5 | import io.legacyfighter.cabs.carfleet.CarTypeService; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.stereotype.Component; 8 | 9 | import java.util.stream.IntStream; 10 | 11 | @Component 12 | class CarTypeFixture { 13 | 14 | @Autowired 15 | CarTypeService carTypeService; 16 | 17 | CarTypeDTO anActiveCarCategory(CarClass carClass) { 18 | CarTypeDTO carTypeDTO = new CarTypeDTO(); 19 | carTypeDTO.setCarClass(carClass); 20 | carTypeDTO.setDescription("opis"); 21 | CarTypeDTO carType = carTypeService.create(carTypeDTO); 22 | IntStream.range(1, carType.getMinNoOfCarsToActivateClass() + 1) 23 | .forEach(i -> carTypeService.registerCar(carType.getCarClass())); 24 | carTypeService.activate(carType.getId()); 25 | return carType; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/test/java/io/legacyfighter/cabs/common/ClaimFixture.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.common; 2 | 3 | 4 | import io.legacyfighter.cabs.crm.claims.ClaimDTO; 5 | import io.legacyfighter.cabs.crm.claims.Claim; 6 | import io.legacyfighter.cabs.crm.Client; 7 | import io.legacyfighter.cabs.ride.TransitDTO; 8 | import io.legacyfighter.cabs.ride.Transit; 9 | import io.legacyfighter.cabs.crm.claims.ClaimService; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.stereotype.Component; 12 | 13 | @Component 14 | class ClaimFixture { 15 | 16 | @Autowired 17 | ClaimService claimService; 18 | 19 | @Autowired 20 | ClientFixture clientFixture; 21 | 22 | Claim createClaim(Client client, Transit transit) { 23 | ClaimDTO claimDTO = claimDto("Okradli mnie na hajs", "$$$", client.getId(), transit.getId()); 24 | claimDTO.setDraft(false); 25 | Claim claim = claimService.create(claimDTO); 26 | return claim; 27 | } 28 | 29 | Claim createClaim(Client client, TransitDTO transit, String reason) { 30 | ClaimDTO claimDTO = claimDto("Okradli mnie na hajs", reason, client.getId(), transit.getId()); 31 | claimDTO.setDraft(false); 32 | return claimService.create(claimDTO); 33 | } 34 | 35 | Claim createAndResolveClaim(Client client, Transit transit) { 36 | Claim claim = createClaim(client, transit); 37 | claim = claimService.tryToResolveAutomatically(claim.getId()); 38 | return claim; 39 | } 40 | 41 | ClaimDTO claimDto(String desc, String reason, Long clientId, Long transitId) { 42 | ClaimDTO claimDTO = new ClaimDTO(); 43 | claimDTO.setClientId(clientId); 44 | claimDTO.setTransitId(transitId); 45 | claimDTO.setIncidentDescription(desc); 46 | claimDTO.setReason(reason); 47 | return claimDTO; 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/test/java/io/legacyfighter/cabs/common/ClientFixture.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.common; 2 | 3 | 4 | import io.legacyfighter.cabs.crm.Client; 5 | 6 | import io.legacyfighter.cabs.crm.ClientRepository; 7 | 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.stereotype.Component; 10 | 11 | @Component 12 | class ClientFixture { 13 | 14 | @Autowired 15 | ClientRepository clientRepository; 16 | 17 | Client aClient() { 18 | return clientRepository.save(new Client()); 19 | } 20 | 21 | Client aClient(Client.Type type) { 22 | Client client = new Client(); 23 | client.setType(type); 24 | return clientRepository.save(client); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/test/java/io/legacyfighter/cabs/common/StubbedTransitPrice.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.common; 2 | 3 | import io.legacyfighter.cabs.pricing.Tariff; 4 | import io.legacyfighter.cabs.money.Money; 5 | import io.legacyfighter.cabs.pricing.Tariffs; 6 | import org.springframework.boot.test.mock.mockito.SpyBean; 7 | import org.springframework.stereotype.Service; 8 | import org.springframework.transaction.annotation.Transactional; 9 | 10 | import java.time.Instant; 11 | 12 | import static org.mockito.ArgumentMatchers.isA; 13 | import static org.mockito.Mockito.when; 14 | 15 | @Service 16 | public class StubbedTransitPrice { 17 | 18 | @SpyBean 19 | Tariffs tariffs; 20 | 21 | @Transactional 22 | public void stub(Money faked) { 23 | Tariff fakeTariff = new Tariff(0, "fake", faked); 24 | when(tariffs.choose(isA(Instant.class))).thenReturn(fakeTariff); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/test/java/io/legacyfighter/cabs/common/TestWithGraphDB.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.common; 2 | 3 | 4 | import io.legacyfighter.cabs.CabsApplication; 5 | import io.legacyfighter.cabs.crm.transitanalyzer.GraphTransitAnalyzer; 6 | import org.neo4j.graphdb.GraphDatabaseService; 7 | import org.neo4j.graphdb.factory.GraphDatabaseFactory; 8 | import org.springframework.beans.factory.annotation.Value; 9 | import org.springframework.boot.test.context.SpringBootTest; 10 | import org.springframework.context.annotation.Bean; 11 | import org.springframework.context.annotation.Primary; 12 | import org.springframework.util.FileSystemUtils; 13 | 14 | import javax.annotation.PreDestroy; 15 | import java.io.File; 16 | 17 | 18 | @SpringBootTest(properties = "neo4j.db.file=${random.int}", classes = {CabsApplication.class, TestWithGraphDB.TestNeo4jConfig.class}) 19 | public class TestWithGraphDB { 20 | 21 | static class TestNeo4jConfig { 22 | 23 | @Value("${neo4j.db.file}") 24 | private String dbPath; 25 | 26 | GraphDatabaseService testGraphDatabaseService() { 27 | GraphDatabaseFactory graphDbFactory = new GraphDatabaseFactory(); 28 | FileSystemUtils.deleteRecursively(new File("db")); 29 | File storeDir = new File("db/" + dbPath); 30 | return graphDbFactory.newEmbeddedDatabase(storeDir); 31 | } 32 | 33 | @Bean(destroyMethod = "onClose") 34 | @Primary 35 | GraphTransitAnalyzer testGraphTransitAnalyzer() { 36 | return new GraphTransitAnalyzer(testGraphDatabaseService()); 37 | } 38 | 39 | @PreDestroy 40 | void cleanDbDir() { 41 | FileSystemUtils.deleteRecursively(new File("db")); 42 | } 43 | 44 | } 45 | 46 | } 47 | 48 | -------------------------------------------------------------------------------- /src/test/java/io/legacyfighter/cabs/contracts/application/dynamic/DocumentOperationResultAssert.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.contracts.application.dynamic; 2 | 3 | import io.legacyfighter.cabs.contracts.application.acme.dynamic.DocumentOperationResult; 4 | import io.legacyfighter.cabs.contracts.model.ContentId; 5 | 6 | import java.util.Set; 7 | 8 | import static org.junit.jupiter.api.Assertions.*; 9 | 10 | 11 | public class DocumentOperationResultAssert { 12 | private DocumentOperationResult result; 13 | 14 | public DocumentOperationResultAssert(DocumentOperationResult result){ 15 | this.result = result; 16 | assertEquals(DocumentOperationResult.Result.SUCCESS, result.getResult()); 17 | } 18 | 19 | public DocumentOperationResultAssert editable(){ 20 | assertTrue(result.isContentChangePossible()); 21 | return this; 22 | } 23 | 24 | public DocumentOperationResultAssert uneditable(){ 25 | assertFalse(result.isContentChangePossible()); 26 | return this; 27 | } 28 | 29 | public DocumentOperationResultAssert state(String state){ 30 | assertEquals(state, result.getStateName()); 31 | return this; 32 | } 33 | 34 | public DocumentOperationResultAssert content(ContentId contentId) { 35 | assertEquals(contentId, result.getContentId()); 36 | return this; 37 | } 38 | 39 | public DocumentOperationResultAssert possibleNextStates(String ... states){ 40 | assertEquals(Set.of(states), 41 | result.getPossibleTransitionsAndRules().keySet()); 42 | return this; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/test/java/io/legacyfighter/cabs/contracts/application/straightforward/acme/ContractResultAssert.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.contracts.application.straightforward.acme; 2 | 3 | import io.legacyfighter.cabs.contracts.application.acme.straigthforward.ContractResult; 4 | import io.legacyfighter.cabs.contracts.model.state.straightforward.BaseState; 5 | 6 | import static org.junit.jupiter.api.Assertions.assertEquals; 7 | 8 | 9 | public class ContractResultAssert { 10 | private ContractResult result; 11 | 12 | public ContractResultAssert(ContractResult result){ 13 | this.result = result; 14 | assertEquals(ContractResult.Result.SUCCESS, result.getResult()); 15 | } 16 | 17 | public ContractResultAssert state(BaseState state){ 18 | assertEquals(state.getStateDescriptor(), result.getStateDescriptor()); 19 | return this; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/test/java/io/legacyfighter/cabs/contracts/legacy/DocumentTest.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.contracts.legacy; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import static org.junit.jupiter.api.Assertions.assertEquals; 6 | import static org.junit.jupiter.api.Assertions.assertTrue; 7 | 8 | 9 | public class DocumentTest { 10 | 11 | private static final String ANY_NUMBER = "number"; 12 | private static final User ANY_USER = new User(); 13 | private static final User OTHER_USER = new User(); 14 | private static final String TITLE = "title"; 15 | 16 | @Test 17 | public void onlyDraftCanBeVerifiedByUserOtherThanCreator(){ 18 | Document doc = new Document(ANY_NUMBER, ANY_USER); 19 | 20 | doc.verifyBy(OTHER_USER); 21 | 22 | assertEquals(DocumentStatus.VERIFIED, doc.getStatus()); 23 | } 24 | 25 | @Test 26 | public void canNotChangePublished(){ 27 | Document doc = new Document(ANY_NUMBER, ANY_USER); 28 | doc.changeTitle(TITLE); 29 | doc.verifyBy(OTHER_USER); 30 | doc.publish(); 31 | 32 | try { 33 | doc.changeTitle(""); 34 | } 35 | catch (IllegalStateException ex){ 36 | assertTrue(true); 37 | } 38 | assertEquals(TITLE, doc.getTitle()); 39 | } 40 | 41 | @Test 42 | public void changingVerifiedMovesToDraft(){ 43 | Document doc = new Document(ANY_NUMBER, ANY_USER); 44 | doc.changeTitle(TITLE); 45 | doc.verifyBy(OTHER_USER); 46 | 47 | doc.changeTitle(""); 48 | 49 | assertEquals(DocumentStatus.DRAFT, doc.getStatus()); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/test/java/io/legacyfighter/cabs/contracts/model/state/dynamic/FakeDocumentPublisher.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.contracts.model.state.dynamic; 2 | 3 | import io.legacyfighter.cabs.contracts.model.state.dynamic.config.events.DocumentEvent; 4 | import org.springframework.context.ApplicationEventPublisher; 5 | 6 | import java.util.HashSet; 7 | import java.util.Set; 8 | 9 | import static org.junit.jupiter.api.Assertions.assertEquals; 10 | import static org.junit.jupiter.api.Assertions.assertTrue; 11 | 12 | 13 | public class FakeDocumentPublisher implements ApplicationEventPublisher { 14 | 15 | private Set events = new HashSet(); 16 | 17 | @Override 18 | public void publishEvent(Object event) { 19 | events.add(event); 20 | } 21 | 22 | public void contains(Class event){ 23 | boolean found = events.stream().anyMatch(e -> e.getClass().equals(event)); 24 | assertTrue(found); 25 | } 26 | 27 | public void noEvents() { 28 | assertEquals(0, events.size()); 29 | } 30 | 31 | public void reset() { 32 | events.clear(); 33 | } 34 | 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/test/java/io/legacyfighter/cabs/driverfleet/DriverLicenseTest.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.driverfleet; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import static org.assertj.core.api.Assertions.assertThatExceptionOfType; 6 | import static org.junit.jupiter.api.Assertions.assertEquals; 7 | 8 | class DriverLicenseTest { 9 | 10 | @Test 11 | void cannotCreateInvalidLicense() { 12 | assertThatExceptionOfType(IllegalArgumentException.class) 13 | .isThrownBy(() -> DriverLicense.withLicense("invalid")); 14 | assertThatExceptionOfType(IllegalArgumentException.class) 15 | .isThrownBy(() -> DriverLicense.withLicense("")); 16 | } 17 | 18 | @Test 19 | void canCreateValidLicense() { 20 | //when 21 | DriverLicense license = DriverLicense.withLicense("FARME100165AB5EW"); 22 | 23 | //then 24 | assertEquals("FARME100165AB5EW", license.asString()); 25 | } 26 | 27 | @Test 28 | void canCreateInvalidLicenseExplicitly() { 29 | //when 30 | DriverLicense license = DriverLicense.withoutValidation("invalid"); 31 | 32 | //then 33 | assertEquals("invalid", license.asString()); 34 | } 35 | 36 | } -------------------------------------------------------------------------------- /src/test/java/io/legacyfighter/cabs/integration/CalculateDriverFeeIntegrationTest.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.integration; 2 | 3 | import io.legacyfighter.cabs.common.Fixtures; 4 | import io.legacyfighter.cabs.driverfleet.Driver; 5 | import io.legacyfighter.cabs.driverfleet.DriverFee; 6 | 7 | import io.legacyfighter.cabs.money.Money; 8 | import io.legacyfighter.cabs.driverfleet.DriverFeeService; 9 | import org.junit.jupiter.api.Test; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.boot.test.context.SpringBootTest; 12 | 13 | import static org.junit.jupiter.api.Assertions.assertEquals; 14 | 15 | @SpringBootTest 16 | class CalculateDriverFeeIntegrationTest { 17 | 18 | @Autowired 19 | Fixtures fixtures; 20 | 21 | @Autowired 22 | DriverFeeService driverFeeService; 23 | 24 | @Test 25 | void shouldCalculateDriversFlatFee() { 26 | //given 27 | Driver driver = fixtures.aDriver(); 28 | //and 29 | fixtures.driverHasFee(driver, DriverFee.FeeType.FLAT, 10); 30 | 31 | //when 32 | Money fee = driverFeeService.calculateDriverFee(new Money(60), driver.getId()); 33 | 34 | //then 35 | assertEquals(new Money(50), fee); 36 | } 37 | 38 | @Test 39 | void shouldCalculateDriversPercentageFee() { 40 | //given 41 | Driver driver = fixtures.aDriver(); 42 | //and 43 | fixtures.driverHasFee(driver, DriverFee.FeeType.PERCENTAGE, 50); 44 | 45 | //when 46 | Money fee = driverFeeService.calculateDriverFee(new Money(80), driver.getId()); 47 | 48 | //then 49 | assertEquals(new Money(40), fee); 50 | } 51 | 52 | @Test 53 | void shouldUseMinimumFee() { 54 | //given 55 | Driver driver = fixtures.aDriver(); 56 | //and 57 | fixtures.driverHasFee(driver, DriverFee.FeeType.PERCENTAGE, 7, 5); 58 | 59 | //when 60 | Money fee = driverFeeService.calculateDriverFee(new Money(10), driver.getId()); 61 | 62 | //then 63 | assertEquals(new Money(5), fee); 64 | } 65 | 66 | } -------------------------------------------------------------------------------- /src/test/java/io/legacyfighter/cabs/integration/DriverTrackingServiceIntegrationTest.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.integration; 2 | 3 | import io.legacyfighter.cabs.common.Fixtures; 4 | import io.legacyfighter.cabs.geolocation.Distance; 5 | import io.legacyfighter.cabs.driverfleet.Driver; 6 | import io.legacyfighter.cabs.tracking.DriverTrackingService; 7 | import org.junit.jupiter.api.Test; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.boot.test.context.SpringBootTest; 10 | import org.springframework.boot.test.mock.mockito.MockBean; 11 | 12 | import java.time.Clock; 13 | import java.time.Instant; 14 | import java.time.LocalDateTime; 15 | import java.time.ZoneOffset; 16 | import java.time.temporal.ChronoUnit; 17 | 18 | import static org.junit.jupiter.api.Assertions.assertEquals; 19 | import static org.mockito.Mockito.when; 20 | 21 | @SpringBootTest 22 | class DriverTrackingServiceIntegrationTest { 23 | 24 | static Instant NOON = LocalDateTime.of(1989, 12, 12, 12, 12).toInstant(ZoneOffset.UTC); 25 | static Instant NOON_FIVE = NOON.plus(5, ChronoUnit.MINUTES); 26 | 27 | @Autowired 28 | DriverTrackingService driverTrackingService; 29 | 30 | @Autowired 31 | Fixtures fixtures; 32 | 33 | @MockBean 34 | Clock clock; 35 | 36 | @Test 37 | void canCalculateTravelledDistanceFromShortTransit() { 38 | //given 39 | Driver driver = fixtures.aDriver(); 40 | //and 41 | itIsNoon(); 42 | //and 43 | driverTrackingService.registerPosition(driver.getId(), 53.32055555555556, -1.7297222222222221, NOON); 44 | driverTrackingService.registerPosition(driver.getId(), 53.31861111111111, -1.6997222222222223, NOON); 45 | driverTrackingService.registerPosition(driver.getId(), 53.32055555555556, -1.7297222222222221, NOON); 46 | 47 | //when 48 | Distance distance = driverTrackingService.calculateTravelledDistance(driver.getId(), NOON, NOON_FIVE); 49 | 50 | //then 51 | assertEquals("4.009km", distance.printIn("km")); 52 | } 53 | 54 | void itIsNoon() { 55 | when(clock.instant()).thenReturn(NOON); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/test/java/io/legacyfighter/cabs/integration/GraphTransitAnalyzerIntegrationTest.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.integration; 2 | 3 | import io.legacyfighter.cabs.common.TestWithGraphDB; 4 | import io.legacyfighter.cabs.crm.transitanalyzer.GraphTransitAnalyzer; 5 | import org.junit.jupiter.api.Test; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | 8 | import java.time.Instant; 9 | import java.util.List; 10 | 11 | import static org.assertj.core.api.Assertions.assertThat; 12 | 13 | class GraphTransitAnalyzerIntegrationTest extends TestWithGraphDB { 14 | 15 | @Autowired 16 | GraphTransitAnalyzer analyzer; 17 | 18 | @Test 19 | void canRecognizeNewAddress() { 20 | //given 21 | analyzer.addTransitBetweenAddresses(1L, 1L, 111, 222, Instant.now(), Instant.now()); 22 | analyzer.addTransitBetweenAddresses(1L, 1L, 222, 333, Instant.now(), Instant.now()); 23 | analyzer.addTransitBetweenAddresses(1L, 1L, 333, 444, Instant.now(), Instant.now()); 24 | 25 | //when 26 | List result = analyzer.analyze(1L, 111); 27 | 28 | //then 29 | assertThat(result).containsExactly(111L, 222L, 333L, 444L); 30 | 31 | } 32 | 33 | } -------------------------------------------------------------------------------- /src/test/java/io/legacyfighter/cabs/money/MoneyTest.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.money; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import static org.junit.jupiter.api.Assertions.*; 6 | 7 | class MoneyTest { 8 | 9 | @Test 10 | void canCreateMoneyFromInteger() { 11 | //expect 12 | assertEquals("100.00", new Money(10000).toString()); 13 | assertEquals("0.00", new Money(0).toString()); 14 | assertEquals("10.12", new Money(1012).toString()); 15 | } 16 | 17 | @Test 18 | void shouldProjectMoneyToInteger() { 19 | //expect 20 | assertEquals(10, new Money(10).toInt()); 21 | assertEquals(0, new Money(0).toInt()); 22 | assertEquals(-5, new Money(-5).toInt()); 23 | } 24 | 25 | @Test 26 | void canAddMoney() { 27 | //expect 28 | assertEquals(new Money(1000), new Money(500).add(new Money(500))); 29 | assertEquals(new Money(1042), new Money(1020).add(new Money(22))); 30 | assertEquals(new Money(0), new Money(0).add(new Money(0))); 31 | assertEquals(new Money(-2), new Money(-4).add(new Money(2))); 32 | } 33 | 34 | @Test 35 | void canSubtractMoney() { 36 | //expect 37 | assertEquals(Money.ZERO, new Money(50).subtract(new Money(50))); 38 | assertEquals(new Money(998), new Money(1020).subtract(new Money(22))); 39 | assertEquals(new Money(-1), new Money(2).subtract(new Money(3))); 40 | } 41 | 42 | @Test 43 | void canCalculatePercentage() { 44 | //expect 45 | assertEquals("30.00", new Money(10000).percentage(30).toString()); 46 | assertEquals("26.40", new Money(8800).percentage(30).toString()); 47 | assertEquals("88.00", new Money(8800).percentage(100).toString()); 48 | assertEquals("0.00", new Money(8800).percentage(0).toString()); 49 | assertEquals("13.20", new Money(4400).percentage(30).toString()); 50 | assertEquals("0.30", new Money(100).percentage(30).toString()); 51 | assertEquals("0.00", new Money(1).percentage(40).toString()); 52 | } 53 | 54 | } -------------------------------------------------------------------------------- /src/test/java/io/legacyfighter/cabs/pricing/TariffTest.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.pricing; 2 | 3 | import io.legacyfighter.cabs.money.Money; 4 | import io.legacyfighter.cabs.pricing.Tariff; 5 | import org.junit.jupiter.api.Test; 6 | import java.time.LocalDateTime; 7 | 8 | import static io.legacyfighter.cabs.geolocation.Distance.ofKm; 9 | import static org.junit.jupiter.api.Assertions.assertEquals; 10 | 11 | class TariffTest { 12 | @Test 13 | void regularTariffShouldBeDisplayedAndCalculated() { 14 | //given 15 | Tariff tariff = Tariff.ofTime(LocalDateTime.of(2021, 4, 16, 8, 30)); 16 | 17 | //expect 18 | assertEquals(new Money(2900), tariff.calculateCost(ofKm(20))); //29.00 19 | assertEquals("Standard", tariff.getName()); 20 | assertEquals(1.0f, tariff.getKmRate()); 21 | } 22 | 23 | @Test 24 | void sundayTariffShouldBeDisplayedAndCalculated() { 25 | //expect 26 | Tariff tariff = Tariff.ofTime(LocalDateTime.of(2021, 4, 18, 8, 30)); 27 | 28 | //expect 29 | assertEquals(new Money(3800), tariff.calculateCost(ofKm(20))); //38.00 30 | assertEquals("Weekend", tariff.getName()); 31 | assertEquals(1.5f, tariff.getKmRate()); 32 | } 33 | 34 | @Test 35 | void newYearsEveTariffShouldBeDisplayedAndCalculated() { 36 | //given 37 | Tariff tariff = Tariff.ofTime(LocalDateTime.of(2021, 12, 31, 8, 30)); 38 | 39 | //expect 40 | assertEquals(new Money(8100), tariff.calculateCost(ofKm(20))); //81.00 41 | assertEquals("Sylwester", tariff.getName()); 42 | assertEquals(3.5f, tariff.getKmRate()); 43 | } 44 | 45 | @Test 46 | void saturdayTariffShouldBeDisplayedAndCalculated() { 47 | //given 48 | Tariff tariff = Tariff.ofTime(LocalDateTime.of(2021, 4, 17, 8, 30)); 49 | 50 | //expect 51 | assertEquals(new Money(3800), tariff.calculateCost(ofKm(20))); //38.00 52 | assertEquals("Weekend", tariff.getName()); 53 | assertEquals(1.5f, tariff.getKmRate()); 54 | } 55 | 56 | @Test 57 | void saturdayNightTariffShouldBeDisplayedAndCalculated() { 58 | //given 59 | Tariff tariff = Tariff.ofTime(LocalDateTime.of(2021, 4, 17, 19, 30)); 60 | 61 | //expect 62 | assertEquals(new Money(6000), tariff.calculateCost(ofKm(20))); //60.00 63 | assertEquals("Weekend+", tariff.getName()); 64 | assertEquals(2.5f, tariff.getKmRate()); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/test/java/io/legacyfighter/cabs/repair/api/RepairProcessTest.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.repair.api; 2 | 3 | import io.legacyfighter.cabs.party.api.PartyId; 4 | import io.legacyfighter.cabs.repair.legacy.parts.Parts; 5 | import org.junit.jupiter.api.Test; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.boot.test.context.SpringBootTest; 8 | 9 | import java.util.Set; 10 | 11 | @SpringBootTest 12 | public class RepairProcessTest { 13 | 14 | @Autowired 15 | private RepairProcess vehicleRepairProcess; 16 | 17 | @Autowired 18 | private ContractManager contractManager; 19 | 20 | private final PartyId vehicle = new PartyId(); 21 | private final PartyId handlingParty = new PartyId(); 22 | 23 | @Test 24 | public void warrantyByInsuranceCoversAllButPaint(){ 25 | //given 26 | contractManager.extendedWarrantyContractSigned(handlingParty, vehicle); 27 | 28 | Set parts = Set.of(new Parts[] {Parts.ENGINE, Parts.GEARBOX, Parts.PAINT, Parts.SUSPENSION}); 29 | RepairRequest repairRequest = new RepairRequest(vehicle, parts); 30 | //when 31 | ResolveResult result = vehicleRepairProcess.resolve(repairRequest); 32 | //then 33 | new VehicleRepairAssert(result).by(handlingParty).free().allPartsBut(parts, new Parts[] {Parts.PAINT}); 34 | } 35 | 36 | @Test 37 | public void manufacturerWarrantyCoversAll(){ 38 | //given 39 | contractManager.manufacturerWarrantyRegistered(handlingParty, vehicle); 40 | 41 | Set parts = Set.of(new Parts[]{Parts.ENGINE, Parts.GEARBOX, Parts.PAINT, Parts.SUSPENSION}); 42 | RepairRequest repairRequest = new RepairRequest(vehicle, parts); 43 | //when 44 | ResolveResult result = vehicleRepairProcess.resolve(repairRequest); 45 | //then 46 | new VehicleRepairAssert(result).by(handlingParty).free().allParts(parts); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/test/java/io/legacyfighter/cabs/repair/api/VehicleRepairAssert.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.repair.api; 2 | 3 | import io.legacyfighter.cabs.money.Money; 4 | import io.legacyfighter.cabs.party.api.PartyId; 5 | import io.legacyfighter.cabs.repair.legacy.parts.Parts; 6 | 7 | import java.util.Arrays; 8 | import java.util.HashSet; 9 | import java.util.Set; 10 | import java.util.stream.Collectors; 11 | 12 | import static org.junit.jupiter.api.Assertions.assertEquals; 13 | 14 | class VehicleRepairAssert { 15 | private ResolveResult result; 16 | 17 | public VehicleRepairAssert(ResolveResult result) { 18 | this(result, true); 19 | } 20 | 21 | public VehicleRepairAssert(ResolveResult result, boolean demandSuccess) { 22 | this.result = result; 23 | if (demandSuccess) 24 | assertEquals(ResolveResult.Status.SUCCESS, result.getStatus()); 25 | else 26 | assertEquals(ResolveResult.Status.ERROR, result.getStatus()); 27 | } 28 | 29 | public VehicleRepairAssert free() { 30 | assertEquals(Money.ZERO, result.getTotalCost()); 31 | return this; 32 | } 33 | 34 | public VehicleRepairAssert allParts(Set parts) { 35 | assertEquals(parts, result.getAcceptedParts()); 36 | return this; 37 | } 38 | 39 | public VehicleRepairAssert by(PartyId handlingParty) { 40 | assertEquals(handlingParty.toUUID(), result.getHandlingParty()); 41 | return this; 42 | } 43 | 44 | public VehicleRepairAssert allPartsBut(Set parts, Parts[] excludedParts) { 45 | Set exptectedParts = new HashSet<>(parts); 46 | exptectedParts.removeAll(Arrays.stream(excludedParts).collect(Collectors.toSet())); 47 | 48 | assertEquals(exptectedParts, result.getAcceptedParts()); 49 | return this; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/test/java/io/legacyfighter/cabs/repair/legacy/job/RepairTest.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.repair.legacy.job; 2 | 3 | import io.legacyfighter.cabs.money.Money; 4 | import io.legacyfighter.cabs.repair.legacy.parts.Parts; 5 | import io.legacyfighter.cabs.repair.legacy.user.EmployeeDriverWithOwnCar; 6 | import io.legacyfighter.cabs.repair.legacy.user.SignedContract; 7 | import org.junit.jupiter.api.Test; 8 | 9 | import java.util.Set; 10 | 11 | import static org.junit.jupiter.api.Assertions.assertEquals; 12 | 13 | 14 | public class RepairTest { 15 | 16 | @Test 17 | public void employeeDriverWithOwnCarCoveredByWarrantyShouldRepairForFree(){ 18 | //given 19 | EmployeeDriverWithOwnCar employee = new EmployeeDriverWithOwnCar(); 20 | employee.setContract(fullCoverageWarranty()); 21 | //when 22 | JobResult result = employee.doJob(fullRepair()); 23 | //then 24 | assertEquals(JobResult.Decision.ACCEPTED, result.getDecision()); 25 | assertEquals(Money.ZERO, result.getParam("totalCost")); 26 | assertEquals(allParts(), result.getParam("acceptedParts")); 27 | } 28 | 29 | private RepairJob fullRepair() { 30 | RepairJob job = new RepairJob(); 31 | job.setEstimatedValue(new Money(50000)); 32 | job.setPartsToRepair(allParts()); 33 | return job; 34 | } 35 | 36 | private SignedContract fullCoverageWarranty() { 37 | SignedContract contract = new SignedContract(); 38 | contract.setCoverageRatio(100.0); 39 | contract.setCoveredParts(allParts()); 40 | return contract; 41 | } 42 | 43 | private Set allParts(){ 44 | return Set.of(Parts.values()); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/test/java/io/legacyfighter/cabs/repair/legacy/service/JobDoerTest.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.repair.legacy.service; 2 | 3 | import io.legacyfighter.cabs.money.Money; 4 | import io.legacyfighter.cabs.repair.legacy.job.JobResult; 5 | import io.legacyfighter.cabs.repair.legacy.job.RepairJob; 6 | import io.legacyfighter.cabs.repair.legacy.parts.Parts; 7 | import org.junit.jupiter.api.Test; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.boot.test.context.SpringBootTest; 10 | 11 | import java.util.Set; 12 | 13 | import static org.junit.jupiter.api.Assertions.assertEquals; 14 | 15 | @SpringBootTest 16 | public class JobDoerTest { 17 | 18 | /** 19 | * fake database returns {@link io.legacyfighter.cabs.repair.legacy.user.EmployeeDriverWithOwnCar} 20 | */ 21 | private static final Long ANY_USER = 1L; 22 | 23 | @Autowired 24 | JobDoer jobDoer; 25 | 26 | @Test 27 | public void employeeWithOwnCarWithWarrantyShouldHaveCoveredAllPartsForFree(){ 28 | JobResult result = jobDoer.repair(ANY_USER, repairJob()); 29 | 30 | assertEquals(result.getDecision(), JobResult.Decision.ACCEPTED); 31 | assertEquals(result.getParam("acceptedParts"), allParts()); 32 | assertEquals(result.getParam("totalCost"), Money.ZERO); 33 | } 34 | 35 | private RepairJob repairJob() { 36 | RepairJob job= new RepairJob(); 37 | job.setPartsToRepair(allParts()); 38 | job.setEstimatedValue(new Money(7000)); 39 | return job; 40 | } 41 | 42 | Set allParts(){ 43 | return Set.of(Parts.values()); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/test/java/io/legacyfighter/cabs/ride/RequestForTransitTest.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.ride; 2 | 3 | import io.legacyfighter.cabs.geolocation.Distance; 4 | import io.legacyfighter.cabs.pricing.Tariff; 5 | import org.junit.jupiter.api.Test; 6 | 7 | import java.time.LocalDateTime; 8 | 9 | import static org.junit.jupiter.api.Assertions.assertNotEquals; 10 | import static org.junit.jupiter.api.Assertions.assertNotNull; 11 | 12 | class RequestForTransitTest { 13 | 14 | @Test 15 | void canCreateRequestForTransit() { 16 | //when 17 | RequestForTransit requestForTransit = requestTransit(); 18 | 19 | //expect 20 | assertNotNull(requestForTransit.getTariff()); 21 | assertNotEquals(0, requestForTransit.getTariff().getKmRate()); 22 | } 23 | 24 | 25 | RequestForTransit requestTransit() { 26 | Tariff tariff = Tariff.ofTime(LocalDateTime.now()); 27 | return new RequestForTransit(tariff, Distance.ZERO); 28 | } 29 | 30 | } -------------------------------------------------------------------------------- /src/test/java/io/legacyfighter/cabs/ride/TransitTest.java: -------------------------------------------------------------------------------- 1 | package io.legacyfighter.cabs.ride; 2 | 3 | import io.legacyfighter.cabs.geolocation.Distance; 4 | import io.legacyfighter.cabs.pricing.Tariff; 5 | import org.junit.jupiter.api.Test; 6 | 7 | import java.time.LocalDateTime; 8 | import java.util.UUID; 9 | 10 | import static io.legacyfighter.cabs.geolocation.Distance.ofKm; 11 | import static org.assertj.core.api.Assertions.assertThatExceptionOfType; 12 | import static org.junit.jupiter.api.Assertions.assertEquals; 13 | 14 | class TransitTest { 15 | 16 | @Test 17 | void canChangeTransitDestination() { 18 | //given 19 | Transit transit = transit(); 20 | 21 | //expect 22 | transit.changeDestination(ofKm(20)); 23 | 24 | //then 25 | assertEquals(ofKm(20), transit.getDistance()); 26 | } 27 | 28 | @Test 29 | void cannotChangeDestinationWhenTransitIsCompleted() { 30 | //given 31 | Transit transit = transit(); 32 | //and 33 | transit.completeAt(ofKm(20)); 34 | 35 | //expect 36 | assertThatExceptionOfType(IllegalStateException.class) 37 | .isThrownBy(() -> transit.changeDestination(ofKm(20))); 38 | } 39 | 40 | @Test 41 | void canCompleteTransit() { 42 | Transit transit = transit(); 43 | //and 44 | transit.completeAt(Distance.ofKm(20)); 45 | 46 | //then 47 | assertEquals(Transit.Status.COMPLETED, transit.getStatus()); 48 | } 49 | 50 | 51 | Transit transit() { 52 | return new Transit(Tariff.ofTime(LocalDateTime.now()), UUID.randomUUID()); 53 | } 54 | } -------------------------------------------------------------------------------- /src/test/resources/application.properties: -------------------------------------------------------------------------------- 1 | #spring.jpa.show-sql=true --------------------------------------------------------------------------------