Sync Creations with Server

This commit is contained in:
Fawkes100 2025-01-06 20:38:23 +01:00
parent 71568a200f
commit 3428e0ce1b
13 changed files with 169 additions and 234 deletions

View File

@ -3,12 +3,16 @@ package core.notevault.data;
import androidx.room.Entity;
import androidx.room.Ignore;
import androidx.room.PrimaryKey;
import core.notevault.data.sync.SyncStatus;
@Entity(tableName = "music_notes")
public class MusicNote {
@PrimaryKey(autoGenerate = true)
private long musicNoteId;
private String serverID;
private SyncStatus syncStatus;
private String title;
private String composer;
private int year;
@ -20,16 +24,16 @@ public class MusicNote {
this.composer = composer;
this.year = year;
this.genre = genre;
this.syncStatus = SyncStatus.CREATED;
}
public MusicNote(long musicNoteId, String title) {
this.musicNoteId = musicNoteId;
this.title = title;
this.syncStatus = SyncStatus.CREATED;
}
@Ignore
public MusicNote() {
}
@ -72,4 +76,20 @@ public class MusicNote {
public void setYear(int year) {
this.year = year;
}
public String getServerID() {
return serverID;
}
public void setServerID(String serverID) {
this.serverID = serverID;
}
public SyncStatus getSyncStatus() {
return syncStatus;
}
public void setSyncStatus(SyncStatus syncStatus) {
this.syncStatus = syncStatus;
}
}

View File

@ -25,6 +25,9 @@ public interface MusicNoteDAO {
@Query("SELECT * FROM concerts WHERE syncStatus = :status")
List<Concert> getConcertsWithSyncStatus(SyncStatus status);
@Query("SELECT * FROM music_notes WHERE syncStatus = :status")
List<MusicNote> getSongsWithSyncStatus(SyncStatus status);
@Insert
void insertConcert(Concert concert);

View File

@ -12,6 +12,8 @@ import core.notevault.data.sync.SyncObject;
import core.notevault.data.sync.SyncResponse;
import core.notevault.data.sync.SyncStatus;
import core.notevault.sync.synchronisation.*;
import core.notevault.sync.synchronisation.songs.SongSyncWorker;
import core.notevault.sync.synchronisation.songs.creation.SongCreationBatchResponse;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
@ -24,104 +26,24 @@ import java.util.concurrent.CountDownLatch;
import java.util.stream.Collectors;
public class SyncWorker extends Worker{
private SyncService syncService;
private MusicNoteDAO database;
private SongSyncWorker songSyncWorker;
public SyncWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
super(context, workerParams);
this.syncService = ApiClient.getRetrofitInstance(context).create(SyncService.class);
SyncService syncService = ApiClient.getRetrofitInstance(context).create(SyncService.class);
MusicDatabase musicDatabase = MusicDatabase.getDatabase(context);
database = musicDatabase.musicNoteDao();
this.songSyncWorker = new SongSyncWorker(syncService, musicDatabase.musicNoteDao(), context);
}
@NonNull
@Override
public Result doWork() {
SyncRequest concertSync = buildConcertSyncRequest();
final CountDownLatch latch = new CountDownLatch(1);
final Result[] result = {Result.failure()};
syncWithServer(concertSync, new Callback<SyncResponseModel>() {
@Override
public void onResponse(Call<SyncResponseModel> call, Response<SyncResponseModel> response) {
if(response.isSuccessful()) {
result[0] = Result.success();
Log.d("SyncWorker", "Sync finished, process now");
processConcertSynResponse(response.body());
} else {
Log.d("SyncWorker", "Sync failed");
if (response.errorBody() != null) {
try {
String errorBody = response.errorBody().string();
Log.d("SyncWorker", "Error Body: " + errorBody);
} catch (IOException e) {
e.printStackTrace();
}
}
result[0] = Result.failure();
}
latch.countDown();
}
@Override
public void onFailure(Call<SyncResponseModel> call, Throwable throwable) {
result[0] = Result.failure();
Log.d("SyncWorker", "Sync failure");
latch.countDown();
}
});
try {
latch.await();
} catch (InterruptedException e) {
// throw new RuntimeException(e);
songSyncWorker.syncSongCreations();
return Result.success();
} catch (Exception e) {
return Result.failure();
}
return result[0];
}
private SyncRequest buildConcertSyncRequest() {
List<Concert> createdConcerts = database.getConcertsWithSyncStatus(SyncStatus.CREATED);
List<Concert> deletedConcerts = database.getConcertsWithSyncStatus(SyncStatus.DELETED);
List<Concert> modifiedConcerts = database.getConcertsWithSyncStatus(SyncStatus.MODIFIED);
List<Integer> deviceIDsCreatedConcerts = createdConcerts.stream().map(Concert::getId).collect(Collectors.toList());
List<SyncChangeRequest> modifiedSyncRequests = modifiedConcerts.stream().map(concert ->
new SyncChangeRequest(concert.getServerSpecificConcertID(), concert.getLastModified())).collect(Collectors.toList());
List<SyncChangeRequest> deletedSyncRequests = deletedConcerts.stream().map(concert ->
new SyncChangeRequest(concert.getServerSpecificConcertID(), concert.getLastModified())).collect(Collectors.toList());
Log.d("SyncWorker", "Build SyncRequest");
// Datenbankabfrage: Nur IDs mit Zustand "Modified" oder "Deleted" abrufen
// Dies ist abhängig von der spezifischen Implementierung der Datenbank
return new SyncRequest(database.getLatestSyncTimestamp(), deletedSyncRequests, modifiedSyncRequests, deviceIDsCreatedConcerts);
}
private void syncWithServer(SyncRequest syncRequest, Callback<SyncResponseModel> callback) {
syncService.syncConcerts(syncRequest).enqueue(callback);
}
private void processConcertSynResponse(SyncResponseModel syncResponse) {
Log.d("SyncWorker", "Process SyncResponse");
//Assign local Concert entries the provided Server ID
List<SyncResponse> syncResponses = new ArrayList<>();
for(SyncCreateResponse createResponse : syncResponse.getCreateResponses()) {
database.updateConcertServerUUID(createResponse.getServerObjectUUID(), createResponse.getDeviceSpecificObjectID());
syncResponses.add(new SyncResponse(createResponse.getServerObjectUUID(), SyncAction.TOBEUPLOADED, SyncObject.CONCERT));
}
//Delete Concerts that the server has marked as deleted
database.deleteConcerts(syncResponse.getToBeDeleted());
//Persist SyncResponse
for(String toBeDownloaded : syncResponse.getToBeDownloaded()) {
syncResponses.add(new SyncResponse(toBeDownloaded, SyncAction.TOBEDOWNLOADED, SyncObject.CONCERT));
}
for(String toBeUploaded : syncResponse.getToBeUploaded()) {
syncResponses.add(new SyncResponse(toBeUploaded, SyncAction.TOBEUPLOADED, SyncObject.CONCERT));
}
database.insertSyncResponses(syncResponses);
}
}

View File

@ -1,29 +0,0 @@
package core.notevault.sync.synchronisation;
import java.time.LocalDateTime;
public class SyncChangeRequest {
private String serverUUID;
private LocalDateTime lastModified;
public SyncChangeRequest(String serverUUID, LocalDateTime lastModified) {
this.serverUUID = serverUUID;
this.lastModified = lastModified;
}
public String getServerUUID() {
return serverUUID;
}
public void setServerUUID(String serverUUID) {
this.serverUUID = serverUUID;
}
public LocalDateTime getLastModified() {
return lastModified;
}
public void setLastModified(LocalDateTime lastModified) {
this.lastModified = lastModified;
}
}

View File

@ -1,22 +0,0 @@
package core.notevault.sync.synchronisation;
public class SyncCreateResponse {
private String serverObjectUUID;
private int deviceSpecificObjectID;
public String getServerObjectUUID() {
return serverObjectUUID;
}
public void setServerObjectUUID(String serverObjectUUID) {
this.serverObjectUUID = serverObjectUUID;
}
public int getDeviceSpecificObjectID() {
return deviceSpecificObjectID;
}
public void setDeviceSpecificObjectID(int deviceSpecificObjectID) {
this.deviceSpecificObjectID = deviceSpecificObjectID;
}
}

View File

@ -1,50 +0,0 @@
package core.notevault.sync.synchronisation;
import java.time.LocalDateTime;
import java.util.List;
public class SyncRequest {
private LocalDateTime lastSync;
private List<SyncChangeRequest> deletedConcerts;
private List<SyncChangeRequest> modifiedConcerts;
private List<Integer> createdConcerts;
public SyncRequest(LocalDateTime lastSync, List<SyncChangeRequest> deletedConcerts, List<SyncChangeRequest> modifiedConcerts, List<Integer> createdConcerts) {
this.lastSync = lastSync;
this.deletedConcerts = deletedConcerts;
this.modifiedConcerts = modifiedConcerts;
this.createdConcerts = createdConcerts;
}
public LocalDateTime getLastSync() {
return lastSync;
}
public void setLastSync(LocalDateTime lastSync) {
this.lastSync = lastSync;
}
public List<SyncChangeRequest> getDeletedConcerts() {
return deletedConcerts;
}
public void setDeletedConcerts(List<SyncChangeRequest> deletedConcerts) {
this.deletedConcerts = deletedConcerts;
}
public List<SyncChangeRequest> getModifiedConcerts() {
return modifiedConcerts;
}
public void setModifiedConcerts(List<SyncChangeRequest> modifiedConcerts) {
this.modifiedConcerts = modifiedConcerts;
}
public List<Integer> getCreatedConcerts() {
return createdConcerts;
}
public void setCreatedConcerts(List<Integer> createdConcerts) {
this.createdConcerts = createdConcerts;
}
}

View File

@ -1,42 +0,0 @@
package core.notevault.sync.synchronisation;
import java.util.List;
public class SyncResponseModel {
private List<String> toBeUploaded;
private List<String> toBeDownloaded;
private List<String> toBeDeleted;
private List<SyncCreateResponse> createResponses;
public List<String> getToBeUploaded() {
return toBeUploaded;
}
public void setToBeUploaded(List<String> toBeUploaded) {
this.toBeUploaded = toBeUploaded;
}
public List<String> getToBeDownloaded() {
return toBeDownloaded;
}
public void setToBeDownloaded(List<String> toBeDownloaded) {
this.toBeDownloaded = toBeDownloaded;
}
public List<String> getToBeDeleted() {
return toBeDeleted;
}
public void setToBeDeleted(List<String> toBeDeleted) {
this.toBeDeleted = toBeDeleted;
}
public List<SyncCreateResponse> getCreateResponses() {
return createResponses;
}
public void setCreateResponses(List<SyncCreateResponse> createResponses) {
this.createResponses = createResponses;
}
}

View File

@ -1,12 +1,14 @@
package core.notevault.sync.synchronisation;
import core.notevault.data.sync.SyncResponse;
import core.notevault.sync.synchronisation.songs.creation.SongCreationBatchRequest;
import core.notevault.sync.synchronisation.songs.creation.SongCreationBatchResponse;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.POST;
public interface SyncService {
@POST("api/v1/sync/concerts")
Call<SyncResponseModel> syncConcerts(@Body SyncRequest syncRequest);
@POST("/sync/songs/create")
Call<SongCreationBatchResponse> performSongCreation(@Body SongCreationBatchRequest syncRequest);
}

View File

@ -0,0 +1,57 @@
package core.notevault.sync.synchronisation.songs;
import android.content.Context;
import android.widget.Toast;
import core.notevault.data.MusicNote;
import core.notevault.data.MusicNoteDAO;
import core.notevault.data.sync.SyncStatus;
import core.notevault.sync.synchronisation.SyncService;
import core.notevault.sync.synchronisation.songs.creation.SongCreationBatchRequest;
import core.notevault.sync.synchronisation.songs.creation.SongCreationBatchResponse;
import core.notevault.sync.synchronisation.songs.creation.SongCreationRequest;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import java.util.ArrayList;
import java.util.List;
public class SongSyncWorker {
private SyncService syncService;
private MusicNoteDAO database;
private Context context;
public SongSyncWorker(SyncService syncService, MusicNoteDAO database, Context context) {
this.syncService = syncService;
this.database = database;
this.context = context;
}
public SongCreationBatchRequest buildCreationRequest() {
List<MusicNote> created_songs = database.getSongsWithSyncStatus(SyncStatus.CREATED);
List<SongCreationRequest> songCreationRequests = new ArrayList<>();
for(MusicNote musicNote : created_songs) {
songCreationRequests.add(new SongCreationRequest(musicNote.getMusicNoteId(), musicNote.getTitle(), musicNote.getComposer(), musicNote.getGenre(), musicNote.getYear()));
}
return new SongCreationBatchRequest(songCreationRequests);
}
public void syncSongCreations() {
SongCreationBatchRequest request = buildCreationRequest();
syncService.performSongCreation(request).enqueue(new Callback<SongCreationBatchResponse>() {
@Override
public void onResponse(Call<SongCreationBatchResponse> call, Response<SongCreationBatchResponse> response) {
if(response.isSuccessful() && response.body() != null) {
Toast.makeText(context, "Song creation synced: " + response.body().getSongs().size(), Toast.LENGTH_LONG).show();
} else {
Toast.makeText(context, "Song creation failed", Toast.LENGTH_LONG).show();
}
}
@Override
public void onFailure(Call<SongCreationBatchResponse> call, Throwable throwable) {
Toast.makeText(context, "Song creation failed", Toast.LENGTH_LONG).show();
}
});
}
}

View File

@ -0,0 +1,11 @@
package core.notevault.sync.synchronisation.songs.creation;
import java.util.List;
public class SongCreationBatchRequest {
private List<SongCreationRequest> songs;
public SongCreationBatchRequest(List<SongCreationRequest> songs) {
this.songs = songs;
}
}

View File

@ -0,0 +1,19 @@
package core.notevault.sync.synchronisation.songs.creation;
import java.util.List;
public class SongCreationBatchResponse {
private List<SongCreationResponse> songs;
public SongCreationBatchResponse(List<SongCreationResponse> songs) {
this.songs = songs;
}
public List<SongCreationResponse> getSongs() {
return songs;
}
public void setSongs(List<SongCreationResponse> songs) {
this.songs = songs;
}
}

View File

@ -0,0 +1,17 @@
package core.notevault.sync.synchronisation.songs.creation;
public class SongCreationRequest {
private long localID;
private String title;
private String composer;
private String genre;
private int year;
public SongCreationRequest(long localID, String title, String composer, String genre, int year) {
this.localID = localID;
this.title = title;
this.composer = composer;
this.genre = genre;
this.year = year;
}
}

View File

@ -0,0 +1,27 @@
package core.notevault.sync.synchronisation.songs.creation;
public class SongCreationResponse {
private int localID;
private String serverID;
public SongCreationResponse(int localID, String serverID) {
this.localID = localID;
this.serverID = serverID;
}
public int getLocalID() {
return localID;
}
public void setLocalID(int localID) {
this.localID = localID;
}
public String getServerID() {
return serverID;
}
public void setServerID(String serverID) {
this.serverID = serverID;
}
}