remake init data
json dump Small bug fix
This commit is contained in:
@@ -38,6 +38,10 @@ dependencies {
|
|||||||
|
|
||||||
implementation 'org.springframework.ai:spring-ai-zhipuai-spring-boot-starter:1.0.0-M6'
|
implementation 'org.springframework.ai:spring-ai-zhipuai-spring-boot-starter:1.0.0-M6'
|
||||||
implementation 'org.springframework:spring-mock:2.0.8'
|
implementation 'org.springframework:spring-mock:2.0.8'
|
||||||
|
|
||||||
|
implementation 'com.google.guava:guava:33.4.8-jre'
|
||||||
|
implementation 'io.micrometer:micrometer-core:1.12.0'
|
||||||
|
implementation 'org.springframework.boot:spring-boot-starter-actuator'
|
||||||
}
|
}
|
||||||
|
|
||||||
apply plugin: 'io.spring.dependency-management'
|
apply plugin: 'io.spring.dependency-management'
|
||||||
@@ -49,6 +53,7 @@ apply plugin: 'io.spring.dependency-management'
|
|||||||
|
|
||||||
apply plugin: 'java'
|
apply plugin: 'java'
|
||||||
|
|
||||||
|
|
||||||
tasks.register("bootRun_Dev") {
|
tasks.register("bootRun_Dev") {
|
||||||
group = "_jambotron_build"
|
group = "_jambotron_build"
|
||||||
description = "Runs the Spring Boot application with the dev profile"
|
description = "Runs the Spring Boot application with the dev profile"
|
||||||
|
|||||||
+32
-3
@@ -2,7 +2,7 @@
|
|||||||
<h2>JSON Dump Management</h2>
|
<h2>JSON Dump Management</h2>
|
||||||
|
|
||||||
<!-- Entity-specific Export/Import -->
|
<!-- Entity-specific Export/Import -->
|
||||||
<div class="entity-actions" *ngFor="let entity of entityTypes">
|
<!-- <div class="entity-actions" *ngFor="let entity of entityTypes">
|
||||||
<h3>{{ entity | titlecase }}</h3>
|
<h3>{{ entity | titlecase }}</h3>
|
||||||
<button mat-raised-button color="primary" (click)="exportEntity(entity)">
|
<button mat-raised-button color="primary" (click)="exportEntity(entity)">
|
||||||
<mat-icon>download</mat-icon> Export {{ entity }}
|
<mat-icon>download</mat-icon> Export {{ entity }}
|
||||||
@@ -11,8 +11,37 @@
|
|||||||
<mat-icon>upload</mat-icon> Import {{ entity }}
|
<mat-icon>upload</mat-icon> Import {{ entity }}
|
||||||
<input type="file" hidden accept=".json" (change)="onFileSelected($event, entity)">
|
<input type="file" hidden accept=".json" (change)="onFileSelected($event, entity)">
|
||||||
</label>
|
</label>
|
||||||
</div>
|
<button mat-icon-button (click)="restore(entity)" matTooltip="Restore from latest file">
|
||||||
|
<mat-icon>restore_from_trash</mat-icon>
|
||||||
|
</button>
|
||||||
|
<label mat-i color="accent">
|
||||||
|
<mat-icon>upload</mat-icon> Import {{ entity }}
|
||||||
|
<input type="file" hidden accept=".json" (change)="onFileSelected($event, entity)">
|
||||||
|
</label>
|
||||||
|
</div>-->
|
||||||
|
<mat-toolbar>
|
||||||
|
@for (entity of entityTypes; track entity) {
|
||||||
|
<mat-toolbar-row>
|
||||||
|
<mat-label>{{ entity | titlecase }}</mat-label>
|
||||||
|
<button mat-icon-button (click)="exportEntity(entity)" matTooltip="Export {{ entity }}">
|
||||||
|
<mat-icon>download</mat-icon>
|
||||||
|
</button>
|
||||||
|
<button mat-icon-button (click)="fileInput.click()" matTooltip=" Import {{ entity }}">
|
||||||
|
<mat-icon>restore_from_trash</mat-icon>
|
||||||
|
</button>
|
||||||
|
<button mat-icon-button (click)="restore(entity)" matTooltip="Restore from latest file">
|
||||||
|
<mat-icon>upload</mat-icon>
|
||||||
|
</button>
|
||||||
|
<input
|
||||||
|
type="file"
|
||||||
|
#fileInput
|
||||||
|
accept=".json"
|
||||||
|
(change)="onFileSelected($event, entity)"
|
||||||
|
hidden
|
||||||
|
/>
|
||||||
|
</mat-toolbar-row>
|
||||||
|
}
|
||||||
|
</mat-toolbar>
|
||||||
<!-- Files Table -->
|
<!-- Files Table -->
|
||||||
<h3>Available JSON Files</h3>
|
<h3>Available JSON Files</h3>
|
||||||
<mat-table [dataSource]="files">
|
<mat-table [dataSource]="files">
|
||||||
|
|||||||
+13
-2
@@ -17,6 +17,8 @@ import {MatProgressSpinner} from '@angular/material/progress-spinner';
|
|||||||
import {MatIcon} from '@angular/material/icon';
|
import {MatIcon} from '@angular/material/icon';
|
||||||
import {MatTooltip} from '@angular/material/tooltip';
|
import {MatTooltip} from '@angular/material/tooltip';
|
||||||
import {ArchivesResponse, FileInfo, FilesResponse, JsonDumpService} from '../../json-dump.service';
|
import {ArchivesResponse, FileInfo, FilesResponse, JsonDumpService} from '../../json-dump.service';
|
||||||
|
import {MatToolbar, MatToolbarRow} from '@angular/material/toolbar';
|
||||||
|
import {MatLabel} from '@angular/material/input';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-json-dump',
|
selector: 'app-json-dump',
|
||||||
@@ -40,7 +42,10 @@ import {ArchivesResponse, FileInfo, FilesResponse, JsonDumpService} from '../../
|
|||||||
MatTable,
|
MatTable,
|
||||||
TitleCasePipe,
|
TitleCasePipe,
|
||||||
NgIf,
|
NgIf,
|
||||||
NgForOf
|
NgForOf,
|
||||||
|
MatToolbar,
|
||||||
|
MatToolbarRow,
|
||||||
|
MatLabel
|
||||||
],
|
],
|
||||||
styleUrls: ['./json-dump.component.scss'],
|
styleUrls: ['./json-dump.component.scss'],
|
||||||
schemas:[CUSTOM_ELEMENTS_SCHEMA]
|
schemas:[CUSTOM_ELEMENTS_SCHEMA]
|
||||||
@@ -99,7 +104,9 @@ export class JsonDumpComponent implements OnInit {
|
|||||||
|
|
||||||
importEntity(entity: string, file: File): void {
|
importEntity(entity: string, file: File): void {
|
||||||
this.jsonDumpService.importEntity(entity, file).subscribe({
|
this.jsonDumpService.importEntity(entity, file).subscribe({
|
||||||
next: () => this.showMessage(`Imported ${entity} successfully`),
|
next: () => {
|
||||||
|
this.showMessage(`Imported ${entity} successfully`)
|
||||||
|
},
|
||||||
error: (err: { message: any; }) => this.showMessage(`Import ${entity} failed: ${err.message}`)
|
error: (err: { message: any; }) => this.showMessage(`Import ${entity} failed: ${err.message}`)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -132,4 +139,8 @@ export class JsonDumpComponent implements OnInit {
|
|||||||
private showMessage(msg: string): void {
|
private showMessage(msg: string): void {
|
||||||
this.snackBar.open(msg, 'Close', { duration: 3000 });
|
this.snackBar.open(msg, 'Close', { duration: 3000 });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
restore(fileName: string) {
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ export class JsonDumpService {
|
|||||||
importEntity(entity: string, file: File) {
|
importEntity(entity: string, file: File) {
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('file', file);
|
formData.append('file', file);
|
||||||
return this.http.post(`${this.baseUrl}/import/${entity}`, formData);
|
return this.http.post(`${this.baseUrl}/import/${entity}/file`, formData);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== EXPORT METHODS ====================
|
// ==================== EXPORT METHODS ====================
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
@use '@angular/material' as mat;
|
@use 'node_modules/@angular/material' as mat;
|
||||||
|
|
||||||
//markdown styles
|
//markdown styles
|
||||||
@import 'bootstrap/dist/css/bootstrap.min.css';
|
@import 'bootstrap/dist/css/bootstrap.min.css';
|
||||||
@@ -58,11 +58,22 @@ body {
|
|||||||
background: #fafafbb3;
|
background: #fafafbb3;
|
||||||
}
|
}
|
||||||
|
|
||||||
html {
|
//// Include the common styles for Angular Material
|
||||||
color-scheme: light dark;
|
//@include mat.core();
|
||||||
@include mat.theme((
|
//
|
||||||
color: mat.$azure-palette,
|
//// Define your custom palette
|
||||||
typography: Jersey 20 Charted,
|
//$my-primary: mat.define-palette(mat.$blue-palette, 700);
|
||||||
density: 0
|
//$my-accent: mat.define-palette(mat.$orange-palette, A200, A100, A400);
|
||||||
));
|
//$my-warn: mat.define-palette(mat.$red-palette);
|
||||||
}
|
//
|
||||||
|
//// Create the theme
|
||||||
|
//$my-theme: mat.define-light-theme((
|
||||||
|
// color: (
|
||||||
|
// primary: $my-primary,
|
||||||
|
// accent: $my-accent,
|
||||||
|
// warn: $my-warn,
|
||||||
|
// )
|
||||||
|
//));
|
||||||
|
//
|
||||||
|
//// Include theme styles for core and each component used in your app
|
||||||
|
//@include mat.all-component-themes($my-theme);
|
||||||
|
|||||||
@@ -74,27 +74,27 @@ public class JsonDumpController {
|
|||||||
|
|
||||||
// ==================== IMPORT ENDPOINTS ====================
|
// ==================== IMPORT ENDPOINTS ====================
|
||||||
|
|
||||||
@PostMapping("/initData/users")
|
@PostMapping("/restore/users")
|
||||||
public ResponseEntity<Map<String, Object>> importUsers() {
|
public ResponseEntity<Map<String, Object>> restoreUsers() {
|
||||||
return performImport(() -> _jsonDumpService.importUsers(), "users");
|
return performImport(() -> _jsonDumpService.importUsers(), "users");
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/initData/tutorials")
|
@PostMapping("/restore/tutorials")
|
||||||
public ResponseEntity<Map<String, Object>> importTutorials() {
|
public ResponseEntity<Map<String, Object>> restoreTutorials() {
|
||||||
return performImport(() -> _jsonDumpService.importTutorials(), "tutorials");
|
return performImport(() -> _jsonDumpService.importTutorials(), "tutorials");
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/initData/roles")
|
@PostMapping("/restore/roles")
|
||||||
public ResponseEntity<Map<String, Object>> importRoles() {
|
public ResponseEntity<Map<String, Object>> restoreRoles() {
|
||||||
return performImport(() -> _jsonDumpService.importRoles(), "roles");
|
return performImport(() -> _jsonDumpService.importRoles(), "roles");
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/initData/users/file")
|
@PostMapping("/import/users/file")
|
||||||
public ResponseEntity<Map<String, Object>> importUsersFromFile(@RequestParam("file") MultipartFile file) {
|
public ResponseEntity<Map<String, Object>> importUsersFromFile(@RequestParam("file") MultipartFile file) {
|
||||||
return importFromFile(file, (path) -> _jsonDumpService.importUsers(path), "users");
|
return importFromFile(file, (path) -> _jsonDumpService.importUsers(path), "users");
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/initData/tutorials/file")
|
@PostMapping("/import/tutorials/file")
|
||||||
public ResponseEntity<Map<String, Object>> importTutorialsFromFile(@RequestParam("file") MultipartFile file) {
|
public ResponseEntity<Map<String, Object>> importTutorialsFromFile(@RequestParam("file") MultipartFile file) {
|
||||||
return importFromFile(file, (path) -> _jsonDumpService.importTutorials(path), "tutorials");
|
return importFromFile(file, (path) -> _jsonDumpService.importTutorials(path), "tutorials");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -124,6 +124,8 @@ public class InitDataService {
|
|||||||
|
|
||||||
List<UserDto> items = readList(jsonFile, new TypeReference<List<UserDto>>() {});
|
List<UserDto> items = readList(jsonFile, new TypeReference<List<UserDto>>() {});
|
||||||
for (UserDto dto : items) {
|
for (UserDto dto : items) {
|
||||||
|
if(!_userRepository.existsByUsername(dto.getUsername())) {
|
||||||
|
|
||||||
User user = new User();
|
User user = new User();
|
||||||
if (dto.getId() != null) user.setId(dto.getId());
|
if (dto.getId() != null) user.setId(dto.getId());
|
||||||
user.setUsername(dto.getUsername());
|
user.setUsername(dto.getUsername());
|
||||||
@@ -149,17 +151,14 @@ public class InitDataService {
|
|||||||
}
|
}
|
||||||
user.setRoles(roles);
|
user.setRoles(roles);
|
||||||
|
|
||||||
if(!_userRepository.existsByUsername(user.getUsername())) {
|
|
||||||
_userRepository.save(user);
|
_userRepository.save(user);
|
||||||
|
|
||||||
_logger.info("Imported user: {} with roles: {}", user.getUsername(), user.getRoles().stream()
|
_logger.info("Imported user: {} with roles: {}", user.getUsername(), user.getRoles().stream()
|
||||||
.map(Role::getName).collect(Collectors.toList()).stream().map(ERole::toString)
|
.map(Role::getName).collect(Collectors.toList()).stream().map(ERole::toString)
|
||||||
.collect(Collectors.joining(", ")));
|
.collect(Collectors.joining(", ")));
|
||||||
} else {
|
} else {
|
||||||
_logger.warn("User with username {} already exists, SKIPPING import", user.getUsername());
|
_logger.warn("User with username {} already exists, SKIPPING import", dto.getUsername());
|
||||||
_logger.info("Imported user: {} with roles: {}", user.getUsername(), user.getRoles().stream()
|
|
||||||
.map(Role::getName).collect(Collectors.toList()).stream().map(ERole::toString)
|
|
||||||
.collect(Collectors.joining(", ")));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -167,6 +167,12 @@ public class JsonDumpService extends InitDataService {
|
|||||||
List<TutorialDto> items = super.readList(jsonFile, new TypeReference<List<TutorialDto>>() {});
|
List<TutorialDto> items = super.readList(jsonFile, new TypeReference<List<TutorialDto>>() {});
|
||||||
|
|
||||||
for (TutorialDto dto : items) {
|
for (TutorialDto dto : items) {
|
||||||
|
|
||||||
|
if(_tutorialRepository.findByUserIdAndTitle(dto.getUserID(), dto.getTitle()).size()> 0) {
|
||||||
|
_logger.warn("Tutorial with title [{}] already exists for userID [{}], SKIPPING import", dto.getTitle(), dto.getUserID());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
Tutorial t = new Tutorial();
|
Tutorial t = new Tutorial();
|
||||||
t.setId(dto.getId());
|
t.setId(dto.getId());
|
||||||
t.setTitle(dto.getTitle());
|
t.setTitle(dto.getTitle());
|
||||||
@@ -193,16 +199,17 @@ public class JsonDumpService extends InitDataService {
|
|||||||
} else {
|
} else {
|
||||||
throw new IllegalArgumentException("Missing user with ROLE_ADMIN to assign tutorial: " + dto.getTitle());
|
throw new IllegalArgumentException("Missing user with ROLE_ADMIN to assign tutorial: " + dto.getTitle());
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
User user = usersById.get(dto.getUserID());
|
User user = usersById.get(dto.getUserID());
|
||||||
//user = _entityManager.merge(user); // Ensure user is managed by EntityManager
|
//user = _entityManager.merge(user); // Ensure user is managed by EntityManager
|
||||||
//user.setRoles(null); // because we don't want to load roles to user object
|
//user.setRoles(null); // because we don't want to load roles to user object
|
||||||
t.setUser(user);
|
t.setUser(user);
|
||||||
|
|
||||||
if(_tutorialRepository.findByUserIdAndTitle(t.getUser().getId(), t.getTitle()).size()> 0) {
|
|
||||||
_logger.warn("Tutorial with title [{}] already exists for user [{}], SKIPPING import", t.getTitle(), t.getUser().getUsername());
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(_tutorialRepository.findByTitle(t.getTitle()).size() > 0) {
|
||||||
|
t.setTitle(t.getTitle() + " (imported) " + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
|
||||||
|
}
|
||||||
|
|
||||||
_tutorialRepository.saveAndFlush(t);
|
_tutorialRepository.saveAndFlush(t);
|
||||||
//_tutorialRepository.save(t);
|
//_tutorialRepository.save(t);
|
||||||
_logger.info("Imported tutorial: {} with user: {}", t.getTitle(), t.getUser().getUsername());
|
_logger.info("Imported tutorial: {} with user: {}", t.getTitle(), t.getUser().getUsername());
|
||||||
|
|||||||
@@ -17,4 +17,6 @@ public interface TutorialRepository extends JpaRepository<Tutorial, Long> {
|
|||||||
List<Tutorial> findBytobepublished(boolean tobepublished);
|
List<Tutorial> findBytobepublished(boolean tobepublished);
|
||||||
List<Tutorial> findByIdAndTobepublished(Long id,boolean tobepublished);
|
List<Tutorial> findByIdAndTobepublished(Long id,boolean tobepublished);
|
||||||
List<Tutorial> findByTitleContaining(String title);
|
List<Tutorial> findByTitleContaining(String title);
|
||||||
|
|
||||||
|
List<Tutorial> findByTitle(String title);
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user