Merge branch '81-custom-endpoints' into 'master'

Resolve "Custom endpoints"

Closes #81

See merge request marcel.schwarz/2020ss-qbc-geofence-timetracking!59
This commit is contained in:
Marcel Schwarz 2020-05-20 22:02:04 +00:00
commit ed284da1ac
10 changed files with 198 additions and 15 deletions

View File

@ -0,0 +1,60 @@
package de.hft.geotime.controllers;
import de.hft.geotime.entities.RecordType;
import de.hft.geotime.entities.TimeRecord;
import de.hft.geotime.entities.TimetrackAccount;
import de.hft.geotime.repositories.RecordRepository;
import de.hft.geotime.repositories.TimetrackAccountRepository;
import org.springframework.data.domain.Page;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.time.LocalDateTime;
import java.util.Optional;
@RestController
public class RecordController {
private final RecordRepository recordRepository;
private final TimetrackAccountRepository accountRepository;
public RecordController(RecordRepository recordRepository, TimetrackAccountRepository accountRepository) {
this.recordRepository = recordRepository;
this.accountRepository = accountRepository;
}
@GetMapping("/track")
public ResponseEntity<TimeRecord> track(@RequestParam String account, Authentication authentication) {
if (account == null || account.isEmpty()) {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
TimetrackAccount selectedAccount = accountRepository.findByUser_UsernameAndName(authentication.getName(), account);
if (selectedAccount == null) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
Page<TimeRecord> entires = recordRepository.findAllByEnddateIsNull(null);
Optional<TimeRecord> collect = entires.stream()
.filter(timeRecord -> timeRecord.getAccount().equals(selectedAccount))
.findFirst();
if (collect.isPresent()) {
collect.get().setEnddate(LocalDateTime.now());
recordRepository.save(collect.get());
return new ResponseEntity<>(collect.get(), HttpStatus.OK);
} else {
TimeRecord newRecord = new TimeRecord();
newRecord.setType(RecordType.PAID);
newRecord.setStartdate(LocalDateTime.now());
newRecord.setAccount(accountRepository.findByUser_UsernameAndName(authentication.getName(), account));
recordRepository.save(newRecord);
return new ResponseEntity<>(newRecord, HttpStatus.CREATED);
}
}
}

View File

@ -5,8 +5,8 @@ import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import javax.persistence.*; import javax.persistence.*;
import java.time.Duration; import java.time.LocalDateTime;
import java.util.Date; import java.time.temporal.ChronoUnit;
@Data @Data
@NoArgsConstructor @NoArgsConstructor
@ -17,11 +17,24 @@ public class TimeRecord {
@Id @Id
@GeneratedValue(strategy = GenerationType.IDENTITY) @GeneratedValue(strategy = GenerationType.IDENTITY)
private long id; private long id;
@OneToOne(fetch = FetchType.LAZY)
@ManyToOne
private TimetrackAccount account; private TimetrackAccount account;
private Date startdate;
private Date enddate; @Column(columnDefinition = "TIMESTAMP")
private Duration time; private LocalDateTime startdate;
@Column(columnDefinition = "TIMESTAMP")
private LocalDateTime enddate;
private RecordType type; private RecordType type;
public long getDuration() {
if (enddate == null) {
return 0;
} else {
return startdate.until(enddate, ChronoUnit.MINUTES);
}
}
} }

View File

@ -1,5 +1,6 @@
package de.hft.geotime.entities; package de.hft.geotime.entities;
import com.fasterxml.jackson.annotation.JsonBackReference;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
@ -18,6 +19,7 @@ public class TimetrackAccount {
private double revenue; private double revenue;
private String name; private String name;
private String description; private String description;
@ManyToOne @ManyToOne
private TimetrackUser user; private TimetrackUser user;

View File

@ -1,6 +1,7 @@
package de.hft.geotime.entities; package de.hft.geotime.entities;
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonManagedReference;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
@ -28,12 +29,9 @@ public class TimetrackUser {
private String lastname; private String lastname;
@OneToOne(fetch = FetchType.EAGER) @ManyToOne(fetch = FetchType.EAGER)
private Role role; private Role role;
@OneToMany(mappedBy = "user", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private List<TimetrackAccount> accounts;
@ManyToOne @ManyToOne
private Location location; private Location location;

View File

@ -0,0 +1,27 @@
package de.hft.geotime.entities.projections;
import de.hft.geotime.entities.TimeRecord;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.rest.core.config.Projection;
import java.time.LocalDateTime;
@Projection(name = "overview", types = TimeRecord.class)
public interface RecordOverviewProjection {
LocalDateTime getStartdate();
LocalDateTime getEnddate();
long getDuration();
@Value("#{target.type.name()}")
String getType();
@Value("#{target.account.name}")
String getAccount();
@Value("#{target.account.user.username}")
String getUsername();
}

View File

@ -1,11 +1,15 @@
package de.hft.geotime.repositories; package de.hft.geotime.repositories;
import de.hft.geotime.entities.TimeRecord; import de.hft.geotime.entities.TimeRecord;
import org.springframework.data.repository.CrudRepository; import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.rest.core.annotation.RepositoryRestResource; import org.springframework.data.rest.core.annotation.RepositoryRestResource;
import org.springframework.data.rest.core.annotation.RestResource;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.List; import java.time.LocalDateTime;
@RepositoryRestResource( @RepositoryRestResource(
path = "records", path = "records",
@ -14,4 +18,37 @@ import java.util.List;
) )
public interface RecordRepository extends PagingAndSortingRepository<TimeRecord, Long> { public interface RecordRepository extends PagingAndSortingRepository<TimeRecord, Long> {
@RestResource(rel = "allBetween", path = "allBetween")
Page<TimeRecord> findAllByStartdateBetween(
@DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss") LocalDateTime start,
@DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss") LocalDateTime end,
Pageable pageable
);
@RestResource(rel = "allBetweenAndUser", path = "allBetweenAndUser")
Page<TimeRecord> findAllByStartdateBetweenAndAccount_User_Username(
LocalDateTime start,
LocalDateTime end,
String username,
Pageable pageable
);
@RestResource(rel = "allForUser", path = "allForUser")
Page<TimeRecord> findAllByAccount_User_Username(String username, Pageable pageable);
@RestResource(rel = "allForUserAndAccount", path = "allForUserAndAccount")
Page<TimeRecord> findAllByAccount_User_UsernameAndAccount_Name(String username, String account, Pageable pageable);
@RestResource(rel = "allFrom", path = "allFrom")
Page<TimeRecord> findAllByStartdateGreaterThanEqual(
@DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss") LocalDateTime date,
Pageable pageable
);
@Query("SELECT record from TimeRecord record where record.account.user.username = :#{principal} AND record.startdate > (current_date-1)")
Page<TimeRecord> today(Pageable pageable);
@RestResource(rel = "openEntries", path = "openEntries")
Page<TimeRecord> findAllByEnddateIsNull(Pageable pageable);
} }

View File

@ -3,6 +3,7 @@ package de.hft.geotime.repositories;
import de.hft.geotime.entities.TimetrackAccount; import de.hft.geotime.entities.TimetrackAccount;
import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.rest.core.annotation.RepositoryRestResource; import org.springframework.data.rest.core.annotation.RepositoryRestResource;
import org.springframework.data.rest.core.annotation.RestResource;
@RepositoryRestResource( @RepositoryRestResource(
path = "accounts", path = "accounts",
@ -11,4 +12,7 @@ import org.springframework.data.rest.core.annotation.RepositoryRestResource;
) )
public interface TimetrackAccountRepository extends PagingAndSortingRepository<TimetrackAccount, Long> { public interface TimetrackAccountRepository extends PagingAndSortingRepository<TimetrackAccount, Long> {
@RestResource(rel = "findByUsernameAndName", path = "findByUsernameAndName")
TimetrackAccount findByUser_UsernameAndName(String username, String account);
} }

View File

@ -0,0 +1,14 @@
package de.hft.geotime.security;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.spel.spi.EvaluationContextExtension;
@Configuration
class SecurityConfiguration {
@Bean
EvaluationContextExtension securityExtension() {
return new SecurityEvaluationContextExtension();
}
}

View File

@ -0,0 +1,21 @@
package de.hft.geotime.security;
import org.springframework.data.spel.spi.EvaluationContextExtension;
import org.springframework.security.access.expression.SecurityExpressionRoot;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
public class SecurityEvaluationContextExtension implements EvaluationContextExtension {
@Override
public String getExtensionId() {
return "security";
}
@Override
public SecurityExpressionRoot getRootObject() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
return new SecurityExpressionRoot(authentication) {
};
}
}

View File

@ -20,8 +20,15 @@ INSERT INTO timetrack_user (id, firstname, lastname, password, username, role_id
(3, 'Tim', 'Zieger' ,'$2y$10$pYGHZhoaelceImO7aIN4nOkWJBp.oqNGFYaRAonHkYF4u9ljqPelC', 'ziti', 1, 1), (3, 'Tim', 'Zieger' ,'$2y$10$pYGHZhoaelceImO7aIN4nOkWJBp.oqNGFYaRAonHkYF4u9ljqPelC', 'ziti', 1, 1),
(4, 'Simon', 'Kellner' ,'$2y$10$Puzm/Nr/Dyq3nQxlkXGIfubS5JPtXJSOf2e6mrQ6HhVYQN9YiQQsC', 'kesi', 1, 1); (4, 'Simon', 'Kellner' ,'$2y$10$Puzm/Nr/Dyq3nQxlkXGIfubS5JPtXJSOf2e6mrQ6HhVYQN9YiQQsC', 'kesi', 1, 1);
INSERT INTO timetrack_account (description, `name`, revenue, user_id) VALUES INSERT INTO timetrack_account (id, description, `name`, revenue, user_id) VALUES
('Gleitzeit Marcel', 'Primary Marcel', 16.0, 1), (1, 'Gleitzeit Marcel', 'Primary', 16.0, 1),
('Festgeld Marcel', 'Secondary Marcel', 25.0, 1); (2, 'Festgeld Marcel', 'Secondary', 25.0, 1),
(3, 'Festgeld Tim', 'Primary', 25.0, 3);
INSERT INTO time_record (id, enddate, startdate, `type`, account_id) VALUES
(1, '2020-05-10 16:00:00', '2020-05-10 12:00:00', 0, 1),
(2, '2020-05-09 16:00:00', '2020-05-09 12:00:00', 1, 1),
(3, '2020-05-20 16:00:00', '2020-05-20 00:00:00', 1, 2),
(4, '2020-05-11 16:00:00', '2020-05-11 12:00:00', 1, 3);
SET FOREIGN_KEY_CHECKS=1; SET FOREIGN_KEY_CHECKS=1;