From cd024d6cf38a6e58090fa7b3159df5c2e937a7f0 Mon Sep 17 00:00:00 2001 From: unknown <86492C@EURCSQNP20433.AMERICAS.CORP.LOCAL> Date: Sun, 24 Mar 2013 15:16:22 +0000 Subject: [PATCH] Custom Columns: Support for custom columns (AKA fields) on cases for use with third-party plugins, e.g. Kanban. --- src/com/fieldexpert/fbapi4j/Case.java | 28 ++++++++ src/com/fieldexpert/fbapi4j/CaseHandler.java | 66 +++++++++++++++++-- .../fieldexpert/fbapi4j/ConnectedSession.java | 2 +- test/com/fieldexpert/fbapi4j/CaseTest.java | 29 ++++++++ .../fieldexpert/fbapi4j/CustomColumnTest.java | 36 ++++++++++ 5 files changed, 155 insertions(+), 6 deletions(-) create mode 100644 test/com/fieldexpert/fbapi4j/CustomColumnTest.java diff --git a/src/com/fieldexpert/fbapi4j/Case.java b/src/com/fieldexpert/fbapi4j/Case.java index 3b5ac7f..b65631b 100644 --- a/src/com/fieldexpert/fbapi4j/Case.java +++ b/src/com/fieldexpert/fbapi4j/Case.java @@ -178,5 +178,33 @@ public void setAssignedTo(Integer personId) { fields.put(Fbapi4j.IX_PERSON_ASSIGNED_TO, personId); fields.remove(Fbapi4j.S_PERSON_ASSIGNED_TO); } + + /*** + * A general-purpose getter for any field. Particularly useful for obtaining + * fields set up as custom in the {@link CaseHandler}. This is typically used + * for those fields belonging to third-party plugins. + * + * @throws IllegalArgumentException If the column specified does not exist + */ + public Object getField(String fieldName) throws IllegalArgumentException { + Object value = null; + + if (fields.containsKey(fieldName)) { + value = fields.get(fieldName); + } else { + throw new IllegalArgumentException(String.format("Unknown field specified '%s'", fieldName)); + } + + return value; + } + + /*** + * A general-purpose setter for any field. Particularly useful for obtaining + * fields set up as custom in the {@link CaseHandler}. This is typically used + * for those fields belonging to third-party plugins. + */ + public void setField(String fieldName, Object fieldValue) { + fields.put(fieldName, fieldValue); + } } diff --git a/src/com/fieldexpert/fbapi4j/CaseHandler.java b/src/com/fieldexpert/fbapi4j/CaseHandler.java index d739a38..5542d24 100644 --- a/src/com/fieldexpert/fbapi4j/CaseHandler.java +++ b/src/com/fieldexpert/fbapi4j/CaseHandler.java @@ -5,10 +5,13 @@ import java.net.URL; import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Properties; import java.util.Set; import org.w3c.dom.Document; @@ -26,10 +29,46 @@ class CaseHandler extends AbstractHandler { - private static final String cols = collectionToCommaDelimitedString(asList(Fbapi4j.S_PROJECT, Fbapi4j.S_AREA, Fbapi4j.S_SCOUT_DESCRIPTION, Fbapi4j.S_TITLE, Fbapi4j.S_EVENT, Fbapi4j.EVENTS)); - - CaseHandler(Dispatch dispatch, Util util, String token) { + /** The complete set of supported columns, i.e. the contents of {@link #DEFAULT_COLUMNS} merged with the custom columns from {@link #PROP_COLUMNS} */ + private Set supportedColumns; + /** A string representation of {@link #supportedColumns}, which can be used for encoded queries against FogBugz */ + private String supportedColumnsCSV; + + /** A set containing {@link #DEFAULT_COLUMNS} */ + private Set defaultColumns; + + /** The default columns to request for each case - regardless of custom configuration */ + static final String[] DEFAULT_COLUMNS = {Fbapi4j.S_PROJECT, Fbapi4j.S_AREA, Fbapi4j.S_SCOUT_DESCRIPTION, Fbapi4j.S_TITLE, Fbapi4j.S_EVENT, Fbapi4j.EVENTS}; + + /** The property used to list a CSV of additional columns to support */ + static final String PROP_COLUMNS = "case.columns"; + /** The delimiter of the CSV property as per {@link #PROP_COLUMNS} */ + static final String FIELD_CSV_DELIM = ","; + + CaseHandler(Dispatch dispatch, Util util, String token, Properties properties) { super(dispatch, util, token); + configureColumns(properties); + } + + /*** + * Configure additional columns to be made available. + * @param properties Application configuration + */ + private void configureColumns(Properties properties) { + supportedColumns = new HashSet(); + defaultColumns = new HashSet(); + // Add those supported by default - i.e. core fogbugz fields + defaultColumns.addAll(asList(DEFAULT_COLUMNS)); + supportedColumns.addAll(defaultColumns); + + if (properties.containsKey(PROP_COLUMNS)) { + String[] configuredColumns = properties.getProperty(PROP_COLUMNS).split(FIELD_CSV_DELIM); + for (String currentColumn : configuredColumns) { + supportedColumns.add(currentColumn); + } + } + + supportedColumnsCSV = collectionToCommaDelimitedString(supportedColumns); } private void allowed(Case c, AllowedOperation operation) { @@ -42,6 +81,15 @@ private void allowed(Case c, AllowedOperation operation) { Case build(Map data, Document doc) { Case c = new Case(Integer.parseInt(data.get(Fbapi4j.IX_BUG)), data.get(Fbapi4j.S_PROJECT), data.get(Fbapi4j.S_AREA), // data.get(Fbapi4j.S_TITLE), data.get(Fbapi4j.S_SCOUT_DESCRIPTION)); + + // Add allthe supported columns to the case + for (String currentColumn : supportedColumns) { + // We assume the default columns are handled elsewhere + if (! defaultColumns.contains(supportedColumns)) { + c.setField(currentColumn, data.get(currentColumn)); + } + } + List events = events(doc, c); c.addEvents(events); update(c, data); @@ -96,7 +144,7 @@ public List findAll() { } public Case findById(Integer id) { - return find(new Request(Fbapi4j.SEARCH, util.map(Fbapi4j.TOKEN, token, Fbapi4j.QUERY, id, Fbapi4j.COLS, cols))); + return find(new Request(Fbapi4j.SEARCH, util.map(Fbapi4j.TOKEN, token, Fbapi4j.QUERY, id, Fbapi4j.COLS, supportedColumnsCSV))); } public Case findByName(String name) { @@ -105,7 +153,7 @@ public Case findByName(String name) { public List query(String... criterion) { String q = collectionToCommaDelimitedString(asList(criterion)); - Response resp = dispatch.invoke(new Request(Fbapi4j.SEARCH, util.map(Fbapi4j.TOKEN, token, Fbapi4j.COLS, cols, Fbapi4j.QUERY, q))); + Response resp = dispatch.invoke(new Request(Fbapi4j.SEARCH, util.map(Fbapi4j.TOKEN, token, Fbapi4j.COLS, supportedColumnsCSV, Fbapi4j.QUERY, q))); return list(resp); } @@ -162,4 +210,12 @@ private void update(Case c, Map data) { } c.setAllowedOperations(operations); } + + /*** + * A ready-only copy of the set of columns supported. + * @return Read-only collection of columns + */ + protected Collection getSupportedColumns() { + return Collections.unmodifiableCollection(supportedColumns); + } } diff --git a/src/com/fieldexpert/fbapi4j/ConnectedSession.java b/src/com/fieldexpert/fbapi4j/ConnectedSession.java index fded773..43948c9 100644 --- a/src/com/fieldexpert/fbapi4j/ConnectedSession.java +++ b/src/com/fieldexpert/fbapi4j/ConnectedSession.java @@ -31,7 +31,7 @@ class ConnectedSession implements Session { private void initHandlers() { handlers.put(Area.class, areaHandler = new AreaHandler(dispatch, util, token)); - handlers.put(Case.class, caseHandler = new CaseHandler(dispatch, util, token)); + handlers.put(Case.class, caseHandler = new CaseHandler(dispatch, util, token, dispatch.getProperties())); handlers.put(Person.class, new PersonHandler(dispatch, util, token)); handlers.put(Priority.class, new PriorityHandler(dispatch, util, token)); handlers.put(Project.class, new ProjectHandler(dispatch, util, token)); diff --git a/test/com/fieldexpert/fbapi4j/CaseTest.java b/test/com/fieldexpert/fbapi4j/CaseTest.java index 71f84f1..64eda7b 100644 --- a/test/com/fieldexpert/fbapi4j/CaseTest.java +++ b/test/com/fieldexpert/fbapi4j/CaseTest.java @@ -1,6 +1,7 @@ package com.fieldexpert.fbapi4j; import static junit.framework.Assert.assertEquals; +import junit.framework.AssertionFailedError; import org.junit.Test; @@ -14,5 +15,33 @@ public void basic() { assertEquals("Misc", bug.getArea()); assertEquals("Test Case Title", bug.getTitle()); } + + @Test + public void customColumnSetter() { + Case bug = new Case("fbapi4j", "Misc", "Test Case Title", "Blue Smoke"); + bug.setField("some-field", "some-value"); + + assertEquals("Custom field getter didn't return value given to setter", "some-value", bug.getField("some-field")); + } + + @Test + public void customColumnUnsetGetter() { + Case bug = new Case("fbapi4j", "Misc", "Test Case Title", "Blue Smoke"); + + boolean caughtException = false; + try { + // When we call a getter, without having set the field..... + bug.getField("some-field"); + } catch (IllegalArgumentException ex) { + // ... we expect to get an IAE! + caughtException = true; + } + + if (! caughtException) { + throw new AssertionFailedError(String.format( + "Calling custom column getter on Case should throw %s if column not previously set", + IllegalArgumentException.class.getSimpleName())); + } + } } diff --git a/test/com/fieldexpert/fbapi4j/CustomColumnTest.java b/test/com/fieldexpert/fbapi4j/CustomColumnTest.java new file mode 100644 index 0000000..902ef97 --- /dev/null +++ b/test/com/fieldexpert/fbapi4j/CustomColumnTest.java @@ -0,0 +1,36 @@ +package com.fieldexpert.fbapi4j; + +import static org.junit.Assert.assertTrue; + +import java.util.Properties; + +import org.junit.Test; + +public class CustomColumnTest { + + /*** + * If custom columns are specified, ensure the case handler correctly adds them to its list of + * supported columns. + */ + @Test + public void testCustColumnSetup () { + Properties propsWithCustomColumns = new Properties(); + propsWithCustomColumns.setProperty(CaseHandler.PROP_COLUMNS, "some-column-a" + CaseHandler.FIELD_CSV_DELIM + "some-column-b"); + CaseHandler caseHandler = new CaseHandler(null, null, null, propsWithCustomColumns); + + assertTrue("The case handler should support 'some-column-a'", caseHandler.getSupportedColumns().contains("some-column-a")); + assertTrue("The case handler should support 'some-column-b'", caseHandler.getSupportedColumns().contains("some-column-b")); + } + + /*** + * Test the custom columns logic doesn't affect the default columns handling + */ + @Test + public void testDefaultColumns() { + CaseHandler caseHandler = new CaseHandler(null, null, null, new Properties()); + for (String currentDefaultColumn : CaseHandler.DEFAULT_COLUMNS) { + assertTrue(String.format("The case handler should support the default column '%s'", currentDefaultColumn), + caseHandler.getSupportedColumns().contains(currentDefaultColumn)); + } + } +}