Implement custom record searches

Create record overview projection
Add today search with scoped principal
Add sample data for records
This commit is contained in:
Marcel Schwarz 2020-05-20 02:03:03 +02:00
parent 3310062138
commit 9f8fd0af1e
9 changed files with 160 additions and 13 deletions

View File

@ -0,0 +1,26 @@
package de.hft.geotime.controllers;
import de.hft.geotime.repositories.RecordRepository;
import de.hft.geotime.repositories.TimetrackUserRepository;
import org.springframework.data.projection.ProjectionFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class RecordController {
private final RecordRepository recordRepository;
private final TimetrackUserRepository userRepository;
private final ProjectionFactory projectionFactory;
public RecordController(RecordRepository recordRepository, TimetrackUserRepository userRepository, ProjectionFactory projectionFactory) {
this.recordRepository = recordRepository;
this.userRepository = userRepository;
this.projectionFactory = projectionFactory;
}
@GetMapping("/track")
public void track() {
//"/track?accountid=bla" start/stop recording for that account
}
}

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,20 @@ 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() {
return startdate.until(enddate, ChronoUnit.MINUTES);
}
} }

View File

@ -1,10 +1,12 @@
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;
import javax.persistence.*; import javax.persistence.*;
import java.util.List;
@Data @Data
@NoArgsConstructor @NoArgsConstructor
@ -18,7 +20,12 @@ public class TimetrackAccount {
private double revenue; private double revenue;
private String name; private String name;
private String description; private String description;
@JsonBackReference
@ManyToOne @ManyToOne
private TimetrackUser user; private TimetrackUser user;
@OneToMany(mappedBy = "account")
private List<TimeRecord> records;
} }

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,10 +29,11 @@ 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) @JsonManagedReference
@OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
private List<TimetrackAccount> accounts; private List<TimetrackAccount> accounts;
@ManyToOne @ManyToOne

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,34 @@ 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);
} }

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;