Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,6 @@ public enum Messages {
VALUE_OUT_OF_RANGE_DETAIL_TEMPLATE,

NOT_SUPPORTED_REASON,

INCORRECT_FOLLOW_LINK
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.ovirt.engine.api.restapi.invocation.CurrentManager;
import org.ovirt.engine.api.restapi.logging.MessageBundle;
import org.ovirt.engine.api.restapi.logging.Messages;
import org.ovirt.engine.api.restapi.resource.exception.IncorrectFollowLinkException;
import org.ovirt.engine.api.restapi.resource.utils.LinkFollower;
import org.ovirt.engine.api.restapi.resource.utils.LinksTreeNode;
import org.ovirt.engine.api.restapi.types.MappingLocator;
Expand Down Expand Up @@ -426,7 +427,13 @@ public final void follow (ActionableResource entity) {
ParametersHelper.removeParameter(MAX);
LinksTreeNode linksTree = linkFollower.createLinksTree(entity.getClass(), followValue);
follow(entity, linksTree);
linkFollower.followLinks(entity, linksTree);
try {
linkFollower.followLinks(entity, linksTree);
} catch (IncorrectFollowLinkException e) {
throw new WebFaultException(e,
localize(Messages.INCORRECT_FOLLOW_LINK, e.getLink(), e.getEntityName()),
Response.Status.BAD_REQUEST);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package org.ovirt.engine.api.restapi.resource.exception;

public class IncorrectFollowLinkException extends RuntimeException {

private final String link;
private final String entityName;

public IncorrectFollowLinkException(String link, String entityName, Throwable cause) {
super(cause);
this.entityName = entityName;
this.link = link;
}

public String getLink() {
return link;
}

public String getEntityName() {
return entityName;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import org.ovirt.engine.api.model.Link;
import org.ovirt.engine.api.restapi.resource.BaseBackendResource;
import org.ovirt.engine.api.restapi.resource.ResourceLocator;
import org.ovirt.engine.api.restapi.resource.exception.IncorrectFollowLinkException;
import org.ovirt.engine.api.utils.EntityHelper;
import org.ovirt.engine.api.utils.ReflectionHelper;

Expand Down Expand Up @@ -139,6 +140,10 @@ private void followLinks(List<ActionableResource> entities, LinksTreeNode node)
* follow all links in the provided links-tree, recursively.
*/
private void followLink(ActionableResource entity, LinksTreeNode node) {
if (entity == null || node == null) {
return;
}

List<ActionableResource> nextStepEntities = new LinkedList<>();
if (EntityHelper.isCollection(entity)) {
nextStepEntities.addAll(fetchData((BaseResources) entity, node));
Expand Down Expand Up @@ -181,7 +186,7 @@ private List<ActionableResource> fetchData(BaseResources collectionEntity, Links
results.add(fetchData(entity, node));
}
} catch (Exception e) {
throw new IllegalStateException("Problem following '" + node.getElement() + "' link in " + collectionEntity.getClass().getSimpleName() + " entity.", e);
throw new IncorrectFollowLinkException(node.getElement(), collectionEntity.getClass().getSimpleName(), e);
}
return results;
}
Expand All @@ -204,17 +209,19 @@ private ActionableResource fetchData(BaseResource entity, LinksTreeNode link) {
String element = underscoreToCamelCase(link.getElement());
if (link.isFollowed()) {
Method getter = ReflectionHelper.getGetter(entity, element);
return (ActionableResource) getter.invoke(entity);
return getter != null ? (ActionableResource) getter.invoke(entity) : null;
} else {
String href = getHref((BaseResource) entity, link.getElement());
ActionableResource result = fetch(href);
Method setter = ReflectionHelper.getSetter(entity, element);
setter.invoke(entity, result);

if (result != null) {
Method setter = ReflectionHelper.getSetter(entity, element);
setter.invoke(entity, result);
}
return result;
}
} catch (Exception e) {
throw new IllegalStateException("Problem fetching '" + link.getElement() +
"' from " + entity.getClass().getSimpleName(), e);
throw new IncorrectFollowLinkException(link.getElement(), entity.getClass().getSimpleName(), e);
}
}

Expand Down Expand Up @@ -243,15 +250,22 @@ private String getHref(BaseResource entity, String link) throws IllegalAccessExc
return optional.get().getHref();
} else { //assume this is not a sub-collection, since it wasn't found among links.
Method getter = ReflectionHelper.getGetter(entity, underscoreToCamelCase(link));
if (getter == null) {
throw new IllegalStateException("Follow link '" + link + "' is incorrect.");
}
BaseResource member = (BaseResource) getter.invoke(entity);
return member.getHref();
return member != null ? member.getHref() : null;
}
}

/**
* This scope of this method is 'protected' for testing purposes.
*/
protected ActionableResource fetch(String href) {
if (href == null) {
return null;
}

try {
BaseBackendResource resource = resourceLocator.locateResource(href);
//need to invoke the method in the resource annotated with @GET
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,4 @@ VALUE_OUT_OF_RANGE_DETAIL_TEMPLATE=The value {0} of attribute ''{1}'' is outside
DISK_UPDATE_NOT_PERMITTED=Updating disk attributes other than QCOW version is permitted only for disk-attachments, which reside under VMs.
CPU_UPDATE_NOT_PERMITTED=Attempt to automatically configure CPU topology or pinning while CPU topology or pinning is also specified.
NOT_SUPPORTED_REASON=The input is unsupported. {0} is not supported.
INCORRECT_FOLLOW_LINK=Problem following {0} link in {1} entity.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

import org.junit.jupiter.api.BeforeEach;
Expand All @@ -18,8 +20,10 @@
import org.ovirt.engine.api.model.Nic;
import org.ovirt.engine.api.model.Nics;
import org.ovirt.engine.api.model.Vm;
import org.ovirt.engine.api.model.Vms;
import org.ovirt.engine.api.restapi.resource.BackendVmNicsResource;
import org.ovirt.engine.api.restapi.resource.ResourceLocator;
import org.ovirt.engine.api.restapi.resource.exception.IncorrectFollowLinkException;
import org.ovirt.engine.api.restapi.resource.utils.LinkFollower;
import org.ovirt.engine.api.restapi.resource.utils.LinksTreeNode;

Expand All @@ -38,7 +42,9 @@ public void setUp() {
linkFollower = new LinkFollower(resourceLocator) {
//override fetch() since it requires a real environment and would crash tests.
protected ActionableResource fetch(String href) {
if (href.equals("/ovirt-engine/api/vms/63978315-2d17-4e67-b393-2ea60a8aeacb/nics")) {
if (href == null) {
return null;
} else if (href.equals("/ovirt-engine/api/vms/63978315-2d17-4e67-b393-2ea60a8aeacb/nics")) {
return createNics();
} else if (href.equals("/ovirt-engine/api/vms/63978315-2d17-4e67-b393-2ea60a8aeacb/diskattachments")) {
return createDiskAttachments();
Expand Down Expand Up @@ -75,6 +81,47 @@ public void testFollowLinks() throws SecurityException, IllegalArgumentException
assertNotNull(vm.getDiskAttachments().getDiskAttachments().get(2).getDisk());
}

@Test
public void testFollowLinksIfFollowIsIncorrect() {
LinksTreeNode linksTree = linkFollower.createLinksTree(Vm.class, "incorrect_nics");
Vm vm = createVm();
IncorrectFollowLinkException actualException = assertThrows(
IncorrectFollowLinkException.class,
() -> linkFollower.followLinks(vm, linksTree)
);
assertEquals("incorrect_nics", actualException.getLink());
assertEquals("Vm", actualException.getEntityName());
}

@Test
public void testFollowLinksForCollectionEntityIfFollowIsIncorrect() {
LinksTreeNode linksTree = linkFollower.createLinksTree(Vm.class, "incorrect_nics");
Vms vms = createVms();
IncorrectFollowLinkException actualException = assertThrows(
IncorrectFollowLinkException.class,
() -> linkFollower.followLinks(vms, linksTree)
);
assertEquals("incorrect_nics", actualException.getLink());
assertEquals("Vms", actualException.getEntityName());
}

@Test
public void testFollowLinksIfFollowedEntityIsNull() {
LinksTreeNode linksTree = linkFollower.createLinksTree(Vm.class, "template");
Vm vm = createVm();
linkFollower.followLinks(vm, linksTree);
assertNull(vm.getTemplate());
assertNull(vm.getNics());
assertNull(vm.getDiskAttachments());
}

private Vms createVms() {
Vms vms = new Vms();
vms.getVms().add(createVm());
vms.getVms().add(createVm());
return vms;
}

private Vm createVm() {
Vm vm = new Vm();
//add an irrelevant link
Expand Down