Skip to content

Commit

Permalink
improve support for record multiple value custom fields (#52)
Browse files Browse the repository at this point in the history
* improve support for record multiple value custom fields

* update changelog

* fix changelog

* fix test
  • Loading branch information
Crim authored Apr 5, 2020
1 parent cf4dc8d commit d8a6495
Show file tree
Hide file tree
Showing 11 changed files with 413 additions and 15 deletions.
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,20 @@
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).

## 2.0.0 (03/05/2020)
- [ISSUE-50](https://github.com/Crim/pardot-java-client/issues/50) Improve support for record multiple fields.

### Breaking Change

In order to better support record multiple value custom fields, the accessor on `Prospect` for `getCustomFields()` has been changed **FROM**
`public Map<String, String> getCustomFields()` **TO** `public Map<String, ProspectCustomFieldValue> getCustomFields()`

For custom fields which are NOT record multiple value custom fields the accessor `public String getCustomField(final String customFieldName)` method will continue to return the stored value for the custom field.

For custom fields which ARE record multiple value custom fields the accessor `public String getCustomField(final String customFieldName)` method will only return the first stored value. You can use the new accessor
`public List<String> getCustomFieldValues(final String customFieldName)` to get all stored values for record multiple value custom fields.


## 1.1.2 (02/20/2020)
#### Bugfixes
- [ISSUE-45](https://github.com/Crim/pardot-java-client/issues/45) Fixed bug preventing the library from auto-renewing a session when it expires.
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ This client library is released on Maven Central. Add a new dependency to your
<dependency>
<groupId>com.darksci</groupId>
<artifactId>pardot-api-client</artifactId>
<version>1.1.2</version>
<version>2.0.0</version>
</dependency>
```

Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>com.darksci</groupId>
<artifactId>pardot-api-client</artifactId>
<version>1.1.2</version>
<version>2.0.0</version>
<packaging>jar</packaging>

<!-- Require Maven 3.5.0 -->
Expand Down
15 changes: 14 additions & 1 deletion src/main/java/com/darksci/pardot/api/parser/JacksonFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@

package com.darksci.pardot.api.parser;

import com.darksci.pardot.api.parser.prospect.ProspectCustomFieldDeserializer;
import com.darksci.pardot.api.response.customfield.ProspectCustomFieldValue;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.dataformat.xml.JacksonXmlModule;
Expand All @@ -30,15 +32,26 @@
*/
public class JacksonFactory {

/**
* Cache mapper instance.
*/
private static final XmlMapper instance = newInstance();

/**
* Creates properly configured Jackson XML Mapper instances.
* @return XmlMapper instance.
*/
public static XmlMapper newInstance() {
if (instance != null) {
return instance;
}

// Create new mapper
final JacksonXmlModule module = new JacksonXmlModule();
module.setDefaultUseWrapper(false);
XmlMapper mapper = new XmlMapper(module);
module.addDeserializer(ProspectCustomFieldValue.class, new ProspectCustomFieldDeserializer());

final XmlMapper mapper = new XmlMapper(module);

// Configure it
mapper
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/**
* Copyright 2017, 2018, 2019, 2020 Stephen Powis https://github.com/Crim/pardot-java-client
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
* persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

package com.darksci.pardot.api.parser.prospect;

import com.darksci.pardot.api.response.customfield.ProspectCustomFieldValue;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
* Custom deserializer to be able to support Record Multiple custom fields.
*/
public class ProspectCustomFieldDeserializer extends StdDeserializer<ProspectCustomFieldValue> {

/**
* Constructor.
*/
public ProspectCustomFieldDeserializer() {
this(null);
}

/**
* Constructor.
* @param vc the type of the class this handles.
*/
public ProspectCustomFieldDeserializer(final Class<?> vc) {
super(vc);
}

@Override
public ProspectCustomFieldValue deserialize(final JsonParser parser, final DeserializationContext context) throws IOException {
// Get the current custom field name.
final String fieldName = parser.getCurrentName();

// Keep track of all the values associated with the field.
final List<String> fieldValues = new ArrayList<>();

// If we have multiple values
if (parser.getCurrentToken() == JsonToken.START_OBJECT) {
// Loop until we hit end object
while (parser.nextToken() != JsonToken.END_OBJECT) {
// Pull out each value
if (parser.getCurrentToken() == JsonToken.VALUE_STRING) {
fieldValues.add(parser.getValueAsString());
}
}
} else if (parser.getCurrentToken() == JsonToken.VALUE_STRING) {
// If we have a single value, we just record the value as is.
fieldValues.add(parser.getValueAsString());
}

// Return our deserialized instance.
return new ProspectCustomFieldValue(fieldName, fieldValues);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
package com.darksci.pardot.api.request.prospect;

import com.darksci.pardot.api.request.BaseRequest;
import com.darksci.pardot.api.response.customfield.ProspectCustomFieldValue;
import com.darksci.pardot.api.response.prospect.Prospect;

import java.util.Map;
Expand Down Expand Up @@ -65,8 +66,18 @@ public T withProspect(final Prospect prospect) {

// Loop through and set custom fields
if (prospect.getCustomFields() != null) {
for (Map.Entry<String, String> entry: prospect.getCustomFields().entrySet()) {
setParam(entry.getKey(), entry.getValue());
for (ProspectCustomFieldValue customFieldValue : prospect.getCustomFields().values()) {
// For single value custom fields.
if (!customFieldValue.hasMultipleValues()) {
setParam(customFieldValue.getFieldName(), customFieldValue.getValue());
} else {
// For multiple value fields, send as field_name_<index> = value
int fieldIndex = 0;
for (final String value : customFieldValue.getValues()) {
setParam(customFieldValue.getFieldName() + "_" + fieldIndex, value);
fieldIndex++;
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/**
* Copyright 2017, 2018, 2019, 2020 Stephen Powis https://github.com/Crim/pardot-java-client
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
* persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

package com.darksci.pardot.api.response.customfield;

import com.darksci.pardot.api.parser.prospect.ProspectCustomFieldDeserializer;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
* Represents a Prospect CustomField value.
* Since custom fields can be stored as a single value, or as a list of values (for record multiple fields),
* this attempts to normalize the way these two types are accessed.
*/
@JsonDeserialize(using = ProspectCustomFieldDeserializer.class)
public class ProspectCustomFieldValue {
private final String fieldName;
private final List<String> values = new ArrayList<>();

/**
* Constructor for custom field which only has a single value.
* @param fieldName name of the field.
* @param value value of the field.
*/
public ProspectCustomFieldValue(final String fieldName, final String value) {
this.fieldName = fieldName;
if (value != null) {
this.values.add(value);
}
}

/**
* Constructor for custom field which has multiple values.
* @param fieldName name of the field.
* @param values values for the field.
*/
public ProspectCustomFieldValue(final String fieldName, final Collection<String> values) {
this.fieldName = fieldName;
if (values != null) {
this.values.addAll(values);
}
}

public String getFieldName() {
return fieldName;
}

/**
* The value of the custom field.
* @return value of the custom field.
* *NOTE* If this is a record multiple field with multiple values, this will return the first value only.
*/
public String getValue() {
if (values.isEmpty()) {
return null;
}
return values.get(0);
}

public List<String> getValues() {
return values;
}

public boolean hasMultipleValues() {
return getValues().size() > 1;
}

public void addValue(final String value) {
values.add(value);
}

public void addValues(final Collection<String> values) {
values.addAll(values);
}

public void setValue(final String value) {
values.clear();
addValue(value);
}

public void setValues(final Collection<String> values) {
values.clear();
addValues(values);
}

@Override
public String toString() {
return "ProspectCustomFieldValue{"
+ "fieldName='" + fieldName + '\''
+ ", values=" + values
+ '}';
}
}
Loading

0 comments on commit d8a6495

Please sign in to comment.