Skip to content

Commit

Permalink
#359 etags: first steps - board list and board
Browse files Browse the repository at this point in the history
  • Loading branch information
desperateCoder committed Nov 22, 2020
1 parent 4f461a1 commit 50eb37d
Show file tree
Hide file tree
Showing 9 changed files with 128 additions and 38 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package it.niedermann.nextcloud.deck.api;


import com.nextcloud.android.sso.api.ParsedResponse;

import java.util.List;

import io.reactivex.Observable;
Expand Down Expand Up @@ -33,6 +35,7 @@
public interface DeckAPI {

String MODIFIED_SINCE_HEADER = "If-Modified-Since";
String IF_NONE_MATCH = "If-None-Match";

// ### BOARDS
@POST("boards")
Expand All @@ -51,7 +54,7 @@ public interface DeckAPI {
Observable<FullBoard> restoreBoard(@Path("id") long id);

@GET("boards")
Observable<List<FullBoard>> getBoards(@Query ("details") boolean verbose, @Header(MODIFIED_SINCE_HEADER) String lastSync );
Observable<ParsedResponse<List<FullBoard>>> getBoards(@Query ("details") boolean verbose, @Header(MODIFIED_SINCE_HEADER) String lastSync, @Header(IF_NONE_MATCH) String eTag);


// ### Stacks
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,7 @@ protected static FullBoard parseBoard(JsonObject e) {
makeTraceableIfFails(() -> {
board.setTitle(getNullAsEmptyString(e.get("title")));
board.setColor(getNullAsEmptyString(e.get("color")));
board.setEtag(getNullAsEmptyString(e.get("ETag")));
board.setArchived(e.get("archived").getAsBoolean());

board.setLastModified(getTimestampFromLong(e.get("lastModified")));
Expand Down Expand Up @@ -396,6 +397,7 @@ protected static FullCard parseCard(JsonObject e) {
card.setDescription(getNullAsEmptyString(e.get("description")));
card.setStackId(e.get("stackId").getAsLong());
card.setType(getNullAsEmptyString(e.get("type")));
card.setEtag(getNullAsEmptyString(e.get("ETag")));
card.setLastModified(getTimestampFromLong(e.get("lastModified")));
card.setCreatedAt(getTimestampFromLong(e.get("createdAt")));
card.setDeletedAt(getTimestampFromLong(e.get("deletedAt")));
Expand Down Expand Up @@ -456,6 +458,7 @@ protected static Attachment parseAttachment(JsonObject e) {
a.setId(e.get("id").getAsLong());
a.setCardId(e.get("cardId").getAsLong());
a.setType(e.get("type").getAsString());
a.setEtag(getNullAsEmptyString(e.get("ETag")));
a.setData(e.get("data").getAsString());
a.setLastModified(getTimestampFromLong(e.get("lastModified")));
a.setCreatedAt(getTimestampFromLong(e.get("createdAt")));
Expand Down Expand Up @@ -571,33 +574,6 @@ private static int getColorAsInt(JsonObject element, String field) {
return Color.GRAY;
}

protected static List<Activity> parseActivity(JsonObject e) {
DeckLog.verbose(e.toString());
List<Activity> activityList = new ArrayList<>();

makeTraceableIfFails(() -> {
if (e.has("ocs")) {
JsonObject ocs = e.getAsJsonObject("ocs");
if (ocs.has("data")) {
JsonArray data = ocs.getAsJsonArray("data");
for (JsonElement activityJson : data) {
Activity activity = new Activity();
JsonObject activityObject = activityJson.getAsJsonObject();

activity.setId(activityObject.get("activity_id").getAsLong());
activity.setType(ActivityType.findByPath(getNullAsEmptyString(activityObject.get("icon"))).getId());
activity.setSubject(getNullAsEmptyString(activityObject.get("subject")));
activity.setCardId(activityObject.get("object_id").getAsLong());
activity.setLastModified(getTimestampFromString(activityObject.get("datetime")));

activityList.add(activity);
}
}
}
}, e);
return activityList;
}

protected static FullStack parseStack(JsonObject e) {
DeckLog.verbose(e.toString());
FullStack fullStack = new FullStack();
Expand All @@ -607,6 +583,7 @@ protected static FullStack parseStack(JsonObject e) {
stack.setTitle(getNullAsEmptyString(e.get("title")));
stack.setBoardId(e.get("boardId").getAsLong());
stack.setId(e.get("id").getAsLong());
stack.setEtag(getNullAsEmptyString(e.get("ETag")));
stack.setLastModified(getTimestampFromLong(e.get("lastModified")));
stack.setDeletedAt(getTimestampFromLong(e.get("deletedAt")));
if (e.has("order") && !e.get("order").isJsonNull()) {
Expand All @@ -627,6 +604,34 @@ protected static FullStack parseStack(JsonObject e) {
return fullStack;
}

protected static List<Activity> parseActivity(JsonObject e) {
DeckLog.verbose(e.toString());
List<Activity> activityList = new ArrayList<>();

makeTraceableIfFails(() -> {
if (e.has("ocs")) {
JsonObject ocs = e.getAsJsonObject("ocs");
if (ocs.has("data")) {
JsonArray data = ocs.getAsJsonArray("data");
for (JsonElement activityJson : data) {
Activity activity = new Activity();
JsonObject activityObject = activityJson.getAsJsonObject();

activity.setId(activityObject.get("activity_id").getAsLong());
activity.setType(ActivityType.findByPath(getNullAsEmptyString(activityObject.get("icon"))).getId());
activity.setSubject(getNullAsEmptyString(activityObject.get("subject")));
activity.setCardId(activityObject.get("object_id").getAsLong());
activity.setEtag(getNullAsEmptyString(e.get("ETag")));
activity.setLastModified(getTimestampFromString(activityObject.get("datetime")));

activityList.add(activity);
}
}
}
}, e);
return activityList;
}

protected static Label parseLabel(JsonObject e) {
DeckLog.verbose(e.toString());
Label label = new Label();
Expand All @@ -635,13 +640,14 @@ protected static Label parseLabel(JsonObject e) {
//todo: last modified!
// label.setLastModified(get);
label.setTitle(getNullAsEmptyString(e.get("title")));
label.setEtag(getNullAsEmptyString(e.get("ETag")));
label.setColor(getColorAsInt(e, "color"));
}, e);
return label;
}

private static String getNullAsEmptyString(JsonElement jsonElement) {
return jsonElement.isJsonNull() ? "" : jsonElement.getAsString();
return jsonElement == null || jsonElement.isJsonNull() ? "" : jsonElement.getAsString();
}

private static Instant getTimestampFromString(JsonElement jsonElement) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ public class Account implements Serializable {
private boolean maintenanceEnabled = false;

private String etag;
private String boardsEtag;

@Ignore
public Account(Long id, @NonNull String name, @NonNull String userName, @NonNull String url) {
Expand Down Expand Up @@ -190,6 +191,14 @@ public void setEtag(String etag) {
this.etag = etag;
}

public String getBoardsEtag() {
return boardsEtag;
}

public void setBoardsEtag(String boardsEtag) {
this.boardsEtag = boardsEtag;
}

/**
* A cache buster parameter is added for duplicate account names on different hosts which shall be fetched from the same {@link SingleSignOnAccount} (e. g. {@link AccountSwitcherDialog})
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ public abstract class AbstractRemoteEntity implements IRemoteEntity {
protected Instant lastModified;
protected Instant lastModifiedLocal;

protected String etag;

public AbstractRemoteEntity() {
}

Expand Down Expand Up @@ -137,6 +139,15 @@ public void setStatusEnum(DBStatus status) {
this.status = status.getId();
}

@Override
public String getEtag() {
return etag;
}

@Override
public void setEtag(String etag) {
this.etag = etag;
}

@Override
public boolean equals(Object o) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,14 @@ default void setStatusEnum(DBStatus status) {
getEntity().setStatusEnum(status);
}

default String getEtag() {
return getEntity().getEtag();
}

default void setEtag(String etag) {
getEntity().setEtag(etag);
}

default <T> List<T> copyList(List<T> listToCopy) {
if (listToCopy == null) {
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,9 @@ private String getLastSyncDateFormatted(long accountId) {
// return lastSyncHeader;
}

public void getBoards(IResponseCallback<List<FullBoard>> responseCallback) {
public void getBoards(IResponseCallback<ParsedResponse<List<FullBoard>>> responseCallback) {
RequestHelper.request(provider, () ->
provider.getDeckAPI().getBoards(true, getLastSyncDateFormatted(responseCallback.getAccount().getId())),
provider.getDeckAPI().getBoards(true, getLastSyncDateFormatted(responseCallback.getAccount().getId()), responseCallback.getAccount().getBoardsEtag()),
responseCallback);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@
UserInBoard.class,
},
exportSchema = false,
version = 22
version = 23
)
@TypeConverters({DateTypeConverter.class})
public abstract class DeckDatabase extends RoomDatabase {
Expand Down Expand Up @@ -375,6 +375,25 @@ public void migrate(SupportSQLiteDatabase database) {
}
};

private static final Migration MIGRATION_22_23 = new Migration(22, 23) {
@Override
public void migrate(SupportSQLiteDatabase database) {
// https://github.com/stefan-niedermann/nextcloud-deck/issues/359
database.execSQL("ALTER TABLE `Account` ADD `boardsEtag` TEXT");
database.execSQL("ALTER TABLE `Board` ADD `etag` TEXT");
database.execSQL("ALTER TABLE `Stack` ADD `etag` TEXT");
database.execSQL("ALTER TABLE `Card` ADD `etag` TEXT");
database.execSQL("ALTER TABLE `Label` ADD `etag` TEXT");
database.execSQL("ALTER TABLE `AccessControl` ADD `etag` TEXT");
database.execSQL("ALTER TABLE `Attachment` ADD `etag` TEXT");
database.execSQL("ALTER TABLE `User` ADD `etag` TEXT");
database.execSQL("ALTER TABLE `DeckComment` ADD `etag` TEXT");
database.execSQL("ALTER TABLE `Activity` ADD `etag` TEXT");
database.execSQL("ALTER TABLE `OcsProject` ADD `etag` TEXT");
database.execSQL("ALTER TABLE `OcsProjectResource` ADD `etag` TEXT");
}
};

public static final RoomDatabase.Callback ON_CREATE_CALLBACK = new RoomDatabase.Callback() {
@Override
public void onCreate(@NonNull SupportSQLiteDatabase db) {
Expand Down Expand Up @@ -434,6 +453,7 @@ public void migrate(@NonNull SupportSQLiteDatabase database) {
lastSyncPref.apply();
}
})
.addMigrations(MIGRATION_22_23)
.fallbackToDestructiveMigration()
.addCallback(ON_CREATE_CALLBACK)
.build();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package it.niedermann.nextcloud.deck.persistence.sync.helpers;

import com.nextcloud.android.sso.exceptions.NextcloudHttpRequestFailedException;

import java.net.HttpURLConnection;
import java.time.Instant;
import java.util.List;
import java.util.concurrent.CountDownLatch;
Expand Down Expand Up @@ -53,9 +56,10 @@ public void onResponse(List<T> response) {
DeckLog.log("Conflicting changes on entity: " + existingEntity);
// TODO: what to do?
} else {
// if (existingEntity.getLastModified().getTime() == entityFromServer.getLastModified().getTime()) {
// continue; // TODO: is this is ok for sure? -> isn`t! NPE
// }
if (entityFromServer.getEtag() != null && entityFromServer.getEtag().equals(existingEntity.getEtag())) {
DeckLog.info("ETags do match! skipping this one.");
continue;
}
provider.updateInDB(dataBaseAdapter, accountId, applyUpdatesFromRemote(provider, existingEntity, entityFromServer, accountId), false);
}
}
Expand All @@ -73,8 +77,17 @@ public void onResponse(List<T> response) {

@Override
public void onError(Throwable throwable) {
super.onError(throwable);
if (throwable.getClass() == NextcloudHttpRequestFailedException.class) {
NextcloudHttpRequestFailedException requestFailedException = (NextcloudHttpRequestFailedException) throwable;
if (HttpURLConnection.HTTP_NOT_MODIFIED == requestFailedException.getStatusCode()){
DeckLog.info("ETags do match! skipping this one.");
// well, etags say we're fine here. no need to go deeper.
provider.childDone(provider, responseCallback, false);
return;
}
}
provider.onError(throwable, responseCallback);
DeckLog.logError(throwable);
responseCallback.onError(throwable);
}
}, lastSync);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package it.niedermann.nextcloud.deck.persistence.sync.helpers.providers;

import android.annotation.SuppressLint;

import com.nextcloud.android.sso.api.ParsedResponse;

import java.time.Instant;
import java.util.ArrayList;
import java.util.Collections;
Expand All @@ -26,8 +30,24 @@ public BoardDataProvider() {
}

@Override
public void getAllFromServer(ServerAdapter serverAdapter, long accountId, IResponseCallback<List<FullBoard>> responder, Instant lastSync) {
serverAdapter.getBoards(responder);
public void getAllFromServer(ServerAdapter serverAdapter, DataBaseAdapter dataBaseAdapter, long accountId, IResponseCallback<List<FullBoard>> responder, Instant lastSync) {
serverAdapter.getBoards(new IResponseCallback<ParsedResponse<List<FullBoard>>>(responder.getAccount()) {
@Override
public void onResponse(ParsedResponse<List<FullBoard>> response) {
String etag = response.getHeaders().get("ETag");
if (etag != null && !etag.equals(account.getBoardsEtag())) {
account.setBoardsEtag(etag);
dataBaseAdapter.updateAccount(account);
}
responder.onResponse(response.getResponse());
}

@SuppressLint("MissingSuperCall")
@Override
public void onError(Throwable throwable) {
responder.onError(throwable);
}
});
}

@Override
Expand Down

0 comments on commit 50eb37d

Please sign in to comment.