diff options
| author | Jesse Morgan <jesse@jesterpm.net> | 2016-03-27 08:02:14 -0700 | 
|---|---|---|
| committer | Jesse Morgan <jesse@jesterpm.net> | 2016-03-27 08:02:14 -0700 | 
| commit | 6d987e0dd18ef830484641166a816661f4b16074 (patch) | |
| tree | effaccb0a85cebca64e39ea8aa4545d59306cfc6 | |
| parent | f4cb9e6a44c016c0200a9291f25003c72e2551cd (diff) | |
Adding the update_individual API
19 files changed, 1256 insertions, 26 deletions
| diff --git a/src/main/java/com/p4square/ccbapi/ApacheHttpClientImpl.java b/src/main/java/com/p4square/ccbapi/ApacheHttpClientImpl.java index 0833c67..4c3a777 100644 --- a/src/main/java/com/p4square/ccbapi/ApacheHttpClientImpl.java +++ b/src/main/java/com/p4square/ccbapi/ApacheHttpClientImpl.java @@ -9,6 +9,8 @@ import org.apache.http.auth.AuthScope;  import org.apache.http.auth.UsernamePasswordCredentials;  import org.apache.http.client.entity.UrlEncodedFormEntity;  import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.ByteArrayEntity; +import org.apache.http.entity.InputStreamEntity;  import org.apache.http.impl.client.DefaultHttpClient;  import org.apache.http.message.BasicNameValuePair;  import org.apache.http.util.EntityUtils; @@ -52,14 +54,13 @@ public class ApacheHttpClientImpl implements HTTPInterface {      }      @Override -    public InputStream sendPostRequest(URI uri, Map<String, String> form) throws IOException { +    public InputStream sendPostRequest(final URI uri, final byte[] form) throws IOException {          // Build the request.          final HttpPost httpPost = new HttpPost(uri); -        final List<NameValuePair> formParameters = new ArrayList<>(); -        for (Map.Entry<String, String> entry : form.entrySet()) { -            formParameters.add(new BasicNameValuePair(entry.getKey(), entry.getValue())); + +        if (form != null) { +            httpPost.setEntity(new ByteArrayEntity(form));          } -        httpPost.setEntity(new UrlEncodedFormEntity(formParameters));          // Make the request.          final HttpResponse response = httpClient.execute(httpPost); diff --git a/src/main/java/com/p4square/ccbapi/CCBAPI.java b/src/main/java/com/p4square/ccbapi/CCBAPI.java index e54e20b..f81a02f 100644 --- a/src/main/java/com/p4square/ccbapi/CCBAPI.java +++ b/src/main/java/com/p4square/ccbapi/CCBAPI.java @@ -39,4 +39,13 @@ public interface CCBAPI extends Closeable {       * @throws IOException on failure.       */      GetIndividualProfilesResponse getIndividualProfiles(GetIndividualProfilesRequest request) throws IOException; + +    /** +     * Update an IndividualProfile. +     * +     * @param request An UpdateIndividualProfileRequest including the fields to modify. +     * @return An UpdateIndividualProfileResponse, including the updated IndividualProfile. +     * @throws IOException on failure. +     */ +    UpdateIndividualProfileResponse updateIndividualProfile(UpdateIndividualProfileRequest request) throws IOException;  } diff --git a/src/main/java/com/p4square/ccbapi/CCBAPIClient.java b/src/main/java/com/p4square/ccbapi/CCBAPIClient.java index ee309c6..404253a 100644 --- a/src/main/java/com/p4square/ccbapi/CCBAPIClient.java +++ b/src/main/java/com/p4square/ccbapi/CCBAPIClient.java @@ -2,11 +2,15 @@ package com.p4square.ccbapi;  import com.p4square.ccbapi.exception.CCBErrorResponseException;  import com.p4square.ccbapi.model.*; +import com.p4square.ccbapi.serializer.AddressFormSerializer; +import com.p4square.ccbapi.serializer.IndividualProfileSerializer; +import com.p4square.ccbapi.serializer.PhoneFormSerializer;  import java.io.IOException;  import java.io.InputStream;  import java.net.URI;  import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets;  import java.util.Collections;  import java.util.HashMap;  import java.util.Map; @@ -23,6 +27,8 @@ public class CCBAPIClient implements CCBAPI {      private static final Map<String, String> EMPTY_MAP = Collections.emptyMap(); +    private static final IndividualProfileSerializer INDIVIDUAL_PROFILE_SERIALIZER = new IndividualProfileSerializer(); +      private final URI apiBaseUri;      private final HTTPInterface httpClient;      private final CCBXmlBinder xmlBinder; @@ -119,12 +125,27 @@ public class CCBAPIClient implements CCBAPI {          }          // Send the request and parse the response. -        return makeRequest(serviceName, params, EMPTY_MAP, GetIndividualProfilesResponse.class); +        return makeRequest(serviceName, params, null, GetIndividualProfilesResponse.class);      }      @Override      public GetCustomFieldLabelsResponse getCustomFieldLabels() throws IOException { -        return makeRequest("custom_field_labels", EMPTY_MAP, EMPTY_MAP, GetCustomFieldLabelsResponse.class); +        return makeRequest("custom_field_labels", EMPTY_MAP, null, GetCustomFieldLabelsResponse.class); +    } + +    @Override +    public UpdateIndividualProfileResponse updateIndividualProfile(UpdateIndividualProfileRequest request) +            throws IOException { + +        if (request.getIndividualId() == 0) { +            throw new IllegalArgumentException("individualId must be set on the request."); +        } + +        final Map<String, String> params = Collections.singletonMap("individual_id", +                                                                    String.valueOf(request.getIndividualId())); +        final String form = INDIVIDUAL_PROFILE_SERIALIZER.encode(request); + +        return makeRequest("update_individual", params, form, UpdateIndividualProfileResponse.class);      }      /** @@ -164,10 +185,15 @@ public class CCBAPIClient implements CCBAPI {       * @throws IOException if an error occurs.       */      private <T extends CCBAPIResponse> T makeRequest(final String api, final Map<String, String> params, -                                                     final Map<String, String> form, final Class<T> clazz) +                                                     final String form, final Class<T> clazz)              throws IOException { -        final InputStream entity = httpClient.sendPostRequest(makeURI(api, params), form); +        byte[] payload = null; +        if (form != null) { +            payload = form.getBytes(StandardCharsets.UTF_8); +        } + +        final InputStream entity = httpClient.sendPostRequest(makeURI(api, params), payload);          try {              T response = xmlBinder.bindResponseXML(entity, clazz);              if (response.getErrors() != null && response.getErrors().size() > 0) { diff --git a/src/main/java/com/p4square/ccbapi/HTTPInterface.java b/src/main/java/com/p4square/ccbapi/HTTPInterface.java index 11d87ec..9c5e818 100644 --- a/src/main/java/com/p4square/ccbapi/HTTPInterface.java +++ b/src/main/java/com/p4square/ccbapi/HTTPInterface.java @@ -24,11 +24,11 @@ public interface HTTPInterface extends Closeable {       * The form data for the request is specified in the form Map.       *       * @param uri The URI to request. -     * @param form Map of key/value pairs to send as form data. +     * @param form Form data or null.       * @return The response received.       * @throws com.p4square.ccbapi.exception.CCBRetryableErrorException       * @throws CCBRetryableErrorException if a retryable error occurs.       * @throws IOException If a non-retryable error occurs.       */ -    InputStream sendPostRequest(URI uri, Map<String, String> form) throws IOException; +    InputStream sendPostRequest(URI uri, byte[] form) throws IOException;  } diff --git a/src/main/java/com/p4square/ccbapi/model/Countries.java b/src/main/java/com/p4square/ccbapi/model/Countries.java new file mode 100644 index 0000000..ec9df70 --- /dev/null +++ b/src/main/java/com/p4square/ccbapi/model/Countries.java @@ -0,0 +1,8 @@ +package com.p4square.ccbapi.model; + +/** + * Constants which define common countries. + */ +public class Countries { +    public static final Country UNITED_STATES = new Country("US", "United States"); +} diff --git a/src/main/java/com/p4square/ccbapi/model/Country.java b/src/main/java/com/p4square/ccbapi/model/Country.java index e6936a5..ac9e9ec 100644 --- a/src/main/java/com/p4square/ccbapi/model/Country.java +++ b/src/main/java/com/p4square/ccbapi/model/Country.java @@ -16,6 +16,30 @@ public class Country {      private String name;      /** +     * Default Country constructor. +     */ +    public Country() { + +    } + +    /** +     * This package-private Country constructor is used to create the +     * constants in the {@link Countries} class. +     * +     * Usually the country name cannot be set except when binding to +     * responses from CCB. This constructor is an exception so that the +     * constants in {@link Countries} can be provide with +     * <strong>known</strong> values used by CCB. +     * +     * @param code The two letter country code. +     * @param name The country name as typically presented by CCB. +     */ +    Country(final String code, final String name) { +        setCountryCode(code); +        this.name = name; +    } + +    /**       * @return The two letter country code.       */      public String getCountryCode() { diff --git a/src/main/java/com/p4square/ccbapi/model/Gender.java b/src/main/java/com/p4square/ccbapi/model/Gender.java index eabaa42..cf6736a 100644 --- a/src/main/java/com/p4square/ccbapi/model/Gender.java +++ b/src/main/java/com/p4square/ccbapi/model/Gender.java @@ -6,8 +6,15 @@ import javax.xml.bind.annotation.XmlEnumValue;   * Enum representing the gender of an individual in CCB.   */  public enum Gender { -    @XmlEnumValue("M") MALE("M"), -    @XmlEnumValue("F") FEMALE("F"); +    /** +     * The documentation currently provides conflicting examples for the gender code. +     * The documentation says it must be 'M' or 'F', but the example given uses 'm'. +     * According to an API Village posting, it should actually be lower case. +     * +     * https://village.ccbchurch.com/message_comment_list.php?message_id=3308 +     */ +    @XmlEnumValue("M") MALE("m"), +    @XmlEnumValue("F") FEMALE("f");      private final String code; diff --git a/src/main/java/com/p4square/ccbapi/model/GetIndividualProfilesResponse.java b/src/main/java/com/p4square/ccbapi/model/GetIndividualProfilesResponse.java index f88bcf7..bc7915c 100644 --- a/src/main/java/com/p4square/ccbapi/model/GetIndividualProfilesResponse.java +++ b/src/main/java/com/p4square/ccbapi/model/GetIndividualProfilesResponse.java @@ -4,7 +4,7 @@ import javax.xml.bind.annotation.*;  import java.util.List;  /** - * GetCustomFieldLabelsResponse models the response of a variety of APIs which return one or more Individual Profiles. + * GetIndividualProfilesResponse models the response of a variety of APIs which return one or more Individual Profiles.   */  @XmlRootElement(name="response")  @XmlAccessorType(XmlAccessType.NONE) diff --git a/src/main/java/com/p4square/ccbapi/model/UpdateIndividualProfileRequest.java b/src/main/java/com/p4square/ccbapi/model/UpdateIndividualProfileRequest.java new file mode 100644 index 0000000..613b678 --- /dev/null +++ b/src/main/java/com/p4square/ccbapi/model/UpdateIndividualProfileRequest.java @@ -0,0 +1,378 @@ +package com.p4square.ccbapi.model; + +import java.time.LocalDate; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +/** + * UpdateIndividualProfileRequest encapsulates a change to an IndividualProfile. + * + * The only property which must be set is {@link #withIndividualId(int)}, + * to indicate the profile to update. + * + * A property can be unset by setting it to null. + */ +public class UpdateIndividualProfileRequest { + +    /** +     * The set of all valid custom text field names. +     */ +    public static final Set<String> CUSTOM_TEXT_FIELD_NAMES = +            IntStream.rangeClosed(1, 12).mapToObj(x -> "udf_text_" + x).collect(Collectors.toSet()); + +    /** +     * The set of all valid custom date field names. +     */ +    public static final Set<String> CUSTOM_DATE_FIELD_NAMES = +            IntStream.rangeClosed(1, 6).mapToObj(x -> "udf_date_" + x).collect(Collectors.toSet()); + +    /** +     * The set of all valid custom pulldown field names. +     */ +    public static final Set<String> CUSTOM_PULLDOWN_FIELD_NAMES = +            IntStream.rangeClosed(1, 6).mapToObj(x -> "udf_pulldown_" + x).collect(Collectors.toSet()); + +    private int individualId; + +    private Integer syncId; +    private String otherId; +    private String givingNumber; + +    private String firstName; +    private String lastName; +    private String middleName; +    private String legalFirstName; +    private String salutation; +    private String suffix; + +    private Integer familyId; + +    private FamilyPosition familyPosition; + +    private MaritalStatus maritalStatus; + +    private Gender gender; +    private LocalDate birthday; +    private LocalDate anniversary; +    private LocalDate deceased; +    private LocalDate membershipDate; +    private LocalDate membershipEnd; + +    private String email; + +    private List<Address> addresses; +    private List<Phone> phones; + +    private String emergencyContactName; +    private String allergies; +    private Boolean confirmedNoAllergies; +    private Boolean baptized; + +    private Map<String, String> customTextFields = new HashMap<>(); +    private Map<String, LocalDate> customDateFields = new HashMap<>(); +    private Map<String, Integer> customPulldownFields = new HashMap<>(); + +    private Integer modifiedById; + +    public int getIndividualId() { +        return individualId; +    } + +    public UpdateIndividualProfileRequest withIndividualId(int individualId) { +        this.individualId = individualId; +        return this; +    } + +    public Integer getSyncId() { +        return syncId; +    } + +    public UpdateIndividualProfileRequest withSyncId(Integer syncId) { +        this.syncId = syncId; +        return this; +    } + +    public String getOtherId() { +        return otherId; +    } + +    public UpdateIndividualProfileRequest withOtherId(String otherId) { +        this.otherId = otherId; +        return this; +    } + +    public String getGivingNumber() { +        return givingNumber; +    } + +    public UpdateIndividualProfileRequest withGivingNumber(String givingNumber) { +        this.givingNumber = givingNumber; +        return this; +    } + +    public String getFirstName() { +        return firstName; +    } + +    public UpdateIndividualProfileRequest withFirstName(String firstName) { +        this.firstName = firstName; +        return this; +    } + +    public String getLastName() { +        return lastName; +    } + +    public UpdateIndividualProfileRequest withLastName(String lastName) { +        this.lastName = lastName; +        return this; +    } + +    public String getMiddleName() { +        return middleName; +    } + +    public UpdateIndividualProfileRequest withMiddleName(String middleName) { +        this.middleName = middleName; +        return this; +    } + +    public String getLegalFirstName() { +        return legalFirstName; +    } + +    public UpdateIndividualProfileRequest withLegalFirstName(String legalFirstName) { +        this.legalFirstName = legalFirstName; +        return this; +    } + +    public String getSalutation() { +        return salutation; +    } + +    public UpdateIndividualProfileRequest withSalutation(String salutation) { +        this.salutation = salutation; +        return this; +    } + +    public String getSuffix() { +        return suffix; +    } + +    public UpdateIndividualProfileRequest withSuffix(String suffix) { +        this.suffix = suffix; +        return this; +    } + +    public Integer getFamilyId() { +        return familyId; +    } + +    public UpdateIndividualProfileRequest withFamilyId(Integer familyId) { +        this.familyId = familyId; +        return this; +    } + +    public FamilyPosition getFamilyPosition() { +        return familyPosition; +    } + +    public UpdateIndividualProfileRequest withFamilyPosition(FamilyPosition familyPosition) { +        this.familyPosition = familyPosition; +        return this; +    } + +    public MaritalStatus getMaritalStatus() { +        return maritalStatus; +    } + +    public UpdateIndividualProfileRequest withMaritalStatus(MaritalStatus maritalStatus) { +        this.maritalStatus = maritalStatus; +        return this; +    } + +    public Gender getGender() { +        return gender; +    } + +    public UpdateIndividualProfileRequest withGender(Gender gender) { +        this.gender = gender; +        return this; +    } + +    public LocalDate getBirthday() { +        return birthday; +    } + +    public UpdateIndividualProfileRequest withBirthday(LocalDate birthday) { +        this.birthday = birthday; +        return this; +    } + +    public LocalDate getAnniversary() { +        return anniversary; +    } + +    public UpdateIndividualProfileRequest withAnniversary(LocalDate anniversary) { +        this.anniversary = anniversary; +        return this; +    } + +    public LocalDate getDeceased() { +        return deceased; +    } + +    public UpdateIndividualProfileRequest withDeceased(LocalDate deceased) { +        this.deceased = deceased; +        return this; +    } + +    public LocalDate getMembershipDate() { +        return membershipDate; +    } + +    public UpdateIndividualProfileRequest withMembershipDate(LocalDate membershipDate) { +        this.membershipDate = membershipDate; +        return this; +    } + +    public LocalDate getMembershipEnd() { +        return membershipEnd; +    } + +    public UpdateIndividualProfileRequest withMembershipEnd(LocalDate membershipEnd) { +        this.membershipEnd = membershipEnd; +        return this; +    } + +    public String getEmail() { +        return email; +    } + +    public UpdateIndividualProfileRequest withEmail(String email) { +        this.email = email; +        return this; +    } + +    public List<Address> getAddresses() { +        return addresses; +    } + +    public UpdateIndividualProfileRequest withAddresses(List<Address> addresses) { +        this.addresses = addresses; +        return this; +    } + +    public List<Phone> getPhones() { +        return phones; +    } + +    public UpdateIndividualProfileRequest withPhones(List<Phone> phones) { +        this.phones = phones; +        return this; +    } + +    public String getEmergencyContactName() { +        return emergencyContactName; +    } + +    public UpdateIndividualProfileRequest withEmergencyContactName(String emergencyContactName) { +        this.emergencyContactName = emergencyContactName; +        return this; +    } + +    public String getAllergies() { +        return allergies; +    } + +    public UpdateIndividualProfileRequest withAllergies(String allergies) { +        this.allergies = allergies; +        return this; +    } + +    public Boolean getConfirmedNoAllergies() { +        return confirmedNoAllergies; +    } + +    public UpdateIndividualProfileRequest withConfirmedNoAllergies(Boolean confirmedNoAllergies) { +        this.confirmedNoAllergies = confirmedNoAllergies; +        return this; +    } + +    public Boolean getBaptized() { +        return baptized; +    } + +    public UpdateIndividualProfileRequest withBaptized(Boolean baptized) { +        this.baptized = baptized; +        return this; +    } + +    /** +     * @return A map of custom field identifiers to text field values. +     */ +    public Map<String, String> getCustomTextFields() { +        return customTextFields; +    } + +    public UpdateIndividualProfileRequest withCustomTextField(final String name, final String value) { +        if (!CUSTOM_TEXT_FIELD_NAMES.contains(name)) { +            throw new IllegalArgumentException(name + " is not a valid a valid text field name."); +        } +        customTextFields.put(name, value); +        return this; +    } + +    public UpdateIndividualProfileRequest withCustomTextField(final int number, final String value) { +        return withCustomTextField("udf_text_" + number, value); +    } + +    /** +     * @return A map of custom field identifiers to date field values. +     */ +    public Map<String, LocalDate> getCustomDateFields() { +        return customDateFields; +    } + +    public UpdateIndividualProfileRequest withCustomDateField(final String name, final LocalDate value) { +        if (!CUSTOM_DATE_FIELD_NAMES.contains(name)) { +            throw new IllegalArgumentException(name + " is not a valid a valid date field name."); +        } +        customDateFields.put(name, value); +        return this; +    } + +    public UpdateIndividualProfileRequest withCustomDateField(final int number, final LocalDate value) { +        return withCustomDateField("udf_date_" + number, value); +    } + +    /** +     * @return A map of custom field identifiers to pulldown field values. +     */ +    public Map<String, Integer> getCustomPulldownFields() { +        return customPulldownFields; +    } + +    public UpdateIndividualProfileRequest withCustomPulldownField(final String name, final Integer value) { +        if (!CUSTOM_PULLDOWN_FIELD_NAMES.contains(name)) { +            throw new IllegalArgumentException(name + " is not a valid a valid pulldown field name."); +        } +        customPulldownFields.put(name, value); +        return this; +    } + +    public UpdateIndividualProfileRequest withCustomPulldownField(final int number, final Integer value) { +        return withCustomPulldownField("udf_pulldown_" + number, value); +    } + +    public Integer getModifiedById() { +        return modifiedById; +    } + +    public UpdateIndividualProfileRequest withModifiedById(Integer modifiedById) { +        this.modifiedById = modifiedById; +        return this; +    } + +} diff --git a/src/main/java/com/p4square/ccbapi/model/UpdateIndividualProfileResponse.java b/src/main/java/com/p4square/ccbapi/model/UpdateIndividualProfileResponse.java new file mode 100644 index 0000000..9067d56 --- /dev/null +++ b/src/main/java/com/p4square/ccbapi/model/UpdateIndividualProfileResponse.java @@ -0,0 +1,27 @@ +package com.p4square.ccbapi.model; + +import javax.xml.bind.annotation.*; +import java.util.List; + +/** + * The response from an update_individual request. + */ +@XmlRootElement(name="response") +@XmlAccessorType(XmlAccessType.NONE) +public class UpdateIndividualProfileResponse extends CCBAPIResponse { + +    @XmlElementWrapper(name = "individuals") +    @XmlElement(name="individual") +    private List<IndividualProfile> individuals; + +    /** +     * @return The list of individuals retrieved from CCB. +     */ +    public List<IndividualProfile> getIndividuals() { +        return individuals; +    } + +    public void setIndividuals(List<IndividualProfile> individuals) { +        this.individuals = individuals; +    } +} diff --git a/src/main/java/com/p4square/ccbapi/serializer/AbstractFormSerializer.java b/src/main/java/com/p4square/ccbapi/serializer/AbstractFormSerializer.java new file mode 100644 index 0000000..06a83a1 --- /dev/null +++ b/src/main/java/com/p4square/ccbapi/serializer/AbstractFormSerializer.java @@ -0,0 +1,96 @@ +package com.p4square.ccbapi.serializer; + +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +/** + * AbstractFormSerializer provides default implementations for some methods defined in Serializer + * and methods to support encoding form data for CCB. + */ +public abstract class AbstractFormSerializer<T> implements Serializer<T> { + +    /** +     * This is the datetime format specified by the CCB API Doc. +     */ +    private static final DateTimeFormatter DATE_TIME_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + +    @Override +    public String encode(final T obj) { +        final StringBuilder sb = new StringBuilder(); +        encode(obj, sb); +        return sb.toString(); +    } + +    /** +     * Append a field to the form. +     * +     * @param builder The StringBuilder to use. +     * @param key The form key, which must be URLEncoded before calling this method. +     * @param value The value associated with the key. The value will be URLEncoded by this method. +     */ +    protected void appendField(final StringBuilder builder, final String key, final String value) { +        if (builder.length() > 0) { +            builder.append("&"); +        } + +        try { +            builder.append(key).append("=").append(URLEncoder.encode(value, "UTF-8")); +        } catch (UnsupportedEncodingException e) { +            throw new AssertionError("UTF-8 encoding should always be available."); +        } +    } + +    /** +     * Append an integer field to the form. +     * +     * @param builder The StringBuilder to use. +     * @param key The form key, which must be URLEncoded before calling this method. +     * @param value The value associated with the key. +     */ +    protected void appendField(final StringBuilder builder, final String key, final int value) { +        if (builder.length() > 0) { +            builder.append("&"); +        } +        builder.append(key).append("=").append(value); +    } + +    /** +     * Append a boolean field to the form. +     * +     * @param builder The StringBuilder to use. +     * @param key The form key, which must be URLEncoded before calling this method. +     * @param value The value associated with the key. +     */ +    protected void appendField(final StringBuilder builder, final String key, final boolean value) { +        if (builder.length() > 0) { +            builder.append("&"); +        } +        builder.append(key).append("=").append(value ? "true" : "false"); +    } + +    /** +     * Append a LocalDate field to the form. +     * +     * @param builder The StringBuilder to use. +     * @param key The form key, which must be URLEncoded before calling this method. +     * @param value The value associated with the key. +     */ +    protected void appendField(final StringBuilder builder, final String key, final LocalDate value) { +        appendField(builder, key, value.toString()); + +    } + +    /** +     * Append a LocalDateTime field to the form. +     * +     * @param builder The StringBuilder to use. +     * @param key The form key, which must be URLEncoded before calling this method. +     * @param value The value associated with the key. +     */ +    protected void appendField(final StringBuilder builder, final String key, final LocalDateTime value) { +        appendField(builder, key, DATE_TIME_FORMAT.format(value)); +    } +} diff --git a/src/main/java/com/p4square/ccbapi/serializer/AddressFormSerializer.java b/src/main/java/com/p4square/ccbapi/serializer/AddressFormSerializer.java new file mode 100644 index 0000000..00af004 --- /dev/null +++ b/src/main/java/com/p4square/ccbapi/serializer/AddressFormSerializer.java @@ -0,0 +1,54 @@ +package com.p4square.ccbapi.serializer; + +import com.p4square.ccbapi.model.Address; + +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; + +/** + * Encode an Address object as form data for CCB. + */ +public class AddressFormSerializer extends AbstractFormSerializer<Address> { + +    @Override +    public void encode(final Address address, final StringBuilder builder) { +        // Sanity check. +        if (address.getType() == null) { +            throw new IllegalArgumentException("Address type cannot be null"); +        } + +        // Every form field will be prefixed with the type. +        final String type = address.getType().toString().toLowerCase(); + +        if (address.getStreetAddress() != null) { +            appendField(builder, type, "street_address", address.getStreetAddress()); +        } + +        if (address.getCity() != null) { +            appendField(builder, type, "city", address.getCity()); +        } + +        if (address.getState() != null) { +            appendField(builder, type, "state", address.getState()); +        } + +        if (address.getZip() != null) { +            appendField(builder, type, "zip", address.getZip()); +        } + +        if (address.getCountry() != null) { +            appendField(builder, type, "country", address.getCountry().getCountryCode()); +        } +    } + +    private void appendField(final StringBuilder builder, final String type, final String key, final String value) { +        if (builder.length() > 0) { +            builder.append("&"); +        } +        try { +            builder.append(type).append("_").append(key).append("=").append(URLEncoder.encode(value, "UTF-8")); +        } catch (UnsupportedEncodingException e) { +            throw new AssertionError("UTF-8 encoding should always be available."); +        } +    } +} diff --git a/src/main/java/com/p4square/ccbapi/serializer/IndividualProfileSerializer.java b/src/main/java/com/p4square/ccbapi/serializer/IndividualProfileSerializer.java new file mode 100644 index 0000000..52b1a44 --- /dev/null +++ b/src/main/java/com/p4square/ccbapi/serializer/IndividualProfileSerializer.java @@ -0,0 +1,125 @@ +package com.p4square.ccbapi.serializer; + +import com.p4square.ccbapi.model.Address; +import com.p4square.ccbapi.model.Phone; +import com.p4square.ccbapi.model.UpdateIndividualProfileRequest; + +import java.time.LocalDate; +import java.util.Map; + +/** + * Serializes an {@link UpdateIndividualProfileRequest} into a form suitable for the update_individual API. + */ +public class IndividualProfileSerializer extends AbstractFormSerializer<UpdateIndividualProfileRequest> { + +    private static final AddressFormSerializer ADDRESS_FORM_SERIALIZER = new AddressFormSerializer(); +    private static final PhoneFormSerializer PHONE_FORM_SERIALIZER = new PhoneFormSerializer(); + +    @Override +    public void encode(final UpdateIndividualProfileRequest request, final StringBuilder builder) { +        // Encode any fields which are present. +        if (request.getSyncId() != null) { +            appendField(builder, "sync_id", request.getSyncId()); +        } +        if (request.getOtherId() != null) { +            appendField(builder, "other_id", request.getOtherId()); +        } +        if (request.getGivingNumber() != null) { +            appendField(builder, "giving_number", request.getGivingNumber()); +        } +        if (request.getFirstName() != null) { +            appendField(builder, "first_name", request.getFirstName()); +        } +        if (request.getLastName() != null) { +            appendField(builder, "last_name", request.getLastName()); +        } +        if (request.getMiddleName() != null) { +            appendField(builder, "middle_name", request.getMiddleName()); +        } +        if (request.getLegalFirstName() != null) { +            appendField(builder, "legal_first_name", request.getLegalFirstName()); +        } +        if (request.getSalutation() != null) { +            appendField(builder, "salutation", request.getSalutation()); +        } +        if (request.getSuffix() != null) { +            appendField(builder, "suffix", request.getSuffix()); +        } +        if (request.getFamilyId() != null) { +            appendField(builder, "family_id", request.getFamilyId()); +        } +        if (request.getFamilyPosition() != null) { +            appendField(builder, "family_position", request.getFamilyPosition().getCode()); +        } +        if (request.getMaritalStatus() != null) { +            appendField(builder, "marital_status", request.getMaritalStatus().getCode()); +        } +        if (request.getGender() != null) { +            appendField(builder, "gender", request.getGender().getCode()); +        } +        if (request.getBirthday() != null) { +            appendField(builder, "birthday", request.getBirthday()); +        } +        if (request.getAnniversary() != null) { +            appendField(builder, "anniversary", request.getAnniversary()); +        } +        if (request.getDeceased() != null) { +            appendField(builder, "deceased", request.getDeceased()); +        } +        if (request.getMembershipDate() != null) { +            appendField(builder, "membership_date", request.getMembershipDate()); +        } +        if (request.getMembershipEnd() != null) { +            appendField(builder, "membership_end", request.getMembershipEnd()); +        } +        if (request.getEmail() != null) { +            appendField(builder, "email", request.getEmail()); +        } +        if (request.getEmergencyContactName() != null) { +            appendField(builder, "emergency_contact_name", request.getEmergencyContactName()); +        } +        if (request.getAllergies() != null) { +            appendField(builder, "allergies", request.getAllergies()); +        } +        if (request.getConfirmedNoAllergies() != null) { +            appendField(builder, "confirmed_no_allergies", request.getConfirmedNoAllergies()); +        } +        if (request.getBaptized() != null) { +            appendField(builder, "baptized", request.getBaptized()); +        } +        if (request.getModifiedById() != null) { +            appendField(builder, "modifier_id", request.getModifiedById()); +        } + +        // Encode all the addresses. +        if (request.getAddresses() != null) { +            for (Address address : request.getAddresses()) { +                ADDRESS_FORM_SERIALIZER.encode(address, builder); +            } +        } + +        // and the phone numbers. +        if (request.getPhones() != null) { +            for (Phone phone : request.getPhones()) { +                PHONE_FORM_SERIALIZER.encode(phone, builder); +            } +        } + +        // Add the User-defined fields. +        for (Map.Entry<String, String> entry : request.getCustomTextFields().entrySet()) { +            if (entry.getValue() != null) { +                appendField(builder, entry.getKey(), entry.getValue()); +            } +        } +        for (Map.Entry<String, LocalDate> entry : request.getCustomDateFields().entrySet()) { +            if (entry.getValue() != null) { +                appendField(builder, entry.getKey(), entry.getValue()); +            } +        } +        for (Map.Entry<String, Integer> entry : request.getCustomPulldownFields().entrySet()) { +            if (entry.getValue() != null) { +                appendField(builder, entry.getKey(), entry.getValue()); +            } +        } +    } +} diff --git a/src/main/java/com/p4square/ccbapi/serializer/PhoneFormSerializer.java b/src/main/java/com/p4square/ccbapi/serializer/PhoneFormSerializer.java new file mode 100644 index 0000000..3569321 --- /dev/null +++ b/src/main/java/com/p4square/ccbapi/serializer/PhoneFormSerializer.java @@ -0,0 +1,24 @@ +package com.p4square.ccbapi.serializer; + +import com.p4square.ccbapi.model.Phone; + +/** + * Encode a Phone object as form data for CCB. + */ +public class PhoneFormSerializer extends AbstractFormSerializer<Phone> { +    @Override +    public void encode(final Phone phone, final StringBuilder builder) { +        // Sanity check. +        if (phone.getType() == null) { +            throw new IllegalArgumentException("Phone type cannot be null"); +        } + +        final String key; +        if (phone.getType() == Phone.Type.EMERGENCY) { +            key = "phone_emergency"; +        } else { +            key = phone.getType().toString().toLowerCase() + "_phone"; +        } +        appendField(builder, key, phone.getNumber()); +    } +} diff --git a/src/main/java/com/p4square/ccbapi/serializer/Serializer.java b/src/main/java/com/p4square/ccbapi/serializer/Serializer.java new file mode 100644 index 0000000..775aa09 --- /dev/null +++ b/src/main/java/com/p4square/ccbapi/serializer/Serializer.java @@ -0,0 +1,11 @@ +package com.p4square.ccbapi.serializer; + +/** + * A Serializer converts an object of type T to a payload suitable for CCB. + */ +public interface Serializer<T> { + +    String encode(T obj); + +    void encode(T obj, StringBuilder builder); +} diff --git a/src/test/java/com/p4square/ccbapi/CCBAPIClientTest.java b/src/test/java/com/p4square/ccbapi/CCBAPIClientTest.java index b15d16f..69f50c2 100644 --- a/src/test/java/com/p4square/ccbapi/CCBAPIClientTest.java +++ b/src/test/java/com/p4square/ccbapi/CCBAPIClientTest.java @@ -2,9 +2,7 @@ package com.p4square.ccbapi;  import com.p4square.ccbapi.exception.CCBErrorResponseException;  import com.p4square.ccbapi.exception.CCBRetryableErrorException; -import com.p4square.ccbapi.model.GetCustomFieldLabelsResponse; -import com.p4square.ccbapi.model.GetIndividualProfilesRequest; -import com.p4square.ccbapi.model.GetIndividualProfilesResponse; +import com.p4square.ccbapi.model.*;  import org.junit.Before;  import java.io.IOException; @@ -16,6 +14,7 @@ import java.util.Map;  import org.easymock.EasyMock;  import org.junit.Test; +import org.junit.rules.ExpectedException;  import static org.junit.Assert.*; @@ -51,7 +50,7 @@ public class CCBAPIClientTest {          // Set expectation.          URI expectedURI = new URI("https://localhost:8080/api.php?srv=custom_field_labels");          InputStream is = getClass().getResourceAsStream("model/ccb_custom_field_labels_response.xml"); -        EasyMock.expect(mockHttpClient.sendPostRequest(expectedURI, Collections.<String, String>emptyMap())) +        EasyMock.expect(mockHttpClient.sendPostRequest(expectedURI, null))                  .andReturn(is);          EasyMock.replay(mockHttpClient); @@ -72,7 +71,7 @@ public class CCBAPIClientTest {          // Set expectation.          URI expectedURI = new URI("https://localhost:8080/api.php?srv=custom_field_labels");          InputStream is = getClass().getResourceAsStream("model/ccb_error_response.xml"); -        EasyMock.expect(mockHttpClient.sendPostRequest(expectedURI, Collections.<String, String>emptyMap())) +        EasyMock.expect(mockHttpClient.sendPostRequest(expectedURI, null))                  .andReturn(is);          EasyMock.replay(mockHttpClient); @@ -92,7 +91,7 @@ public class CCBAPIClientTest {      @Test(expected = CCBRetryableErrorException.class)      public void testGetCustomFieldLabelsTimeout() throws Exception {          // Setup mocks. -        EasyMock.expect(mockHttpClient.sendPostRequest(EasyMock.anyObject(URI.class), EasyMock.anyObject(Map.class))) +        EasyMock.expect(mockHttpClient.sendPostRequest(EasyMock.anyObject(URI.class), EasyMock.anyObject()))                  .andThrow(new CCBRetryableErrorException("Retryable error"));          EasyMock.replay(mockHttpClient); @@ -103,7 +102,7 @@ public class CCBAPIClientTest {      @Test(expected = IOException.class)      public void testGetCustomFieldLabelsException() throws Exception {          // Setup mocks. -        EasyMock.expect(mockHttpClient.sendPostRequest(EasyMock.anyObject(URI.class), EasyMock.anyObject(Map.class))) +        EasyMock.expect(mockHttpClient.sendPostRequest(EasyMock.anyObject(URI.class), EasyMock.anyObject()))                  .andThrow(new IOException());          EasyMock.replay(mockHttpClient); @@ -116,7 +115,7 @@ public class CCBAPIClientTest {          // Set expectation.          URI expectedURI = new URI("https://localhost:8080/api.php?srv=individual_profile_from_id&individual_id=48");          InputStream is = getClass().getResourceAsStream("model/ccb_individual_profile_response.xml"); -        EasyMock.expect(mockHttpClient.sendPostRequest(expectedURI, Collections.<String, String>emptyMap())) +        EasyMock.expect(mockHttpClient.sendPostRequest(expectedURI, null))                  .andReturn(is);          EasyMock.replay(mockHttpClient); @@ -137,7 +136,7 @@ public class CCBAPIClientTest {          URI expectedURI = new URI("https://localhost:8080/api.php?"                  + "srv=individual_profile_from_login_password&password=pass&login=user");          InputStream is = getClass().getResourceAsStream("model/ccb_individual_profile_response.xml"); -        EasyMock.expect(mockHttpClient.sendPostRequest(expectedURI, Collections.<String, String>emptyMap())) +        EasyMock.expect(mockHttpClient.sendPostRequest(expectedURI, null))                  .andReturn(is);          EasyMock.replay(mockHttpClient); @@ -159,7 +158,7 @@ public class CCBAPIClientTest {          URI expectedURI = new URI("https://localhost:8080/api.php?"                  + "srv=individual_profile_from_micr&account_number=4567&routing_number=1234");          InputStream is = getClass().getResourceAsStream("model/ccb_individual_profile_response.xml"); -        EasyMock.expect(mockHttpClient.sendPostRequest(expectedURI, Collections.<String, String>emptyMap())) +        EasyMock.expect(mockHttpClient.sendPostRequest(expectedURI, null))                  .andReturn(is);          EasyMock.replay(mockHttpClient); @@ -179,7 +178,7 @@ public class CCBAPIClientTest {          // Set expectation.          URI expectedURI = new URI("https://localhost:8080/api.php?srv=individual_profiles");          InputStream is = getClass().getResourceAsStream("model/ccb_individual_profile_response.xml"); -        EasyMock.expect(mockHttpClient.sendPostRequest(expectedURI, Collections.<String, String>emptyMap())) +        EasyMock.expect(mockHttpClient.sendPostRequest(expectedURI, null))                  .andReturn(is);          EasyMock.replay(mockHttpClient); @@ -200,7 +199,7 @@ public class CCBAPIClientTest {          URI expectedURI = new URI("https://localhost:8080/api.php?srv=individual_profiles"                  + "&per_page=15&include_inactive=true&modified_since=2016-03-19&page=5");          InputStream is = getClass().getResourceAsStream("model/ccb_individual_profile_response.xml"); -        EasyMock.expect(mockHttpClient.sendPostRequest(expectedURI, Collections.<String, String>emptyMap())) +        EasyMock.expect(mockHttpClient.sendPostRequest(expectedURI, null))                  .andReturn(is);          EasyMock.replay(mockHttpClient); @@ -219,6 +218,46 @@ public class CCBAPIClientTest {          assertEquals(48, response.getIndividuals().get(0).getId());      } +    @Test +    public void testUpdateIndividual() throws Exception { +        // Set expectation. +        URI expectedURI = new URI("https://localhost:8080/api.php?srv=update_individual&individual_id=48"); +        InputStream is = getClass().getResourceAsStream("model/ccb_individual_profile_response.xml"); +        EasyMock.expect(mockHttpClient.sendPostRequest(EasyMock.eq(expectedURI), +                    EasyMock.aryEq("legal_first_name=Larabar".getBytes("UTF-8")))) +                .andReturn(is); +        EasyMock.replay(mockHttpClient); + +        UpdateIndividualProfileRequest request = new UpdateIndividualProfileRequest() +                .withIndividualId(48) +                .withLegalFirstName("Larabar"); + +        // Test update_individual. +        UpdateIndividualProfileResponse response = client.updateIndividualProfile(request); + +        // Verify results. +        EasyMock.verify(mockHttpClient); +        assertNull(response.getErrors()); +        assertEquals(1, response.getIndividuals().size()); +        assertEquals(48, response.getIndividuals().get(0).getId()); +    } + +    /** +     * This test ensures {@link CCBAPIClient#updateIndividualProfile(UpdateIndividualProfileRequest)} +     * throws an exception if the individualId property isn't set on the request. +     */ +    @Test(expected = IllegalArgumentException.class) +    public void testUpdateIndividualWithoutIndividualId() throws Exception { +        // Set expectation. +        UpdateIndividualProfileRequest request = new UpdateIndividualProfileRequest() +                .withLegalFirstName("Larabar"); + +        // Test update_individual. +        UpdateIndividualProfileResponse response = client.updateIndividualProfile(request); + +       // Expect exception. +    } +      /**       * Simple extension of CCBAPIClient to swap out the HTTPInterface with a mock.       */ diff --git a/src/test/java/com/p4square/ccbapi/serializer/AddressFormSerializerTest.java b/src/test/java/com/p4square/ccbapi/serializer/AddressFormSerializerTest.java new file mode 100644 index 0000000..0275358 --- /dev/null +++ b/src/test/java/com/p4square/ccbapi/serializer/AddressFormSerializerTest.java @@ -0,0 +1,101 @@ +package com.p4square.ccbapi.serializer; + +import com.p4square.ccbapi.model.Address; +import com.p4square.ccbapi.model.Countries; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * Tests for the AddressFormSerializer. + * + * Serializer output is compared to the expected output in the update_individual API example. + */ +public class AddressFormSerializerTest { + +    private AddressFormSerializer serializer; + +    @Before +    public void setUp() { +        serializer = new AddressFormSerializer(); +    } + +    @Test +    public void testEncodeAllFields() { +        final Address address = new Address(); +        address.setType(Address.Type.MAILING); +        address.setStreetAddress("12265 Oracle Blvd, Suite 105"); +        address.setCity("Colorado Springs"); +        address.setState("CO"); +        address.setZip("80921"); +        address.setCountry(Countries.UNITED_STATES); + +        final String actual = serializer.encode(address); + +        assertEquals("mailing_street_address=12265+Oracle+Blvd%2C+Suite+105" +                     + "&mailing_city=Colorado+Springs&mailing_state=CO&mailing_zip=80921&mailing_country=US", +                     actual); +    } + +    @Test +    public void testEncodeSomeFields() { +        final Address address = new Address(); +        address.setType(Address.Type.MAILING); +        address.setStreetAddress("12265 Oracle Blvd, Suite 105"); + +        final String actual = serializer.encode(address); + +        assertEquals("mailing_street_address=12265+Oracle+Blvd%2C+Suite+105", actual); +    } + +    @Test +    public void testEncodeSomeFieldsWithExistingData() { +        final StringBuilder sb = new StringBuilder("foo=bar"); + +        final Address address = new Address(); +        address.setType(Address.Type.MAILING); +        address.setStreetAddress("12265 Oracle Blvd, Suite 105"); +        address.setCity("Colorado Springs"); + +        serializer.encode(address, sb); +        final String actual = sb.toString(); + +        assertEquals("foo=bar&mailing_street_address=12265+Oracle+Blvd%2C+Suite+105&mailing_city=Colorado+Springs", +                     actual); +    } + +    @Test +    public void testEncodeHomeAddress() { +        final Address address = new Address(); +        address.setType(Address.Type.HOME); +        address.setStreetAddress("12265 Oracle Blvd, Suite 105"); + +        final String actual = serializer.encode(address); + +        assertEquals("home_street_address=12265+Oracle+Blvd%2C+Suite+105", actual); +    } + +    @Test +    public void testEncodeWorkAddress() { +        final Address address = new Address(); +        address.setType(Address.Type.WORK); +        address.setStreetAddress("12265 Oracle Blvd, Suite 105"); + +        final String actual = serializer.encode(address); + +        assertEquals("work_street_address=12265+Oracle+Blvd%2C+Suite+105", actual); +    } + +    @Test +    public void testEncodeOtherAddress() { +        final Address address = new Address(); +        address.setType(Address.Type.OTHER); +        address.setStreetAddress("12265 Oracle Blvd, Suite 105"); + +        final String actual = serializer.encode(address); + +        assertEquals("other_street_address=12265+Oracle+Blvd%2C+Suite+105", actual); +    } + +}
\ No newline at end of file diff --git a/src/test/java/com/p4square/ccbapi/serializer/IndividualProfileSerializerTest.java b/src/test/java/com/p4square/ccbapi/serializer/IndividualProfileSerializerTest.java new file mode 100644 index 0000000..a1bb8c9 --- /dev/null +++ b/src/test/java/com/p4square/ccbapi/serializer/IndividualProfileSerializerTest.java @@ -0,0 +1,203 @@ +package com.p4square.ccbapi.serializer; + +import com.p4square.ccbapi.model.*; +import org.junit.Before; +import org.junit.Test; + +import java.time.LocalDate; +import java.util.Arrays; +import java.util.Collections; + +import static org.junit.Assert.*; + +/** + * Tests for the IndividualProfileSerializer. + * + * Serializer output is compared to the expected output in the update_individual API example. + */ +public class IndividualProfileSerializerTest { + +    private IndividualProfileSerializer serializer; + +    @Before +    public void setUp() { +        serializer = new IndividualProfileSerializer(); +    } + +    /** +     * This test sets all of the fields on an +     * {@link UpdateIndividualProfileRequest} and tries to serialize them. +     */ +    @Test +    public void testEncodeAllFields() { +        final Address mailing_address = new Address(); +        mailing_address.setType(Address.Type.MAILING); +        mailing_address.setStreetAddress("12265 Oracle Blvd, Suite 105"); +        mailing_address.setCity("Colorado Springs"); +        mailing_address.setState("CO"); +        mailing_address.setZip("80921"); +        mailing_address.setCountry(Countries.UNITED_STATES); + +        final Phone phone = new Phone(); +        phone.setType(Phone.Type.CONTACT); +        phone.setNumber("719-555-2888"); + +        final UpdateIndividualProfileRequest request = new UpdateIndividualProfileRequest() +                .withSyncId(1001) +                .withOtherId("A1001") +                .withGivingNumber("ABC123") +                .withFirstName("Larry") +                .withLastName("Bob") +                .withMiddleName("P") +                .withLegalFirstName("Larabar") +                .withSalutation("Mr.") +                .withSuffix("Jr.") +                .withFamilyId(48) +                .withFamilyPosition(FamilyPosition.PRIMARY_CONTACT) +                .withMaritalStatus(MaritalStatus.MARRIED) +                .withGender(Gender.MALE) +                .withBirthday(LocalDate.of(1966, 2, 12)) +                .withAnniversary(LocalDate.of(1989, 5, 6)) +                .withDeceased(LocalDate.of(2016, 12, 31)) +                .withMembershipDate(LocalDate.of(2010, 1, 1)) +                .withMembershipEnd(LocalDate.of(2016, 12, 1)) +                .withEmail("ken@example.com") +                .withAddresses(Collections.singletonList(mailing_address)) +                .withPhones(Collections.singletonList(phone)) +                .withEmergencyContactName("Marry Bob") +                .withAllergies("Pine Nuts") +                .withConfirmedNoAllergies(true) +                .withBaptized(true) +                .withModifiedById(12); + +        final String actual = serializer.encode(request); + +        final String expected = "sync_id=1001&other_id=A1001&giving_number=ABC123" +                        + "&first_name=Larry&last_name=Bob&middle_name=P&legal_first_name=Larabar&salutation=Mr." +                        + "&suffix=Jr.&family_id=48&family_position=h&marital_status=m&gender=m" +                        + "&birthday=1966-02-12&anniversary=1989-05-06&deceased=2016-12-31" +                        + "&membership_date=2010-01-01&membership_end=2016-12-01" +                        + "&email=ken%40example.com&emergency_contact_name=Marry+Bob" +                        + "&allergies=Pine+Nuts&confirmed_no_allergies=true&baptized=true&modifier_id=12" +                        + "&mailing_street_address=12265+Oracle+Blvd%2C+Suite+105" +                        + "&mailing_city=Colorado+Springs&mailing_state=CO&mailing_zip=80921&mailing_country=US" +                        + "&contact_phone=719-555-2888"; + +        assertEquals(expected, actual); +    } + +    @Test +    public void testEncodeSomeFields() { +        final UpdateIndividualProfileRequest request = new UpdateIndividualProfileRequest() +                .withSyncId(1001) +                .withMiddleName("P") +                .withMaritalStatus(MaritalStatus.DIVORCED) +                .withGender(Gender.FEMALE) +                .withBirthday(LocalDate.of(1966, 2, 12)); + +        final String actual = serializer.encode(request); + +        assertEquals("sync_id=1001&middle_name=P&marital_status=d&gender=f&birthday=1966-02-12", actual); +    } + +    @Test +    public void testEncodeMultipleAddresses() { +        final Address address1 = new Address(); +        address1.setType(Address.Type.HOME); +        address1.setStreetAddress("123 Happy Lane"); + +        final Address address2 = new Address(); +        address2.setType(Address.Type.WORK); +        address2.setStreetAddress("12265 Oracle Blvd, Suite 105"); + +        final UpdateIndividualProfileRequest request = new UpdateIndividualProfileRequest() +                .withAddresses(Arrays.asList(address1, address2)); + +        final String actual = serializer.encode(request); + +        assertEquals("home_street_address=123+Happy+Lane&work_street_address=12265+Oracle+Blvd%2C+Suite+105", actual); +    } + +    @Test +    public void testEncodeMultiplePhones() { +        final Phone phone1 = new Phone(); +        phone1.setType(Phone.Type.MOBILE); +        phone1.setNumber("719-555-2888"); + +        final Phone phone2 = new Phone(); +        phone2.setType(Phone.Type.EMERGENCY); +        phone2.setNumber("719-555-3999"); + +        final UpdateIndividualProfileRequest request = new UpdateIndividualProfileRequest() +                .withPhones(Arrays.asList(phone1, phone2)); + +        final String actual = serializer.encode(request); + +        assertEquals("mobile_phone=719-555-2888&phone_emergency=719-555-3999", actual); +    } + +    @Test +    public void testEncodeCustomFieldsByName() { +        final UpdateIndividualProfileRequest request = new UpdateIndividualProfileRequest() +                .withCustomTextField("udf_text_1", "Hello") +                .withCustomDateField("udf_date_1", LocalDate.of(1966, 2, 12)) +                .withCustomPulldownField("udf_pulldown_1", 12); + +        final String actual = serializer.encode(request); + +        assertEquals("udf_text_1=Hello&udf_date_1=1966-02-12&udf_pulldown_1=12", actual); +    } + +    @Test +    public void testEncodeCustomFieldsByNumber() { +        final UpdateIndividualProfileRequest request = new UpdateIndividualProfileRequest() +                .withCustomTextField(2, "Hello") +                .withCustomDateField(2, LocalDate.of(1966, 2, 12)) +                .withCustomPulldownField(2, 12); + +        final String actual = serializer.encode(request); + +        assertEquals("udf_text_2=Hello&udf_date_2=1966-02-12&udf_pulldown_2=12", actual); +    } + +    @Test +    public void testNullCustomField() { +        final UpdateIndividualProfileRequest request = new UpdateIndividualProfileRequest() +                .withCustomTextField(1, null) +                .withCustomDateField(1, null) +                .withCustomPulldownField(1, null); + +        final String actual = serializer.encode(request); +        assertEquals("", actual); +    } + +    @Test(expected = IllegalArgumentException.class) +    public void testInvalidCustomTextFieldName() { +        new UpdateIndividualProfileRequest().withCustomTextField("udf_text_13", "foo"); +    } + +    @Test(expected = IllegalArgumentException.class) +    public void testInvalidCustomTextFieldNumber() { +        new UpdateIndividualProfileRequest().withCustomTextField(0, "foo"); +    } + +    @Test(expected = IllegalArgumentException.class) +    public void testInvalidCustomDateFieldName() { +        new UpdateIndividualProfileRequest().withCustomDateField("udf_date_7", LocalDate.of(1966, 2, 12)); +    } + +    @Test(expected = IllegalArgumentException.class) +    public void testInvalidCustomDateFieldNumber() { +        new UpdateIndividualProfileRequest().withCustomDateField(0, LocalDate.of(1966, 2, 12)); +    } + +    @Test(expected = IllegalArgumentException.class) +    public void testInvalidCustomPulldownFieldName() { +        new UpdateIndividualProfileRequest().withCustomPulldownField("udf_pulldown_7", 12); +    } + +    @Test(expected = IllegalArgumentException.class) +    public void testInvalidCustomPulldownFieldNumber() { +        new UpdateIndividualProfileRequest().withCustomPulldownField(0, 12); +    } +}
\ No newline at end of file diff --git a/src/test/java/com/p4square/ccbapi/serializer/PhoneFormSerializerTest.java b/src/test/java/com/p4square/ccbapi/serializer/PhoneFormSerializerTest.java new file mode 100644 index 0000000..7074ad3 --- /dev/null +++ b/src/test/java/com/p4square/ccbapi/serializer/PhoneFormSerializerTest.java @@ -0,0 +1,97 @@ +package com.p4square.ccbapi.serializer; + +import com.p4square.ccbapi.model.Phone; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * Tests for the PhoneFormSerializer. + * + * Serializer output is compared to the expected output in the update_individual API example. + */ +public class PhoneFormSerializerTest { + +    private PhoneFormSerializer serializer; + +    @Before +    public void setUp() { +        serializer = new PhoneFormSerializer(); +    } + +    @Test +    public void testEncodeContactPhone() { +        final Phone phone = new Phone(); +        phone.setType(Phone.Type.CONTACT); +        phone.setNumber("719-555-2888"); + +        final String actual = serializer.encode(phone); + +        assertEquals("contact_phone=719-555-2888", actual); +    } + +    @Test +    public void testEncodeHomePhone() { +        final Phone phone = new Phone(); +        phone.setType(Phone.Type.HOME); +        phone.setNumber("719-555-2888"); + +        final String actual = serializer.encode(phone); + +        assertEquals("home_phone=719-555-2888", actual); +    } + +    @Test +    public void testEncodeWorkPhone() { +        final Phone phone = new Phone(); +        phone.setType(Phone.Type.WORK); +        phone.setNumber("719-555-2888"); + +        final String actual = serializer.encode(phone); + +        assertEquals("work_phone=719-555-2888", actual); +    } + +    @Test +    public void testEncodeMobilePhone() { +        final Phone phone = new Phone(); +        phone.setType(Phone.Type.MOBILE); +        phone.setNumber("719-555-2888"); + +        final String actual = serializer.encode(phone); + +        assertEquals("mobile_phone=719-555-2888", actual); +    } + +    @Test +    public void testEncodeEmergencyPhone() { +        final Phone phone = new Phone(); +        phone.setType(Phone.Type.EMERGENCY); +        phone.setNumber("719-555-2888"); + +        final String actual = serializer.encode(phone); + +        assertEquals("phone_emergency=719-555-2888", actual); +    } + +    @Test +    public void testEncodeWithExistingData() { +        final StringBuilder sb = new StringBuilder(); + +        final Phone phone1 = new Phone(); +        phone1.setType(Phone.Type.MOBILE); +        phone1.setNumber("719-555-2888"); + +        final Phone phone2 = new Phone(); +        phone2.setType(Phone.Type.EMERGENCY); +        phone2.setNumber("719-555-3999"); + +        serializer.encode(phone1, sb); +        serializer.encode(phone2, sb); +        final String actual = sb.toString(); + +        assertEquals("mobile_phone=719-555-2888&phone_emergency=719-555-3999", actual); +    } + +}
\ No newline at end of file | 
