Skip to content

Commit

Permalink
Improve error handling while importing sub-components (#35)
Browse files Browse the repository at this point in the history
  • Loading branch information
Boris Skert authored May 24, 2019
1 parent 9148459 commit d85add5
Show file tree
Hide file tree
Showing 5 changed files with 654 additions and 63 deletions.
Original file line number Diff line number Diff line change
@@ -1,27 +1,32 @@
package de.adorsys.keycloak.config.repository;

import com.fasterxml.jackson.databind.ObjectMapper;
import de.adorsys.keycloak.config.exception.KeycloakRepositoryException;
import de.adorsys.keycloak.config.util.ResponseUtil;
import org.apache.logging.log4j.util.Strings;
import org.keycloak.admin.client.resource.ComponentResource;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.common.util.MultivaluedHashMap;
import org.keycloak.representations.idm.ComponentExportRepresentation;
import org.keycloak.representations.idm.ComponentRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.ws.rs.core.Response;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.*;

@Service
public class ComponentRepository {

private final RealmRepository realmRepository;

private final ObjectMapper mapper;

@Autowired
public ComponentRepository(RealmRepository realmRepository) {
public ComponentRepository(RealmRepository realmRepository, ObjectMapper mapper) {
this.realmRepository = realmRepository;
this.mapper = mapper;
}

public void create(String realm, ComponentRepresentation componentToCreate) throws KeycloakRepositoryException {
Expand All @@ -32,7 +37,7 @@ public void create(String realm, ComponentRepresentation componentToCreate) thro
}

public void update(String realm, ComponentRepresentation componentToUpdate) {
assert(Strings.isNotBlank(componentToUpdate.getId()));
assert (Strings.isNotBlank(componentToUpdate.getId()));

RealmResource realmResource = realmRepository.loadRealm(realm);
ComponentResource componentResource = realmResource.components().component(componentToUpdate.getId());
Expand All @@ -43,16 +48,19 @@ public void update(String realm, ComponentRepresentation componentToUpdate) {
public ComponentRepresentation get(String realm, String subType, String name) {
RealmResource realmResource = realmRepository.loadRealm(realm);

Optional<ComponentRepresentation> maybeComponent = realmResource.components()
.query(realm, subType, name)
List<ComponentRepresentation> realmComponents = realmResource.components().query();

Optional<ComponentRepresentation> maybeComponent = realmComponents
.stream()
.filter(c -> Objects.equals(c.getName(), name))
.filter(c -> Objects.equals(c.getProviderType(), subType))
.findFirst();

if(maybeComponent.isPresent()) {
if (maybeComponent.isPresent()) {
return maybeComponent.get();
}

throw new RuntimeException("Cannot find component by name '" + name + "' and subtype '" + subType + "' in realm '" + realm + "' ");
throw new KeycloakRepositoryException("Cannot find component by name '" + name + "' and subtype '" + subType + "' in realm '" + realm + "' ");
}

public Optional<ComponentRepresentation> tryToGet(String realm, String parentId, String subType, String name) {
Expand All @@ -62,7 +70,7 @@ public Optional<ComponentRepresentation> tryToGet(String realm, String parentId,
List<ComponentRepresentation> existingComponents = realmResource.components()
.query(parentId, subType, name);

if(existingComponents.isEmpty()) {
if (existingComponents.isEmpty()) {
maybeComponent = Optional.empty();
} else {
maybeComponent = Optional.of(existingComponents.get(0));
Expand All @@ -73,6 +81,7 @@ public Optional<ComponentRepresentation> tryToGet(String realm, String parentId,

/**
* Try to get a component by its properties.
*
* @param subType may be null
*/
public Optional<ComponentRepresentation> tryToGetComponent(String realm, String name, String subType) {
Expand All @@ -88,21 +97,73 @@ public Optional<ComponentRepresentation> tryToGetComponent(String realm, String
.findFirst();
}

public ComponentRepresentation getSubComponent(String realm, String parentId, String subType, String name) {
RealmResource realmResource = realmRepository.loadRealm(realm);
public ComponentExportRepresentation getSubComponentByName(String realm, String parentId, String name) {
ComponentExportRepresentation parentComponent = getComponentById(realm, parentId);

Optional<ComponentRepresentation> maybeComponent = realmResource.components()
.query(parentId, subType, name)
.stream()
MultivaluedHashMap<String, ComponentExportRepresentation> subComponents = parentComponent.getSubComponents();
List<ComponentExportRepresentation> subComponentsAsList = toFlatList(subComponents);

Optional<ComponentExportRepresentation> maybeSubComponent = subComponentsAsList.stream()
.filter(c -> Objects.equals(c.getName(), name))
.findFirst();

if(maybeComponent.isPresent()) {
return maybeComponent.get();
if (maybeSubComponent.isPresent()) {
return maybeSubComponent.get();
}

throw new RuntimeException("Cannot find sub-component by name '" + name
+ "', subtype '" + subType + "' and parent-id '" + parentId
+ "', and parent-id '" + parentId
+ "' in realm '" + realm + "' ");
}

public ComponentExportRepresentation getComponentById(String realm, String id) {
Optional<ComponentExportRepresentation> maybeComponent = tryToGetExportedComponentById(realm, id);

if (maybeComponent.isPresent()) {
return maybeComponent.get();
}

throw new KeycloakRepositoryException("Cannot find component by id '" + id + "'");
}

private Optional<ComponentExportRepresentation> tryToGetExportedComponentById(String realm, String id) {
return getFlatComponentsAndSubComponents(realm)
.stream()
.filter(c -> Objects.equals(c.getId(), id))
.findFirst();
}

private List<ComponentExportRepresentation> getFlatComponentsAndSubComponents(String realm) {
RealmRepresentation exportedRealm = realmRepository.partialExport(realm);

MultivaluedHashMap<String, ComponentExportRepresentation> components = exportedRealm.getComponents();

return toFlatList(components);
}

private List<ComponentExportRepresentation> getAllComponentsAndSubComponents(ComponentExportRepresentation component) {
List<ComponentExportRepresentation> allComponents = new ArrayList<>();
allComponents.add(component);

MultivaluedHashMap<String, ComponentExportRepresentation> subComponents = component.getSubComponents();
allComponents.addAll(toFlatList(subComponents));

return allComponents;
}

private List<ComponentExportRepresentation> toFlatList(MultivaluedHashMap<String, ComponentExportRepresentation> componentsMap) {
List<ComponentExportRepresentation> allComponents = new ArrayList<>();

Set<Map.Entry<String, List<ComponentExportRepresentation>>> componentsLists = componentsMap.entrySet();

for (Map.Entry<String, List<ComponentExportRepresentation>> componentsList : componentsLists) {
List<ComponentExportRepresentation> components = componentsList.getValue();

for (ComponentExportRepresentation component : components) {
allComponents.addAll(getAllComponentsAndSubComponents(component));
}
}

return allComponents;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package de.adorsys.keycloak.config.service;

import de.adorsys.keycloak.config.exception.ImportProcessingException;
import de.adorsys.keycloak.config.exception.KeycloakRepositoryException;
import de.adorsys.keycloak.config.model.RealmImport;
import de.adorsys.keycloak.config.repository.ComponentRepository;
import de.adorsys.keycloak.config.util.CloneUtils;
Expand All @@ -14,6 +16,7 @@
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

@Service
public class ComponentImportService {
Expand Down Expand Up @@ -44,21 +47,19 @@ private void createOrUpdateComponents(String realm, String providerType, List<Co

private void createOrUpdateComponent(String realm, String providerType, ComponentExportRepresentation componentToImport) {
Optional<ComponentRepresentation> maybeComponent = componentRepository.tryToGetComponent(realm, componentToImport.getName(), componentToImport.getSubType());
MultivaluedHashMap<String, ComponentExportRepresentation> subComponentsToImport = componentToImport.getSubComponents();

if (maybeComponent.isPresent()) {
updateComponentIfNeeded(realm, componentToImport, maybeComponent.get(), subComponentsToImport);
updateComponentIfNeeded(realm, providerType, componentToImport, maybeComponent.get());
} else {
logger.debug("Creating component: {}/{}", providerType, componentToImport.getName());
createComponent(realm, providerType, componentToImport, subComponentsToImport);
createComponent(realm, providerType, componentToImport);
}
}

private void createComponent(
String realm,
String providerType,
ComponentExportRepresentation component,
MultivaluedHashMap<String, ComponentExportRepresentation> subComponentChildren
ComponentExportRepresentation component
) {
ComponentRepresentation subComponentToAdd = CloneUtils.deepClone(component, ComponentRepresentation.class);

Expand All @@ -68,70 +69,117 @@ private void createComponent(

componentRepository.create(realm, subComponentToAdd);

if (subComponentChildren != null && !subComponentChildren.isEmpty()) {
ComponentRepresentation exitingComponent = componentRepository.get(realm, component.getSubType(), component.getName());
createOrUpdateSubComponents(realm, subComponentChildren, exitingComponent.getId());
MultivaluedHashMap<String, ComponentExportRepresentation> subComponents = component.getSubComponents();

if (subComponents != null && !subComponents.isEmpty()) {
ComponentRepresentation exitingComponent = componentRepository.get(realm, providerType, component.getName());
createOrUpdateSubComponents(realm, subComponents, exitingComponent.getId());
}
}

private void updateComponentIfNeeded(
String realm,
String providerType,
ComponentExportRepresentation componentToImport,
ComponentRepresentation existingComponent,
MultivaluedHashMap<String, ComponentExportRepresentation> subComponentChildren
ComponentRepresentation existingComponent
) {
ComponentRepresentation patchedComponent = CloneUtils.patch(existingComponent, componentToImport, "id");

boolean hasToBeUpdated = !CloneUtils.deepEquals(existingComponent, patchedComponent);

if (hasToBeUpdated) {
updateComponent(realm, componentToImport, patchedComponent, subComponentChildren);
updateComponent(realm, providerType, componentToImport, patchedComponent);
} else {
logger.debug("No need to update component: {}/{}", existingComponent.getProviderType(), componentToImport.getName());
}
}

private void updateComponent(String realm, ComponentExportRepresentation componentToImport, ComponentRepresentation patchedComponent, MultivaluedHashMap<String, ComponentExportRepresentation> subComponentChildren) {
private void updateComponent(
String realm,
String providerType,
ComponentExportRepresentation componentToImport,
ComponentRepresentation patchedComponent
) {
logger.debug("Updating component: {}/{}", patchedComponent.getProviderType(), componentToImport.getName());

if (patchedComponent.getProviderType() == null) {
patchedComponent.setProviderType(providerType);
}

componentRepository.update(realm, patchedComponent);

if (subComponentChildren != null && !subComponentChildren.isEmpty()) {
createOrUpdateSubComponents(realm, subComponentChildren, patchedComponent.getId());
MultivaluedHashMap<String, ComponentExportRepresentation> subComponents = componentToImport.getSubComponents();

if (subComponents != null && !subComponents.isEmpty()) {
createOrUpdateSubComponents(realm, subComponents, patchedComponent.getId());
}
}

private void createOrUpdateSubComponents(String realm, Map<String, List<ComponentExportRepresentation>> componentsToImport, String parentId) {
for (Map.Entry<String, List<ComponentExportRepresentation>> entry : componentsToImport.entrySet()) {
createOrUpdateSubComponents(realm, entry.getValue(), parentId);
private void createOrUpdateSubComponents(String realm, Map<String, List<ComponentExportRepresentation>> subComponents, String parentId) {
for (Map.Entry<String, List<ComponentExportRepresentation>> entry : subComponents.entrySet()) {
createOrUpdateSubComponents(realm, entry.getKey(), entry.getValue(), parentId);
}
}

private void createOrUpdateSubComponents(String realm, List<ComponentExportRepresentation> components, String parentId) {
for (ComponentExportRepresentation component : components) {
createOrUpdateSubComponent(realm, parentId, component);
private void createOrUpdateSubComponents(String realm, String providerType, List<ComponentExportRepresentation> subComponents, String parentId) {
for (ComponentExportRepresentation subComponent : subComponents) {
createOrUpdateSubComponent(realm, parentId, providerType, subComponent);
}
}

private void createOrUpdateSubComponent(String realm, String parentId, ComponentExportRepresentation component) {
Optional<ComponentRepresentation> maybeComponent = componentRepository.tryToGet(realm, parentId, component.getSubType(), component.getName());
MultivaluedHashMap<String, ComponentExportRepresentation> subComponentChildren = component.getSubComponents();
private void createOrUpdateSubComponent(String realm, String parentId, String providerType, ComponentExportRepresentation subComponent) {
Optional<ComponentRepresentation> maybeComponent = componentRepository.tryToGet(realm, parentId, subComponent.getSubType(), subComponent.getName());

if (maybeComponent.isPresent()) {
updateComponentIfNeeded(realm, component, maybeComponent.get(), subComponentChildren);
updateComponentIfNeeded(realm, providerType, subComponent, maybeComponent.get());
} else {
updateSubComponent(realm, parentId, component, subComponentChildren);
createSubComponent(realm, parentId, providerType, subComponent);
}
}

private void updateSubComponent(String realm, String parentId, ComponentExportRepresentation component, MultivaluedHashMap<String, ComponentExportRepresentation> subComponentChildren) {
ComponentRepresentation subComponentToAdd = CloneUtils.deepClone(component, ComponentRepresentation.class);
private void createSubComponent(String realm, String parentId, String providerType, ComponentExportRepresentation subComponent) {
logger.debug("Create sub-component '{}' for provider-type '{}' within component with id '{}' and realm '{}'", subComponent.getName(), providerType, parentId, realm);

componentRepository.create(realm, subComponentToAdd);
ComponentRepresentation clonedSubComponent = CloneUtils.deepClone(subComponent, ComponentRepresentation.class);

if (clonedSubComponent.getProviderType() == null) {
clonedSubComponent.setProviderType(providerType);
}

if (clonedSubComponent.getParentId() == null) {
clonedSubComponent.setParentId(parentId);
}

try {
componentRepository.create(realm, clonedSubComponent);
} catch (KeycloakRepositoryException e) {
throw new ImportProcessingException("Cannot create sub-component '" + clonedSubComponent.getName() + "' in realm '" + realm + "'", e);
}

createSubComponents(realm, parentId, subComponent);
}

private void createSubComponents(String realm, String parentId, ComponentExportRepresentation subComponent) {
MultivaluedHashMap<String, ComponentExportRepresentation> subComponents = subComponent.getSubComponents();

if (subComponents != null && !subComponents.isEmpty()) {
ComponentExportRepresentation parentComponent = componentRepository.getSubComponentByName(realm, parentId, subComponent.getName());

createSubComponents(realm, parentComponent.getId(), subComponents.entrySet());
}
}

private void createSubComponents(String realm, String parentId, Set<Map.Entry<String, List<ComponentExportRepresentation>>> subComponents) {
for (Map.Entry<String, List<ComponentExportRepresentation>> subComponentsToCreate : subComponents) {
String providerType = subComponentsToCreate.getKey();

createSubComponents(realm, parentId, providerType, subComponentsToCreate);
}
}

if (subComponentChildren != null) {
ComponentRepresentation exitingComponent = componentRepository.getSubComponent(realm, parentId, component.getSubType(), component.getName());
createOrUpdateSubComponents(realm, subComponentChildren, exitingComponent.getId());
private void createSubComponents(String realm, String parentId, String subComponentsProviderType, Map.Entry<String, List<ComponentExportRepresentation>> subComponentsToCreate) {
for (ComponentExportRepresentation subComponentToCreate : subComponentsToCreate.getValue()) {
createSubComponent(realm, parentId, subComponentsProviderType, subComponentToCreate);
}
}
}
Loading

0 comments on commit d85add5

Please sign in to comment.