Skip to content

Commit

Permalink
Fix StandardEntityChanger to support String casting when possible (#198)
Browse files Browse the repository at this point in the history
* Fix StandardEntityChanger to support String casting when possible

* Generify cast assignment in StandardEntityChanger.setField

* Removing DateTime explicit check in StandardEntityChanger.setField

* Add failed casts to additionalProperties when possible.

* Change log level when entity does not support additionalProperties

Co-authored-by: Trevor Swartz <swartz.trevor24@gmail.com>

* Adding better handling of nested field setting

---------

Co-authored-by: Trevor Swartz <swartz.trevor24@gmail.com>
  • Loading branch information
scriptom and taswartz authored Dec 19, 2023
1 parent 7aed3e3 commit 3565409
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@ public <T> T setField(T entity, String field, Object value) {
try {
PropertyDescriptor propertyDescriptor = BeanUtils.getPropertyDescriptor(target.getClass(), finalField);
if (propertyDescriptor == null) {
if (entity instanceof AbstractEntity) {
((AbstractEntity) entity).getAdditionalProperties().put(finalField, value);
if (target instanceof AbstractEntity) {
((AbstractEntity) target).getAdditionalProperties().put(finalField, value);
return entity;
}

Expand All @@ -91,20 +91,23 @@ public <T> T setField(T entity, String field, Object value) {
try {
propertyDescriptor.getWriteMethod().invoke(target, value);
} catch (IllegalAccessException | InvocationTargetException | IllegalArgumentException e) {
if (DateTime.class.equals(fieldType)) {
propertyDescriptor.getWriteMethod().invoke(target, asType(value, DateTime.class));
} else if (BullhornEntity.class.isAssignableFrom(fieldType)) {
BullhornEntity bullhornEntity = (BullhornEntity) fieldType.getDeclaredConstructor().newInstance();
bullhornEntity.setId((Integer) value);
propertyDescriptor.getWriteMethod().invoke(target, bullhornEntity);
} else if (Integer.class.equals(fieldType)) {
propertyDescriptor.getWriteMethod().invoke(target, asType(value, Integer.class));
} else if (BigDecimal.class.equals(fieldType)) {
propertyDescriptor.getWriteMethod().invoke(target, asType(value, BigDecimal.class));
} else if (Boolean.class.equals(fieldType)) {
propertyDescriptor.getWriteMethod().invoke(target, asType(value, Boolean.class));
} else {
log.error("Error setting field " + finalField + " to value " + value, e);
try {
if (BullhornEntity.class.isAssignableFrom(fieldType)) {
BullhornEntity bullhornEntity = (BullhornEntity) fieldType.getDeclaredConstructor().newInstance();
bullhornEntity.setId((Integer) value);
propertyDescriptor.getWriteMethod().invoke(target, bullhornEntity);
} else {
propertyDescriptor.getWriteMethod().invoke(target, asType(value, fieldType));
}
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException |
InvocationTargetException | NoSuchMethodException | SecurityException ex) {
log.warn("Failed to cast value to type {} for field {}", fieldType.getName(), propertyDescriptor.getName(), ex);
if (entity instanceof AbstractEntity) {
((AbstractEntity) entity).getAdditionalProperties().put(finalField, value);
log.warn("Saved {} to entity's additionalProperties", finalField);
} else {
log.error("Entity does not support additionalProperties so couldn't save {}", finalField);
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,30 @@

import com.bullhornsdk.data.model.entity.core.standard.Candidate;
import com.bullhornsdk.data.model.entity.core.standard.CorporateUser;
import com.bullhornsdk.data.model.entity.core.type.AbstractEntity;
import com.bullhornsdk.data.model.entity.core.type.BullhornEntity;
import com.bullhornsdk.data.model.entity.embedded.Address;
import com.client.core.base.tools.entitychanger.EntityChanger;
import com.google.common.collect.ImmutableMap;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import org.joda.time.DateTime;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.math.BigDecimal;
import java.util.Map;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.*;


public class StandardEntityChangerTest {
private Candidate candidate;
private Candidate emptyCandidate;
private TestBullhornEntity testBullhornEntity;
private NoAdditionalProperties noAdditionalProperties;

@BeforeEach
public void setUp() {
Expand All @@ -39,6 +40,7 @@ public void setUp() {
emptyCandidate = new Candidate();

testBullhornEntity = new TestBullhornEntity();
noAdditionalProperties = new NoAdditionalProperties();
}

private Address makeAddress() {
Expand Down Expand Up @@ -229,7 +231,7 @@ public void returnsNullIfNoReadMethod() {
public void setValueFailsIfPropertyDoesNotExistAndEntityDoesntHaveAdditionalProperties() {
EntityChanger entityChanger = new StandardEntityChanger();
Assertions.assertThrows(RuntimeException.class, () ->
entityChanger.setField(testBullhornEntity, "thisPropertyDoesNotExist", "customValue")
entityChanger.setField(noAdditionalProperties, "thisPropertyDoesNotExist", "customValue")
);
}

Expand All @@ -250,23 +252,66 @@ public void setValueFailsIfIntermediaryPropertyDoesntExist() {
}

@Test
public void setValueAddsUnknownPropertiesToAdditionalPropertiesIfPossible() {
public void setFieldAddsUnknownPropertiesToAdditionalPropertiesIfPossible() {
EntityChanger entityChanger = new StandardEntityChanger();
entityChanger.setField(candidate, "candidateMissingField", "Test Value");
assertEquals("Test Value", candidate.getAdditionalProperties().get("candidateMissingField"));
// Simulating assigning a missing relationship as a map
}

@Test
public void setFieldAddsUnknownRelationshipToAdditionalPropertiesIfPossible() {
EntityChanger entityChanger = new StandardEntityChanger();
entityChanger.setField(candidate, "missingRelation", Map.of("id", 10));
assertEquals(10, ((Map) candidate.getAdditionalProperties().get("missingRelation")).get("id"));
}

@Test
public void setFieldCastsIntegerToString() {
EntityChanger entityChanger = new StandardEntityChanger();
entityChanger.setField(candidate, "customText1", 10);
assertEquals(candidate.getCustomText1(), "10");
}

@Test
public void setFieldCastsDoubleToString() {
EntityChanger entityChanger = new StandardEntityChanger();
entityChanger.setField(candidate, "customText1", 10.5d);
assertEquals(candidate.getCustomText1(), "10.5");
}

@Test
public void setFieldSavesFailedCastsToAdditionalPropertiesIfAvailable() {
EntityChanger entityChanger = new StandardEntityChanger();
entityChanger.setField(testBullhornEntity, "simpleStringContainer", "Test");
assertEquals("Test", testBullhornEntity.getAdditionalProperties().get("simpleStringContainer"));
}

@Test
public void setFieldSavesUnknownPropertiesOfNestedEntitiesToAdditionalPropertiesIfAvailable() {
EntityChanger entityChanger = new StandardEntityChanger();
ImmutableMap<Object, Object> map = ImmutableMap.builder()
.put("id", "Test").build();
entityChanger.setField(testBullhornEntity, "simpleStringContainer", map);
assertNotNull(testBullhornEntity.getSimpleStringContainer());
assertEquals("Test", testBullhornEntity.getSimpleStringContainer().getAdditionalProperties().get("id"));
}

@Test
public void setFieldSavesUnknownNestedEntityPropertiesToAdditionalPropertiesIfAvailable() {
EntityChanger entityChanger = new StandardEntityChanger();
entityChanger.setField(testBullhornEntity, "simpleStringContainer.unknown", "Test");
assertNotNull(testBullhornEntity.getSimpleStringContainer());
assertEquals("Test", testBullhornEntity.getSimpleStringContainer().getAdditionalProperties().get("unknown"));
}

@SuppressWarnings("ALL")
static class TestBullhornEntity implements BullhornEntity {
static class TestBullhornEntity extends AbstractEntity implements BullhornEntity {
private Integer id;
private String fieldWithNoGetter;
private String fieldWithNoSetter = "value";

private String invisibleProperty;

private SimpleStringContainer simpleStringContainer;
@Override
public Integer getId() {
return this.id;
Expand All @@ -292,5 +337,24 @@ private String getInvisibleProperty() {
private void setInvisibleProperty(String invisibleProperty) {
this.invisibleProperty = invisibleProperty;
}

public SimpleStringContainer getSimpleStringContainer() {
return simpleStringContainer;
}

public void setSimpleStringContainer(SimpleStringContainer simpleStringContainer) {
this.simpleStringContainer = simpleStringContainer;
}
}

@Data
static class NoAdditionalProperties {
String property;
}
@EqualsAndHashCode(callSuper = true)
@Data
@NoArgsConstructor
static class SimpleStringContainer extends AbstractEntity {
String string;
}
}

0 comments on commit 3565409

Please # to comment.