Skip to content
This repository was archived by the owner on Jun 7, 2023. It is now read-only.

Commit 25f3c1a

Browse files
author
Rustam Aliyev
committed
Add support for custom label attributes. Closes #39
1 parent 39fb0e7 commit 25f3c1a

File tree

8 files changed

+315
-63
lines changed

8 files changed

+315
-63
lines changed

itests/src/test/java/com/elasticinbox/itests/RestV2IT.java

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,89 @@ public void labelListAddDeleteTest()
203203

204204
logger.info("Delete label test OK");
205205
}
206+
207+
@Test
208+
public void labelCustomAttributesAddUpdateDeleteTest()
209+
{
210+
initAccount();
211+
212+
final String labelA = "MyLabel";
213+
final String labelB = "MyAnotherLabel";
214+
215+
// add label with attributes
216+
Response response =
217+
given().
218+
pathParam("labelName", labelA).
219+
request().
220+
body("{\"name\" : \"" + labelB + "\", \"attributes\": { \"order\" : \"3\", \"color\" : \"red\"} }").
221+
contentType(ContentType.JSON).
222+
expect().
223+
statusCode(201).
224+
when().
225+
post(REST_PATH + "/mailbox/label?name={labelName}");
226+
227+
int labelId = with(response.asString()).getInt("id");
228+
229+
// check added label
230+
expect().
231+
statusCode(200).and().
232+
body("'" + labelId + "'.name", equalTo(labelB)).
233+
body("'" + labelId + "'.attributes.order", equalTo("3")).
234+
body("'" + labelId + "'.attributes.color", equalTo("red")).
235+
when().
236+
get(REST_PATH + "/mailbox?metadata=true");
237+
238+
// update label and delete some attributes
239+
given().
240+
pathParam("labelId", labelId).
241+
request().
242+
body("{\"name\" : \"" + labelA + "\", \"attributes\": { \"order\" : \"22\", \"icon\" : \"qutab.png\", \"color\" : \"\"} }").
243+
contentType(ContentType.JSON).
244+
expect().
245+
statusCode(204).
246+
when().
247+
put(REST_PATH + "/mailbox/label/{labelId}");
248+
249+
// check label updates
250+
expect().
251+
statusCode(200).and().
252+
body("'" + labelId + "'.name", equalTo(labelA)).
253+
body("'" + labelId + "'.attributes.order", equalTo("22")).
254+
body("'" + labelId + "'.attributes.icon", equalTo("qutab.png")).
255+
body("'" + labelId + "'.attributes", not(hasItems("color"))).
256+
when().
257+
get(REST_PATH + "/mailbox?metadata=true");
258+
259+
// delete attributes
260+
given().
261+
pathParam("labelId", labelId).
262+
request().
263+
body("{\"attributes\": { \"order\" : null, \"icon\" : \"\" } }").
264+
contentType(ContentType.JSON).
265+
expect().
266+
statusCode(204).
267+
when().
268+
put(REST_PATH + "/mailbox/label/{labelId}");
269+
270+
// check label updates
271+
expect().
272+
statusCode(200).and().
273+
body("'" + labelId + "'.attributes", not(hasItems("order", "icon", "color"))).
274+
when().
275+
get(REST_PATH + "/mailbox?metadata=true");
276+
277+
// add invalid attribute name
278+
given().
279+
pathParam("labelId", labelId).
280+
request().
281+
body("{\"attributes\": { \"new:attr\" : \"bad\"} }").
282+
contentType(ContentType.JSON).
283+
expect().
284+
statusCode(400).
285+
when().
286+
put(REST_PATH + "/mailbox/label/{labelId}");
287+
288+
}
206289

207290
@SuppressWarnings("unchecked")
208291
@Test

modules/core/src/main/java/com/elasticinbox/core/LabelDAO.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import java.io.IOException;
3232
import java.util.Map;
3333

34+
import com.elasticinbox.core.model.Label;
3435
import com.elasticinbox.core.model.LabelMap;
3536
import com.elasticinbox.core.model.Mailbox;
3637

@@ -69,7 +70,7 @@ public interface LabelDAO
6970
* @throws IOException
7071
* @throws IllegalLabelException
7172
*/
72-
public int add(Mailbox mailbox, String label) throws IOException, IllegalLabelException;
73+
public int add(Mailbox mailbox, Label label) throws IOException, IllegalLabelException;
7374

7475
/**
7576
* Rename existing label
@@ -82,7 +83,7 @@ public interface LabelDAO
8283
* @throws IOException
8384
* @throws IllegalLabelException
8485
*/
85-
public void rename(Mailbox mailbox, Integer labelId, String label) throws IOException, IllegalLabelException;
86+
public void update(Mailbox mailbox, Label label) throws IOException, IllegalLabelException;
8687

8788
/**
8889
* Delete existing label

modules/core/src/main/java/com/elasticinbox/core/cassandra/CassandraLabelDAO.java

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -96,17 +96,17 @@ public Map<Integer, String> getAll(final Mailbox mailbox) {
9696
}
9797

9898
@Override
99-
public int add(final Mailbox mailbox, String label)
99+
public int add(Mailbox mailbox, Label label)
100100
{
101-
Integer labelId;
102-
103101
// get all existing labels
104102
LabelMap existingLabels = AccountPersistence.getLabels(mailbox.getId());
105103

106-
LabelUtils.validateLabelName(label, existingLabels);
104+
LabelUtils.validateLabelName(label.getName(), existingLabels);
107105

108106
try {
109-
labelId = LabelUtils.getNewLabelId(existingLabels.getIds());
107+
// generate new label id
108+
int labelId = LabelUtils.getNewLabelId(existingLabels.getIds());
109+
label.setId(labelId);
110110
} catch (IllegalLabelException ile) {
111111
// log and rethrow
112112
logger.warn("{} reached max random label id attempts with {} labels",
@@ -115,47 +115,46 @@ public int add(final Mailbox mailbox, String label)
115115
}
116116

117117
// begin batch operation
118-
Mutator<String> m = createMutator(keyspace, strSe);
118+
Mutator<String> mutator = createMutator(keyspace, strSe);
119119

120120
// add new label
121-
AccountPersistence.setLabelName(m, mailbox.getId(), labelId, label);
121+
AccountPersistence.putLabel(mutator, mailbox.getId(), label);
122122

123123
// commit batch operation
124-
m.execute();
124+
mutator.execute();
125125

126-
return labelId;
126+
return label.getId();
127127
}
128128

129129
@Override
130-
public void rename(final Mailbox mailbox, final Integer labelId, String label)
131-
throws IOException
130+
public void update(Mailbox mailbox, Label label) throws IOException
132131
{
133132
// get all existing labels
134133
LabelMap existingLabels = AccountPersistence.getLabels(mailbox.getId());
135134

136135
// validate only if name is changed (skips letter case changes)
137-
if (!existingLabels.containsName(label)) {
138-
LabelUtils.validateLabelName(label, existingLabels);
136+
if (label.getName() != null && !existingLabels.containsName(label.getName())) {
137+
LabelUtils.validateLabelName(label.getName(), existingLabels);
139138
}
140139

141140
// check if label id reserved
142-
if(ReservedLabels.contains(labelId)) {
141+
if (ReservedLabels.contains(label.getId())) {
143142
throw new ExistingLabelException("This is reserved label and can't be modified");
144143
}
145144

146145
// check if label id exists
147-
if(!existingLabels.containsId(labelId)) {
146+
if (!existingLabels.containsId(label.getId())) {
148147
throw new IllegalLabelException("Label does not exist");
149148
}
150149

151150
// begin batch operation
152-
Mutator<String> m = createMutator(keyspace, strSe);
151+
Mutator<String> mutator = createMutator(keyspace, strSe);
153152

154153
// set new name
155-
AccountPersistence.setLabelName(m, mailbox.getId(), labelId, label);
154+
AccountPersistence.putLabel(mutator, mailbox.getId(), label);
156155

157156
// commit batch operation
158-
m.execute();
157+
mutator.execute();
159158
}
160159

161160
@Override

modules/core/src/main/java/com/elasticinbox/core/cassandra/persistence/AccountPersistence.java

Lines changed: 86 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,10 @@
3535
import java.io.IOException;
3636
import java.util.HashMap;
3737
import java.util.Map;
38+
import java.util.Map.Entry;
3839

3940
import com.elasticinbox.common.utils.Assert;
41+
import com.elasticinbox.core.IllegalLabelException;
4042
import com.elasticinbox.core.cassandra.CassandraDAOFactory;
4143
import com.elasticinbox.core.cassandra.utils.BatchConstants;
4244
import com.elasticinbox.core.model.Label;
@@ -80,7 +82,7 @@ public static Map<String, Object> getAll(final String mailbox)
8082
// execute
8183
QueryResult<ColumnSlice<String, byte[]>> r = q.execute();
8284

83-
// read message ids from the result
85+
// read attributes from the result
8486
Map<String, Object> attributes = new HashMap<String, Object>();
8587
for (HColumn<String, byte[]> c : r.get().getColumns()) {
8688
if( (c != null) && (c.getValue() != null)) {
@@ -99,10 +101,14 @@ public static Map<String, Object> getAll(final String mailbox)
99101
*/
100102
public static <V> void set(Mutator<String> mutator, final String mailbox, final Map<String, V> attributes)
101103
{
102-
for (Map.Entry<String, V> a : attributes.entrySet()) {
103-
mutator.addInsertion(mailbox, CF_ACCOUNTS,
104-
createColumn(a.getKey(), a.getValue(), strSe,
105-
SerializerTypeInferer.getSerializer(a.getValue())));
104+
if (!attributes.isEmpty())
105+
{
106+
for (Map.Entry<String, V> a : attributes.entrySet())
107+
{
108+
mutator.addInsertion(mailbox, CF_ACCOUNTS,
109+
createColumn(a.getKey(), a.getValue(), strSe,
110+
SerializerTypeInferer.getSerializer(a.getValue())));
111+
}
106112
}
107113
}
108114

@@ -135,9 +141,32 @@ public static LabelMap getLabels(final String mailbox)
135141
{
136142
if (a.getKey().startsWith(CN_LABEL_NAME_PREFIX))
137143
{
144+
// set label name
138145
Integer labelId = Integer.parseInt(a.getKey().split(CN_SEPARATOR)[1]);
139-
Label label = new Label(labelId, (String) a.getValue());
140-
labels.put(label);
146+
String labelName = (String) a.getValue();
147+
148+
if (labels.containsId(labelId)) {
149+
labels.get(labelId).setName(labelName);
150+
} else {
151+
Label label = new Label(labelId, labelName);
152+
labels.put(label);
153+
}
154+
}
155+
else if (a.getKey().startsWith(CN_LABEL_ATTRIBUTE_PREFIX))
156+
{
157+
// set label custom attribute
158+
String[] attrKeys = a.getKey().split(CN_SEPARATOR);
159+
Integer labelId = Integer.parseInt(attrKeys[1]);
160+
String attrName = attrKeys[2];
161+
String attrValue = (String) a.getValue();
162+
163+
if (labels.containsId(labelId)) {
164+
labels.get(labelId).addAttribute(attrName, attrValue);
165+
} else {
166+
Label label = new Label(labelId);
167+
label.addAttribute(attrName, attrValue);
168+
labels.put(label);
169+
}
141170
}
142171
}
143172

@@ -152,19 +181,45 @@ public static LabelMap getLabels(final String mailbox)
152181
}
153182

154183
/**
155-
* Inserts new label or updates name of the existing label.
184+
* Inserts new or updates existing label.
156185
*
157186
* @param mutator
158187
* @param mailbox
159-
* @param labelId
160-
* @param labelName
188+
* @param label
161189
*/
162-
public static void setLabelName(Mutator<String> mutator, final String mailbox, int labelId,
163-
final String labelName)
190+
public static void putLabel(Mutator<String> mutator, final String mailbox, Label label)
164191
{
165-
String labelKey = getLabelNameKey(labelId);
192+
String labelKey = getLabelNameKey(label.getId());
166193
Map<String, String> attributes = new HashMap<String, String>(1);
167-
attributes.put(labelKey, labelName);
194+
195+
// upsert label name
196+
if (label.getName() != null)
197+
{
198+
attributes.put(labelKey, label.getName());
199+
}
200+
201+
// upsert custom label attributes (delete if value is null or empty)
202+
if (label.getAttributes() != null)
203+
{
204+
for (Entry<String, String> labelAttr : label.getAttributes().entrySet())
205+
{
206+
// custom label attribute should not contain separator char
207+
if (labelAttr.getKey().contains(CN_SEPARATOR)) {
208+
throw new IllegalLabelException("Invalid character detected in the custom label attribute name.");
209+
}
210+
211+
String labelAttrKey = getLabelAttributeKey(label.getId(), labelAttr.getKey());
212+
213+
if (labelAttr.getValue() != null && !labelAttr.getValue().isEmpty()) {
214+
// upsert attribute
215+
attributes.put(labelAttrKey, labelAttr.getValue());
216+
} else {
217+
// delete if value is empty
218+
mutator.addDeletion(mailbox, CF_ACCOUNTS, labelAttrKey, strSe);
219+
}
220+
}
221+
}
222+
168223
AccountPersistence.set(mutator, mailbox, attributes);
169224
}
170225

@@ -177,14 +232,28 @@ public static void setLabelName(Mutator<String> mutator, final String mailbox, i
177232
*/
178233
public static void deleteLabel(Mutator<String> mutator, final String mailbox, int labelId)
179234
{
180-
String labelKey = getLabelNameKey(labelId);
181-
mutator.addDeletion(mailbox, CF_ACCOUNTS, labelKey, strSe);
235+
String labelNameKey = getLabelNameKey(labelId);
236+
String labelAttrPrefix = getLabelAttributeKey(labelId, ""); // label attributes prefix
237+
238+
// delete label name
239+
mutator.addDeletion(mailbox, CF_ACCOUNTS, labelNameKey, strSe);
240+
241+
// delete all attributes
242+
Map<String, Object> attributes = getAll(mailbox);
243+
244+
for (String labelAttrKey : attributes.keySet())
245+
{
246+
if (labelAttrKey.startsWith(labelAttrPrefix))
247+
{
248+
mutator.addDeletion(mailbox, CF_ACCOUNTS, labelAttrKey, strSe);
249+
}
250+
}
182251
}
183252

184253
/**
185254
* Generates key for custom label attribute.
186255
* <p>
187-
* Example <code>"label:123:attr:MyAttribute"</code>
256+
* Example <code>"lattr:123:MyAttribute"</code>
188257
*
189258
* @param labelId
190259
* @param attributeName Custom attribute name

0 commit comments

Comments
 (0)