This commit is contained in:
Francesco 2023-10-01 09:02:54 +02:00
parent c7258e9968
commit 5b027be0d1
10 changed files with 90 additions and 15 deletions

View File

@ -55,8 +55,8 @@ import tech.ailef.dbadmin.external.misc.Utils;
/** /**
* The main DbAdmin class responsible for the initialization phase. This class scans * The main DbAdmin class responsible for the initialization phase. This class scans
* the user provided package containing the `@Entity` definitions and tries to map each * the user provided package containing the {@code Entity} definitions and tries to map each
* entity to a DbObjectSchema instance. * entity to a {@link DbObjectSchema} instance.
* *
* This process involves determining the correct type for each class field and its * This process involves determining the correct type for each class field and its
* configuration at the database level. An exception will be thrown if it's not possible * configuration at the database level. An exception will be thrown if it's not possible

View File

@ -41,8 +41,10 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
import tech.ailef.dbadmin.internal.InternalDbAdminConfiguration; import tech.ailef.dbadmin.internal.InternalDbAdminConfiguration;
/** /**
* The configuration class that adds and configures the "internal" data source. * The configuration class for "internal" data source. This is not the
* * source connected to the user's data/entities, but rather an internal
* H2 database which is used by Spring Boot Database Admin to store user
* settings and other information like operations history.
*/ */
@ConditionalOnProperty(name = "dbadmin.enabled", matchIfMissing = true) @ConditionalOnProperty(name = "dbadmin.enabled", matchIfMissing = true)
@ComponentScan @ComponentScan

View File

@ -49,9 +49,9 @@ public class AutocompleteController {
/** /**
* Returns a list of entities from a given table that match an input query. * Returns a list of entities from a given table that match an input query.
* @param className * @param className full qualified class name; only search items for this entity
* @param query * @param query the query to search for
* @return * @return a list of {@link AutocompleteSearchResult}
*/ */
@GetMapping("/{className}") @GetMapping("/{className}")
public ResponseEntity<?> autocomplete(@PathVariable String className, @RequestParam String query) { public ResponseEntity<?> autocomplete(@PathVariable String className, @RequestParam String query) {

View File

@ -46,25 +46,30 @@ public class LogsSearchRequest implements FilterRequest {
private String itemId; private String itemId;
/** /**
* The requested page * The requested page number
*/ */
private int page; private int page;
/** /**
* The requested page size * The requested number of elements per page
*/ */
private int pageSize; private int pageSize;
/** /**
* The requested sort key * The requested sort key, possibly null
*/ */
private String sortKey; private String sortKey;
/** /**
* The requested sort order * The requested sort order, possibly null
*/ */
private String sortOrder; private String sortOrder;
/**
* Returns the table specified in this search request. If the value is blank or 'Any',
* the method returns null in order ignore the field in subsequent queries.
* @return
*/
public String getTable() { public String getTable() {
return table == null || table.isBlank() || table.equalsIgnoreCase("Any") ? null : table; return table == null || table.isBlank() || table.equalsIgnoreCase("Any") ? null : table;
} }
@ -73,6 +78,11 @@ public class LogsSearchRequest implements FilterRequest {
this.table = table; this.table = table;
} }
/**
* Returns the actionType specified in this search request. If the value is blank or 'Any',
* the method returns null in order ignore the field in subsequent queries.
* @return
*/
public String getActionType() { public String getActionType() {
return actionType == null || actionType.isBlank() || actionType.equalsIgnoreCase("Any") ? null : actionType; return actionType == null || actionType.isBlank() || actionType.equalsIgnoreCase("Any") ? null : actionType;
} }
@ -81,6 +91,11 @@ public class LogsSearchRequest implements FilterRequest {
this.actionType = actionType; this.actionType = actionType;
} }
/**
* Returns the itemId specified in this search request. If the value is blank or 'Any',
* the method returns null in order ignore the field in subsequent queries.
* @return
*/
public String getItemId() { public String getItemId() {
return itemId == null || itemId.isBlank() ? null : itemId; return itemId == null || itemId.isBlank() ? null : itemId;
} }
@ -89,6 +104,10 @@ public class LogsSearchRequest implements FilterRequest {
this.itemId = itemId; this.itemId = itemId;
} }
/**
* Returns the requested page number
* @return
*/
public int getPage() { public int getPage() {
return page; return page;
} }
@ -97,6 +116,10 @@ public class LogsSearchRequest implements FilterRequest {
this.page = page; this.page = page;
} }
/**
* Returns the requested number of elements per page
* @return
*/
public int getPageSize() { public int getPageSize() {
return pageSize; return pageSize;
} }
@ -105,6 +128,10 @@ public class LogsSearchRequest implements FilterRequest {
this.pageSize = pageSize; this.pageSize = pageSize;
} }
/**
* Returns the requested sort key, possibly null
* @return
*/
public String getSortKey() { public String getSortKey() {
return sortKey; return sortKey;
} }
@ -113,6 +140,11 @@ public class LogsSearchRequest implements FilterRequest {
this.sortKey = sortKey; this.sortKey = sortKey;
} }
/**
* Returns the requested sort order, possibly null
* @param sortOrder
*/
public String getSortOrder() { public String getSortOrder() {
return sortOrder; return sortOrder;
} }

View File

@ -23,6 +23,10 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
/**
* Configuration class for the "internal" data source. Place in the root "internal"
* package so as to allow component scanning and detection of models and repositories.
*/
@ConditionalOnProperty(name = "dbadmin.enabled", matchIfMissing = true) @ConditionalOnProperty(name = "dbadmin.enabled", matchIfMissing = true)
@ComponentScan @ComponentScan
@Configuration @Configuration

View File

@ -31,8 +31,9 @@ import jakarta.persistence.Id;
import jakarta.persistence.Lob; import jakarta.persistence.Lob;
/** /**
* An action executed by any user from the web UI. * An write operation executed by a user from the web UI. This class
* * only holds metadata about the operation and not anything
* concrete yet (e.g. a diff or SQL query) about what change was performed.
*/ */
@Entity @Entity
public class UserAction { public class UserAction {
@ -40,22 +41,39 @@ public class UserAction {
@GeneratedValue(strategy = GenerationType.IDENTITY) @GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id; private Integer id;
/**
* The time at which the operation was perfomed
*/
@Column(nullable = false) @Column(nullable = false)
private LocalDateTime createdAt; private LocalDateTime createdAt;
/**
* The SQL query generated by the operation.
* This field is here but it's NOT currently supported
*/
@Lob @Lob
@Column(nullable = false) @Column(nullable = false)
private String sql; private String sql;
/**
* The full qualified name of the Entity class this operation
* occurred on.
*/
@Column(nullable = false) @Column(nullable = false)
private String javaClass; private String javaClass;
/**
* The table name of the Entity class this operation occurred on.
*/
@Column(nullable = false) @Column(nullable = false)
private String onTable; private String onTable;
@Column(nullable = false) @Column(nullable = false)
private String primaryKey; private String primaryKey;
/**
* The action type (EDIT, CREATE or DELETE)
*/
@Column(nullable = false) @Column(nullable = false)
private String actionType; private String actionType;

View File

@ -23,7 +23,7 @@ import jakarta.persistence.Entity;
import jakarta.persistence.Id; import jakarta.persistence.Id;
/** /**
* A single variable in the settings. * A single variable in the user settings.
*/ */
@Entity @Entity
public class UserSetting { public class UserSetting {

View File

@ -0,0 +1,10 @@
/**
* This is the root package for the "internal" data source, i.e. not the data source
* that interacts with the user entities, but rather the one used internally by Spring
* Boot Database Admin in order to save information.
*
* Due to the way Spring Boot component scanning works, it is needed to create this package and the
* respective {@link tech.ailef.dbadmin.internal.InternalDbAdminConfiguration} in order to
* have the component scanning only pick the correct entities/repositories.
*/
package tech.ailef.dbadmin.internal;

View File

@ -92,7 +92,7 @@ public class CustomActionRepositoryImpl implements CustomActionRepository {
/** /**
* Returns the count that match the filtering parameters, used for pagination. * Returns the count that match the filtering parameters, used for pagination.
* @return * @return the number of user actions matching the filtering parameters
*/ */
@Override @Override
public long countActions(String table, String actionType, String itemId) { public long countActions(String table, String actionType, String itemId) {

View File

@ -33,6 +33,10 @@ import tech.ailef.dbadmin.internal.model.UserAction;
import tech.ailef.dbadmin.internal.repository.CustomActionRepositoryImpl; import tech.ailef.dbadmin.internal.repository.CustomActionRepositoryImpl;
import tech.ailef.dbadmin.internal.repository.UserActionRepository; import tech.ailef.dbadmin.internal.repository.UserActionRepository;
/**
* Service class to retrieve user actions through the {@link CustomActionRepositoryImpl}.
*
*/
@Service @Service
public class UserActionService { public class UserActionService {
@Autowired @Autowired
@ -46,6 +50,11 @@ public class UserActionService {
return repo.save(a); return repo.save(a);
} }
/**
* Retruns a page of results of user actions that match the given input request.
* @param request a request containing filtering parameters for user actions
* @return a page of results matching the input request
*/
public PaginatedResult<UserAction> findActions(LogsSearchRequest request) { public PaginatedResult<UserAction> findActions(LogsSearchRequest request) {
String table = request.getTable(); String table = request.getTable();
String actionType = request.getActionType(); String actionType = request.getActionType();