PersistentResource implements com.yahoo.elide.security.PersistentResource<T> { protected void auditField(final ChangeSpec changeSpec) { final String fieldName = changeSpec.getFieldName(); Audit[] annotations = dictionary.getAttributeOrRelationAnnotations(getResourceClass(), Audit.class, fieldName ); if (annotations == null || annotations.length == 0) { auditClass(Audit.Action.UPDATE, changeSpec); return; } for (Audit annotation : annotations) { if (annotation.action().length == 1 && annotation.action()[0] == Audit.Action.UPDATE) { LogMessage message = new LogMessage(annotation, this, Optional.of(changeSpec)); getRequestScope().getAuditLogger().log(message); } else { throw new InvalidSyntaxException("Only Audit.Action.UPDATE is allowed on fields."); } } } PersistentResource(@NonNull T obj, PersistentResource parent, String id, @NonNull RequestScope scope); @Override String toString(); static PersistentResource<T> createObject(
            PersistentResource<?> parent,
            Class<T> entityClass,
            RequestScope requestScope,
            Optional<String> uuid); @Override boolean matchesId(String checkId); @SuppressWarnings("resource") @NonNull static PersistentResource<T> loadRecord(
            Class<T> loadClass, String id, RequestScope requestScope); static Set<PersistentResource> loadRecords(
            Class<?> loadClass,
            List<String> ids,
            Optional<FilterExpression> filter,
            Optional<Sorting> sorting,
            Optional<Pagination> pagination,
            RequestScope requestScope); boolean updateAttribute(String fieldName, Object newVal); boolean updateRelation(String fieldName, Set<PersistentResource> resourceIdentifiers); boolean clearRelation(String relationName); void removeRelation(String fieldName, PersistentResource removeResource); boolean relationshipAlreadyExists(String fieldName, PersistentResource toAdd); void addRelation(String fieldName, PersistentResource newRelation); void deleteResource(); @Override String getId(); void setId(String id); boolean isIdGenerated(); @Override Optional<String> getUUID(); PersistentResource getRelation(String relation, String id); Set<PersistentResource> getRelation(String relation,
            List<String> ids,
            Optional<FilterExpression> filter,
            Optional<Sorting> sorting,
            Optional<Pagination> pagination); Set<PersistentResource> getRelationCheckedFiltered(String relationName,
            Optional<FilterExpression> filterExpression,
            Optional<Sorting> sorting,
            Optional<Pagination> pagination); RelationshipType getRelationshipType(String relation); Object getAttribute(String attr); @Override T getObject(); void setObject(T obj); @Override @JsonIgnore Class<T> getResourceClass(); @Override String getType(); @Override int hashCode(); @Override boolean equals(Object obj); ResourceLineage getLineage(); EntityDictionary getDictionary(); @Override RequestScope getRequestScope(); Resource toResource(); Resource toResourceWithSortingAndPagination(); Resource toResource(final Supplier<Map<String, Relationship>> relationshipSupplier,
                               final Supplier<Map<String, Object>> attributeSupplier); static Object getValue(Object target, String fieldName, RequestScope requestScope);  }
@Test public void testFieldLevelAudit() throws Exception { Child child = newChild(5); Parent parent = newParent(7); User goodUser = new User(1); TestAuditLogger logger = new TestAuditLogger(); RequestScope requestScope = getUserScope(goodUser, logger); PersistentResource<Parent> parentResource = new PersistentResource<>(parent, null, requestScope.getUUIDFor(parent), requestScope); PersistentResource<Child> childResource = new PersistentResource<>(child, parentResource, requestScope.getUUIDFor(child), requestScope); childResource.auditField(new ChangeSpec(childResource, "name", parent, null)); assertEquals(1, logger.getMessages().size(), "One message should be logged"); LogMessage message = logger.getMessages().get(0); assertEquals("UPDATE Child 5 Parent 7", message.getMessage(), "Logging template should match"); assertEquals(1, message.getOperationCode(), "Operation code should match"); logger.clear(); }