Change album ID to be unique, add internal ID
authorDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Fri, 24 Jul 2015 04:25:34 +0000 (06:25 +0200)
committerDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Fri, 24 Jul 2015 18:47:09 +0000 (20:47 +0200)
src/main/java/net/pterodactylus/sone/core/Core.java
src/main/java/net/pterodactylus/sone/data/Album.java
src/main/java/net/pterodactylus/sone/data/IdBuilder.java [new file with mode: 0644]
src/main/java/net/pterodactylus/sone/data/impl/AlbumImpl.java
src/main/java/net/pterodactylus/sone/web/CreateAlbumPage.java
src/main/java/net/pterodactylus/sone/web/DeleteAlbumPage.java
src/main/java/net/pterodactylus/sone/web/EditAlbumPage.java
src/main/java/net/pterodactylus/sone/web/UploadImagePage.java
src/main/resources/templates/insert/sone.xml
src/test/java/net/pterodactylus/sone/data/IdBuilderTest.java [new file with mode: 0644]
src/test/java/net/pterodactylus/sone/database/memory/MemoryDatabaseTest.java

index da209d8..8e00e23 100644 (file)
@@ -1514,7 +1514,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
                        int albumCounter = 0;
                        for (Album album : albums) {
                                String albumPrefix = sonePrefix + "/Albums/" + albumCounter++;
-                               configuration.getStringValue(albumPrefix + "/ID").setValue(album.getId());
+                               configuration.getStringValue(albumPrefix + "/ID").setValue(album.getInternalId());
                                configuration.getStringValue(albumPrefix + "/Title").setValue(album.getTitle());
                                configuration.getStringValue(albumPrefix + "/Description").setValue(album.getDescription());
                                configuration.getStringValue(albumPrefix + "/Parent").setValue(album.getParent().equals(sone.getRootAlbum()) ? null : album.getParent().getId());
@@ -1531,7 +1531,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
                                        }
                                        String imagePrefix = sonePrefix + "/Images/" + imageCounter++;
                                        configuration.getStringValue(imagePrefix + "/ID").setValue(image.getId());
-                                       configuration.getStringValue(imagePrefix + "/Album").setValue(album.getId());
+                                       configuration.getStringValue(imagePrefix + "/Album").setValue(album.getInternalId());
                                        configuration.getStringValue(imagePrefix + "/Key").setValue(image.getKey());
                                        configuration.getStringValue(imagePrefix + "/Title").setValue(image.getTitle());
                                        configuration.getStringValue(imagePrefix + "/Description").setValue(image.getDescription());
index c75088f..b4c6b0f 100644 (file)
@@ -107,6 +107,7 @@ public interface Album extends Identified, Fingerprintable {
         * @return The ID of this album
         */
        String getId();
+       String getInternalId();
 
        /**
         * Returns the Sone this album belongs to.
diff --git a/src/main/java/net/pterodactylus/sone/data/IdBuilder.java b/src/main/java/net/pterodactylus/sone/data/IdBuilder.java
new file mode 100644 (file)
index 0000000..f9accc1
--- /dev/null
@@ -0,0 +1,31 @@
+package net.pterodactylus.sone.data;
+
+import javax.annotation.Nonnull;
+import javax.annotation.concurrent.ThreadSafe;
+
+import com.google.common.base.Charsets;
+import com.google.common.hash.HashFunction;
+import com.google.common.hash.Hashing;
+
+/**
+ * Builds (practically) unique IDs by combining Sone and element IDs.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+@ThreadSafe
+public class IdBuilder {
+
+       private static final HashFunction HASH_FUNCTION = Hashing.sha256();
+       public static final int ID_STRING_LENGTH = HASH_FUNCTION.bits() / 4;
+
+       private final HashFunction sha256 = HASH_FUNCTION;
+
+       @Nonnull
+       public String buildId(@Nonnull String soneId, @Nonnull String id) {
+               return sha256.newHasher()
+                               .putBytes(soneId.getBytes(Charsets.UTF_8))
+                               .putBytes(id.getBytes(Charsets.UTF_8))
+                               .hash().toString();
+       }
+
+}
index 3488577..6a45187 100644 (file)
@@ -29,6 +29,7 @@ import java.util.Map;
 import java.util.UUID;
 
 import net.pterodactylus.sone.data.Album;
+import net.pterodactylus.sone.data.IdBuilder;
 import net.pterodactylus.sone.data.Image;
 import net.pterodactylus.sone.data.Sone;
 
@@ -46,6 +47,8 @@ import com.google.common.hash.Hashing;
  */
 public class AlbumImpl implements Album {
 
+       private final IdBuilder idBuilder = new IdBuilder();
+
        /** The ID of this album. */
        private final String id;
 
@@ -95,6 +98,11 @@ public class AlbumImpl implements Album {
 
        @Override
        public String getId() {
+               return idBuilder.buildId(sone.getId(), id);
+       }
+
+       @Override
+       public String getInternalId() {
                return id;
        }
 
index 1c599e4..e0f0725 100644 (file)
@@ -19,6 +19,7 @@ package net.pterodactylus.sone.web;
 
 import net.pterodactylus.sone.data.Album;
 import net.pterodactylus.sone.data.Album.Modifier.AlbumTitleMustNotBeEmpty;
+import net.pterodactylus.sone.data.IdBuilder;
 import net.pterodactylus.sone.data.Sone;
 import net.pterodactylus.sone.text.TextFilter;
 import net.pterodactylus.sone.web.page.FreenetRequest;
@@ -63,7 +64,7 @@ public class CreateAlbumPage extends SoneTemplatePage {
                        }
                        String description = request.getHttpRequest().getPartAsStringFailsafe("description", 256).trim();
                        Sone currentSone = getCurrentSone(request.getToadletContext());
-                       String parentId = request.getHttpRequest().getPartAsStringFailsafe("parent", 36);
+                       String parentId = request.getHttpRequest().getPartAsStringFailsafe("parent", IdBuilder.ID_STRING_LENGTH);
                        Album parent = webInterface.getCore().getAlbum(parentId);
                        if (parentId.equals("")) {
                                parent = currentSone.getRootAlbum();
index 98e8909..e588976 100644 (file)
@@ -18,6 +18,7 @@
 package net.pterodactylus.sone.web;
 
 import net.pterodactylus.sone.data.Album;
+import net.pterodactylus.sone.data.IdBuilder;
 import net.pterodactylus.sone.web.page.FreenetRequest;
 import net.pterodactylus.util.template.Template;
 import net.pterodactylus.util.template.TemplateContext;
@@ -49,7 +50,7 @@ public class DeleteAlbumPage extends SoneTemplatePage {
        protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
                super.processTemplate(request, templateContext);
                if (request.getMethod() == Method.POST) {
-                       String albumId = request.getHttpRequest().getPartAsStringFailsafe("album", 36);
+                       String albumId = request.getHttpRequest().getPartAsStringFailsafe("album", IdBuilder.ID_STRING_LENGTH);
                        Album album = webInterface.getCore().getAlbum(albumId);
                        if (album == null) {
                                throw new RedirectException("invalid.html");
index 66434e5..eca15de 100644 (file)
@@ -19,6 +19,7 @@ package net.pterodactylus.sone.web;
 
 import net.pterodactylus.sone.data.Album;
 import net.pterodactylus.sone.data.Album.Modifier.AlbumTitleMustNotBeEmpty;
+import net.pterodactylus.sone.data.IdBuilder;
 import net.pterodactylus.sone.text.TextFilter;
 import net.pterodactylus.sone.web.page.FreenetRequest;
 import net.pterodactylus.util.template.Template;
@@ -51,7 +52,7 @@ public class EditAlbumPage extends SoneTemplatePage {
        protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
                super.processTemplate(request, templateContext);
                if (request.getMethod() == Method.POST) {
-                       String albumId = request.getHttpRequest().getPartAsStringFailsafe("album", 36);
+                       String albumId = request.getHttpRequest().getPartAsStringFailsafe("album", IdBuilder.ID_STRING_LENGTH);
                        Album album = webInterface.getCore().getAlbum(albumId);
                        if (album == null) {
                                throw new RedirectException("invalid.html");
index a1e987a..2c7b0a9 100644 (file)
@@ -34,6 +34,7 @@ import javax.imageio.ImageReader;
 import javax.imageio.stream.ImageInputStream;
 
 import net.pterodactylus.sone.data.Album;
+import net.pterodactylus.sone.data.IdBuilder;
 import net.pterodactylus.sone.data.Image.Modifier.ImageTitleMustNotBeEmpty;
 import net.pterodactylus.sone.data.Sone;
 import net.pterodactylus.sone.data.TemporaryImage;
@@ -83,7 +84,7 @@ public class UploadImagePage extends SoneTemplatePage {
                super.processTemplate(request, templateContext);
                if (request.getMethod() == Method.POST) {
                        Sone currentSone = getCurrentSone(request.getToadletContext());
-                       String parentId = request.getHttpRequest().getPartAsStringFailsafe("parent", 36);
+                       String parentId = request.getHttpRequest().getPartAsStringFailsafe("parent", IdBuilder.ID_STRING_LENGTH);
                        Album parent = webInterface.getCore().getAlbum(parentId);
                        if (parent == null) {
                                throw new RedirectException("noPermission.html");
index 3d456d4..747ad51 100644 (file)
@@ -66,9 +66,9 @@
        <albums>
                <%/first>
                <album>
-                       <id><%album.id|xml></id>
+                       <id><%album.internalId|xml></id>
                        <%if !album.parent.root>
-                       <parent><%album.parent.id|xml></parent>
+                       <parent><%album.parent.internalId|xml></parent>
                        <%/if>
                        <title><%album.title|xml></title>
                        <description><%album.description|xml></description>
diff --git a/src/test/java/net/pterodactylus/sone/data/IdBuilderTest.java b/src/test/java/net/pterodactylus/sone/data/IdBuilderTest.java
new file mode 100644 (file)
index 0000000..bc8da11
--- /dev/null
@@ -0,0 +1,26 @@
+package net.pterodactylus.sone.data;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+
+import org.junit.Test;
+
+/**
+ * Unit test for {@link IdBuilderTest}.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class IdBuilderTest {
+
+       private static final String SONE_ID = "~Yp72VX0c6FLDvgIzip5wIvaGIIrjKcKvnX~pTaMKXs";
+       private static final String ELEMENT_ID = "88CC70AE-E853-4EEE-B245-E4C55F40DDDF";
+       private static final String EXPECTED_ID = "139a629a13f6a2c4191fb19ecead7e57335ea3deb2a971b88d5e004378c4daad";
+
+       private final IdBuilder idBuilder = new IdBuilder();
+
+       @Test
+       public void idBuilderBuildsCorrectIds() {
+               assertThat(idBuilder.buildId(SONE_ID, ELEMENT_ID), is(EXPECTED_ID));
+       }
+
+}
index cc5babb..c00f25a 100644 (file)
@@ -268,7 +268,7 @@ public class MemoryDatabaseTest {
 
        @Test
        public void testBasicAlbumFunctionality() {
-               Album newAlbum = new AlbumImpl(mock(Sone.class));
+               Album newAlbum = new AlbumImpl(when(mock(Sone.class).getId()).thenReturn(SONE_ID).<Sone>getMock());
                assertThat(memoryDatabase.getAlbum(newAlbum.getId()), is(Optional.<Album>absent()));
                memoryDatabase.storeAlbum(newAlbum);
                assertThat(memoryDatabase.getAlbum(newAlbum.getId()), is(of(newAlbum)));